Initial USB device stack for STM32.

Patch provided by Gareth McMullin <gareth@blacksphere.co.nz>,
thanks a lot!
This commit is contained in:
Uwe Hermann 2010-11-02 02:02:21 +01:00
parent 1621fde1f4
commit 6e090ccee1
30 changed files with 2913 additions and 10 deletions

View File

@ -24,7 +24,7 @@ Q := @
MAKEFLAGS += --no-print-directory
endif
all: i2c_stts75_sensor adc_temperature_sensor dma_mem2mem timer_interrupt systick dogm128 rtc
all: i2c_stts75_sensor adc_temperature_sensor dma_mem2mem timer_interrupt systick dogm128 rtc usb_cdcacm usb_dfu usb_hid
i2c_stts75_sensor:
@printf " BUILD examples/other/i2c_stts75_sensor\n"
@ -54,6 +54,18 @@ rtc:
@printf " BUILD examples/other/rtc\n"
$(Q)$(MAKE) -C rtc
usb_cdcacm:
@printf " BUILD examples/other/usb_cdcacm\n"
$(Q)$(MAKE) -C usb_cdcacm
usb_dfu:
@printf " BUILD examples/other/usb_dfu\n"
$(Q)$(MAKE) -C usb_dfu
usb_hid:
@printf " BUILD examples/other/usb_hid\n"
$(Q)$(MAKE) -C usb_hid
clean:
@printf " CLEAN examples/other/i2c_stts75_sensor\n"
$(Q)$(MAKE) -C i2c_stts75_sensor clean
@ -69,6 +81,12 @@ clean:
$(Q)$(MAKE) -C dogm128 clean
@printf " CLEAN examples/other/rtc\n"
$(Q)$(MAKE) -C rtc clean
@printf " CLEAN examples/other/usb_cdcacm\n"
$(Q)$(MAKE) -C usb_cdcacm clean
@printf " CLEAN examples/other/usb_dfu\n"
$(Q)$(MAKE) -C usb_dfu clean
@printf " CLEAN examples/other/usb_hid\n"
$(Q)$(MAKE) -C usb_hid clean
.PHONY: i2c_stts75_sensor adc_temperature_sensor dma_mem2mem timer_interrupt systick dogm128 rtc clean
.PHONY: i2c_stts75_sensor adc_temperature_sensor dma_mem2mem timer_interrupt systick dogm128 rtc clean usb_cdcacm usb_dfu usb_hid

View File

@ -0,0 +1,95 @@
##
## This file is part of the libopenstm32 project.
##
## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
##
BINARY = cdcacm
# PREFIX ?= arm-none-eabi
PREFIX ?= arm-elf
CC = $(PREFIX)-gcc
LD = $(PREFIX)-gcc
OBJCOPY = $(PREFIX)-objcopy
OBJDUMP = $(PREFIX)-objdump
# Uncomment this line if you want to use the installed (not local) library.
#TOOLCHAIN_DIR = `dirname \`which $(CC)\``/..
TOOLCHAIN_DIR = ../../..
CFLAGS = -O0 -g -Wall -Wextra -I$(TOOLCHAIN_DIR)/include -fno-common \
-mcpu=cortex-m3 -mthumb
LDSCRIPT = $(BINARY).ld
LDFLAGS = -L$(TOOLCHAIN_DIR)/lib -T$(LDSCRIPT) -nostartfiles
OBJS = $(BINARY).o
OPENOCD_BASE = /usr
OPENOCD = $(OPENOCD_BASE)/bin/openocd
OPENOCD_SCRIPTS = $(OPENOCD_BASE)/share/openocd/scripts
OPENOCD_FLASHER = $(OPENOCD_SCRIPTS)/interface/jtagkey.cfg
OPENOCD_BOARD = $(OPENOCD_SCRIPTS)/target/stm32.cfg
# Be silent per default, but 'make V=1' will show all compiler calls.
ifneq ($(V),1)
Q := @
NULL := 2>/dev/null
endif
all: images
images: $(BINARY)
@printf " OBJCOPY $(BINARY).bin\n"
$(Q)$(OBJCOPY) -Obinary $(BINARY) $(BINARY).bin
@printf " OBJCOPY $(BINARY).hex\n"
$(Q)$(OBJCOPY) -Oihex $(BINARY) $(BINARY).hex
@printf " OBJCOPY $(BINARY).srec\n"
$(Q)$(OBJCOPY) -Osrec $(BINARY) $(BINARY).srec
@printf " OBJDUMP $(BINARY).list\n"
$(Q)$(OBJDUMP) -S $(BINARY) > $(BINARY).list
$(BINARY): $(OBJS) $(LDSCRIPT)
@printf " LD $(subst $(shell pwd)/,,$(@))\n"
$(Q)$(LD) $(LDFLAGS) -o $(BINARY) $(OBJS) -lopenstm32
%.o: %.c Makefile
@printf " CC $(subst $(shell pwd)/,,$(@))\n"
$(Q)$(CC) $(CFLAGS) -o $@ -c $<
clean:
@printf " CLEAN $(subst $(shell pwd)/,,$(OBJS))\n"
$(Q)rm -f *.o
@printf " CLEAN $(BINARY)\n"
$(Q)rm -f $(BINARY)
@printf " CLEAN $(BINARY).bin\n"
$(Q)rm -f $(BINARY).bin
@printf " CLEAN $(BINARY).hex\n"
$(Q)rm -f $(BINARY).hex
@printf " CLEAN $(BINARY).srec\n"
$(Q)rm -f $(BINARY).srec
@printf " CLEAN $(BINARY).list\n"
$(Q)rm -f $(BINARY).list
flash: images
@printf " FLASH $(BINARY).bin\n"
@# IMPORTANT: Don't use "resume", only "reset" will work correctly!
$(Q)$(OPENOCD) -s $(OPENOCD_SCRIPTS) \
-f $(OPENOCD_FLASHER) \
-f $(OPENOCD_BOARD) \
-c "init" -c "reset halt" \
-c "flash write_image erase $(BINARY).hex" \
-c "reset" \
-c "shutdown" $(NULL)
.PHONY: images clean

View File

@ -0,0 +1,43 @@
------------------------------------------------------------------------------
README
------------------------------------------------------------------------------
This example implements a USB CDC-ACM device (aka Virtual Serial Port)
to demonstrate the use of the USB device stack.
Building
--------
$ make
Running 'make' on the top-level libopenstm32 directory will automatically
also build this example. Or you can build the library "manually" and
then run 'make' in this directory.
You may want to override the toolchain (e.g., arm-elf or arm-none-eabi):
$ PREFIX=arm-none-eabi make
For a more verbose build you can use
$ make V=1
Flashing
--------
You can flash the generated code using OpenOCD:
$ make flash
Or you can do the same manually via:
$ openocd -f interface/jtagkey-tiny.cfg -f target/stm32.cfg
$ telnet localhost 4444
> reset halt
> flash write_image erase systick.hex
> reset
Replace the "jtagkey-tiny.cfg" with whatever JTAG device you are using, and/or
replace "stm.cfg" with your respective config file.

View File

@ -0,0 +1,128 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CDC_H
#define __CDC_H
/* Definitions of Communications Device Class from
* "Universal Serial Bus Class Definitions for Communications Devices
* Revision 1.2"
*/
/* Table 2: Communications Device Class Code */
#define USB_CLASS_CDC 0x02
/* Table 4: Class Subclass Code */
#define USB_CDC_SUBCLASS_DLCM 0x01
#define USB_CDC_SUBCLASS_ACM 0x02
/* ... */
/* Table 5 Communications Interface Class Control Protocol Codes */
#define USB_CDC_PROTOCOL_NONE 0x00
#define USB_CDC_PROTOCOL_AT 0x01
/* ... */
/* Table 6: Data Interface Class Code */
#define USB_CLASS_DATA 0x0A
/* Table 12: Type Values for the bDescriptorType Field */
#define CS_INTERFACE 0x24
#define CS_ENDPOINT 0x25
/* Table 13: bDescriptor SubType in Communications Class Functional
* Descriptors */
#define USB_CDC_TYPE_HEADER 0x00
#define USB_CDC_TYPE_CALL_MANAGEMENT 0x01
#define USB_CDC_TYPE_ACM 0x02
/* ... */
#define USB_CDC_TYPE_UNION 0x06
/* ... */
/* Table 15: Class-Specific Descriptor Header Format */
struct usb_cdc_header_descriptor {
uint8_t bFunctionLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint16_t bcdCDC;
} __attribute__((packed));
/* Table 16: Union Interface Functional Descriptor */
struct usb_cdc_union_descriptor {
uint8_t bFunctionLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bControlInterface;
uint8_t bSubordinateInterface0;
/* ... */
} __attribute__((packed));
/* Definitions for Abstract Control Model devices from:
* "Universal Serial Bus Communications Class Subclass Specification for
* PSTN Devices"
*/
/* Table 3: Call Management Functional Descriptor */
struct usb_cdc_call_management_descriptor {
uint8_t bFunctionLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bmCapabilities;
uint8_t bDataInterface;
} __attribute__((packed));
/* Table 4: Abstract Control Management Functional Descriptor */
struct usb_cdc_acm_descriptor {
uint8_t bFunctionLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bmCapabilities;
} __attribute__((packed));
/* Table 13: Class-Specific Request Codes for PSTN subclasses */
/* ... */
#define USB_CDC_REQ_SET_LINE_CODING 0x20
/* ... */
#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22
/* ... */
/* Table 17: Line Coding Structure */
struct usb_cdc_line_coding {
uint32_t dwDTERate;
uint8_t bCharFormat;
uint8_t bParityType;
uint8_t bDataBits;
} __attribute__((packed));
/* Table 30: Class-Specific Notification Codes for PSTN subclasses */
/* ... */
#define USB_CDC_NOTIFY_SERIAL_STATE 0x28
/* ... */
/* Notification Structure */
struct usb_cdc_notification {
uint8_t bmRequestType;
uint8_t bNotification;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} __attribute__((packed));
#endif

View File

@ -0,0 +1,255 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libopenstm32/rcc.h>
#include <libopenstm32/gpio.h>
#include <usbd.h>
#include <stdlib.h>
#include "cdc.h"
static const struct usb_device_descriptor dev = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = USB_CLASS_CDC,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = 0x0483,
.idProduct = 0x5740,
.bcdDevice = 0x0200,
.iManufacturer = 1,
.iProduct = 2,
.iSerialNumber = 3,
.bNumConfigurations = 1,
};
/* This notification endpoint isn't implemented. According to CDC spec its
* optional, but its absence causes a NULL pointer dereference in Linux cdc_acm
* driver. */
static const struct usb_endpoint_descriptor comm_endp[] = {{
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x83,
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
.wMaxPacketSize = 16,
.bInterval = 255,
}};
static const struct usb_endpoint_descriptor data_endp[] = {{
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x01,
.bmAttributes = USB_ENDPOINT_ATTR_BULK,
.wMaxPacketSize = 64,
.bInterval = 1,
}, {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x82,
.bmAttributes = USB_ENDPOINT_ATTR_BULK,
.wMaxPacketSize = 64,
.bInterval = 1,
}};
static const struct {
struct usb_cdc_header_descriptor header;
struct usb_cdc_call_management_descriptor call_mgmt;
struct usb_cdc_acm_descriptor acm;
struct usb_cdc_union_descriptor cdc_union;
} __attribute__((packed)) cdcacm_functional_descriptors = {
.header = {
.bFunctionLength = sizeof(struct usb_cdc_header_descriptor),
.bDescriptorType = CS_INTERFACE,
.bDescriptorSubtype = USB_CDC_TYPE_HEADER,
.bcdCDC = 0x0110,
},
.call_mgmt = {
.bFunctionLength =
sizeof(struct usb_cdc_call_management_descriptor),
.bDescriptorType = CS_INTERFACE,
.bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT,
.bmCapabilities = 0,
.bDataInterface = 1,
},
.acm = {
.bFunctionLength = sizeof(struct usb_cdc_acm_descriptor),
.bDescriptorType = CS_INTERFACE,
.bDescriptorSubtype = USB_CDC_TYPE_ACM,
.bmCapabilities = 0,
},
.cdc_union = {
.bFunctionLength = sizeof(struct usb_cdc_union_descriptor),
.bDescriptorType = CS_INTERFACE,
.bDescriptorSubtype = USB_CDC_TYPE_UNION,
.bControlInterface = 0,
.bSubordinateInterface0 = 1,
}
};
static const struct usb_interface_descriptor comm_iface[] = {{
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_CDC,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = USB_CDC_PROTOCOL_AT,
.iInterface = 0,
.endpoint = comm_endp,
.extra = &cdcacm_functional_descriptors,
.extralen = sizeof(cdcacm_functional_descriptors)
}};
static const struct usb_interface_descriptor data_iface[] = {{
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 1,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_DATA,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.iInterface = 0,
.endpoint = data_endp,
}};
static const struct usb_interface ifaces[] = {{
.num_altsetting = 1,
.altsetting = comm_iface,
}, {
.num_altsetting = 1,
.altsetting = data_iface,
}};
static const struct usb_config_descriptor config = {
.bLength = USB_DT_CONFIGURATION_SIZE,
.bDescriptorType = USB_DT_CONFIGURATION,
.wTotalLength = 0,
.bNumInterfaces = 2,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0x80,
.bMaxPower = 0x32,
.interface = ifaces,
};
static const char *usb_strings[] = {
"x",
"Black Sphere Technologies",
"CDC-ACM Demo",
"DEMO"
};
static int cdcacm_control_command(struct usb_setup_data *req,
void (**complete)(struct usb_setup_data *req))
{
(void)complete;
char buf[10];
struct usb_cdc_notification *notif = (void*)buf;
switch(req->bRequest) {
case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
/* This Linux cdc_acm driver requires this to be implemented
* even though it's optional in the CDC spec, and we don't
* advertise it in the ACM functional descriptor. */
/* We echo signals back to host as notification */
notif->bmRequestType = 0xA1;
notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE;
notif->wValue = 0;
notif->wIndex = 0;
notif->wLength = 2;
buf[8] = req->wValue & 3;
buf[9] = 0;
//usbd_ep_write_packet(0x83, buf, 10);
return 1;
}
return 0;
}
static int cdcacm_control_write(struct usb_setup_data *req, u8 *buf, u16 len,
void (**complete)(struct usb_setup_data *req))
{
(void)complete;
(void)buf;
switch(req->bRequest) {
case USB_CDC_REQ_SET_LINE_CODING:
if(len < sizeof(struct usb_cdc_line_coding))
return 0;
return 1;
}
return 0;
}
static void cdcacm_data_rx_cb(u8 ep)
{
(void)ep;
char buf[64];
int len = usbd_ep_read_packet(0x01, buf, 64);
if(len) {
usbd_ep_write_packet(0x82, buf, len);
buf[len] = 0;
}
}
static void cdcacm_set_config(u16 wValue)
{
(void)wValue;
usbd_ep_setup(0x01, USB_ENDPOINT_ATTR_BULK, 64, cdcacm_data_rx_cb);
usbd_ep_setup(0x82, USB_ENDPOINT_ATTR_BULK, 64, NULL);
usbd_ep_setup(0x83, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL);
usbd_register_control_command_callback(cdcacm_control_command);
usbd_register_control_write_callback(cdcacm_control_write);
}
int main(void)
{
rcc_clock_setup_in_hsi_out_48mhz();
rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN);
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_AFIOEN);
AFIO_MAPR |= AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON;
gpio_set_mode(GPIOA, GPIO_MODE_INPUT, 0, GPIO15);
usbd_init(&dev, &config, usb_strings);
usbd_register_set_config_callback(cdcacm_set_config);
gpio_set(GPIOA, GPIO15);
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO15);
while (1)
usbd_poll();
}

View File

@ -0,0 +1,29 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Thomas Otto <tommi@viadmin.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Define memory regions. */
MEMORY
{
rom (rx) : ORIGIN = 0x08000000, LENGTH =64K
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
}
/* Include the common ld script from libopenstm32. */
INCLUDE libopenstm32.ld

View File

@ -0,0 +1,95 @@
##
## This file is part of the libopenstm32 project.
##
## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
##
BINARY = usbdfu
# PREFIX ?= arm-none-eabi
PREFIX ?= arm-elf
CC = $(PREFIX)-gcc
LD = $(PREFIX)-gcc
OBJCOPY = $(PREFIX)-objcopy
OBJDUMP = $(PREFIX)-objdump
# Uncomment this line if you want to use the installed (not local) library.
#TOOLCHAIN_DIR = `dirname \`which $(CC)\``/..
TOOLCHAIN_DIR = ../../..
CFLAGS = -O0 -g3 -Wall -Wextra -I$(TOOLCHAIN_DIR)/include -fno-common \
-mcpu=cortex-m3 -mthumb
LDSCRIPT = $(BINARY).ld
LDFLAGS = -L$(TOOLCHAIN_DIR)/lib -T$(LDSCRIPT) -nostartfiles -Wl,--defsym,_stack=0x20001000
OBJS = $(BINARY).o
OPENOCD_BASE = /usr
OPENOCD = $(OPENOCD_BASE)/bin/openocd
OPENOCD_SCRIPTS = $(OPENOCD_BASE)/share/openocd/scripts
OPENOCD_FLASHER = $(OPENOCD_SCRIPTS)/interface/jtagkey.cfg
OPENOCD_BOARD = $(OPENOCD_SCRIPTS)/target/stm32.cfg
# Be silent per default, but 'make V=1' will show all compiler calls.
ifneq ($(V),1)
Q := @
NULL := 2>/dev/null
endif
all: images
images: $(BINARY)
@printf " OBJCOPY $(BINARY).bin\n"
$(Q)$(OBJCOPY) -Obinary $(BINARY) $(BINARY).bin
@printf " OBJCOPY $(BINARY).hex\n"
$(Q)$(OBJCOPY) -Oihex $(BINARY) $(BINARY).hex
@printf " OBJCOPY $(BINARY).srec\n"
$(Q)$(OBJCOPY) -Osrec $(BINARY) $(BINARY).srec
@printf " OBJDUMP $(BINARY).list\n"
$(Q)$(OBJDUMP) -S $(BINARY) > $(BINARY).list
$(BINARY): $(OBJS) $(LDSCRIPT)
@printf " LD $(subst $(shell pwd)/,,$(@))\n"
$(Q)$(LD) $(LDFLAGS) -o $(BINARY) $(OBJS) -lopenstm32
%.o: %.c Makefile
@printf " CC $(subst $(shell pwd)/,,$(@))\n"
$(Q)$(CC) $(CFLAGS) -o $@ -c $<
clean:
@printf " CLEAN $(subst $(shell pwd)/,,$(OBJS))\n"
$(Q)rm -f *.o
@printf " CLEAN $(BINARY)\n"
$(Q)rm -f $(BINARY)
@printf " CLEAN $(BINARY).bin\n"
$(Q)rm -f $(BINARY).bin
@printf " CLEAN $(BINARY).hex\n"
$(Q)rm -f $(BINARY).hex
@printf " CLEAN $(BINARY).srec\n"
$(Q)rm -f $(BINARY).srec
@printf " CLEAN $(BINARY).list\n"
$(Q)rm -f $(BINARY).list
flash: images
@printf " FLASH $(BINARY).bin\n"
@# IMPORTANT: Don't use "resume", only "reset" will work correctly!
$(Q)$(OPENOCD) -s $(OPENOCD_SCRIPTS) \
-f $(OPENOCD_FLASHER) \
-f $(OPENOCD_BOARD) \
-c "init" -c "reset halt" \
-c "flash write_image erase $(BINARY).hex" \
-c "reset" \
-c "shutdown" $(NULL)
.PHONY: images clean

View File

@ -0,0 +1,43 @@
------------------------------------------------------------------------------
README
------------------------------------------------------------------------------
This example implements a USB Device Firmware Upgrade (DFU) bootloader
to demonstrate the use of the USB device stack.
Building
--------
$ make
Running 'make' on the top-level libopenstm32 directory will automatically
also build this example. Or you can build the library "manually" and
then run 'make' in this directory.
You may want to override the toolchain (e.g., arm-elf or arm-none-eabi):
$ PREFIX=arm-none-eabi make
For a more verbose build you can use
$ make V=1
Flashing
--------
You can flash the generated code using OpenOCD:
$ make flash
Or you can do the same manually via:
$ openocd -f interface/jtagkey-tiny.cfg -f target/stm32.cfg
$ telnet localhost 4444
> reset halt
> flash write_image erase systick.hex
> reset
Replace the "jtagkey-tiny.cfg" with whatever JTAG device you are using, and/or
replace "stm.cfg" with your respective config file.

View File

@ -0,0 +1,81 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __DFU_H
#define __DFU_H
enum dfu_req {
DFU_DETACH,
DFU_DNLOAD,
DFU_UPLOAD,
DFU_GETSTATUS,
DFU_CLRSTATUS,
DFU_GETSTATE,
DFU_ABORT
};
enum dfu_status {
DFU_STATUS_OK,
DFU_STATUS_ERR_TARGET,
DFU_STATUS_ERR_FILE,
DFU_STATUS_ERR_WRITE,
DFU_STATUS_ERR_ERASE,
DFU_STATUS_ERR_CHECK_ERASED,
DFU_STATUS_ERR_PROG,
DFU_STATUS_ERR_VERIFY,
DFU_STATUS_ERR_ADDRESS,
DFU_STATUS_ERR_NOTDONE,
DFU_STATUS_ERR_FIRMWARE,
DFU_STATUS_ERR_VENDOR,
DFU_STATUS_ERR_USBR,
DFU_STATUS_ERR_POR,
DFU_STATUS_ERR_UNKNOWN,
DFU_STATUS_ERR_STALLEDPKT,
};
enum dfu_state {
STATE_APP_IDLE,
STATE_APP_DETACH,
STATE_DFU_IDLE,
STATE_DFU_DNLOAD_SYNC,
STATE_DFU_DNBUSY,
STATE_DFU_DNLOAD_IDLE,
STATE_DFU_MANIFEST_SYNC,
STATE_DFU_MANIFEST,
STATE_DFU_MANIFEST_WAIT_RESET,
STATE_DFU_UPLOAD_IDLE,
STATE_DFU_ERROR,
};
#define DFU_FUNCTIONAL 0x21
struct usb_dfu_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bmAttributes;
#define USB_DFU_CAN_DOWNLOAD 0x01
#define USB_DFU_CAN_UPLOAD 0x02
#define USB_DFU_MANIFEST_TOLERANT 0x04
#define USB_DFU_WILL_DETACH 0x08
uint16_t wDetachTimeout;
uint16_t wTransferSize;
uint16_t bcdDFUVersion;
} __attribute__((packed));
#endif

View File

@ -0,0 +1,298 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libopenstm32/rcc.h>
#include <libopenstm32/gpio.h>
#include <libopenstm32/flash.h>
#include <libopenstm32/scb.h>
#include <usbd.h>
#include <string.h>
#include "dfu.h"
#define APP_ADDRESS 0x08002000
/* Commands sent with wBlockNum == 0 as per ST implementation. */
#define CMD_SETADDR 0x21
#define CMD_ERASE 0x41
/* We need a special large control buffer for this device: */
u8 usbd_control_buffer[1024];
static enum dfu_state usbdfu_state = STATE_DFU_IDLE;
static struct {
u8 buf[sizeof(usbd_control_buffer)];
u16 len;
u32 addr;
u16 blocknum;
} prog;
const struct usb_device_descriptor dev = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = 0,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = 0x0483,
.idProduct = 0xDF11,
.bcdDevice = 0x0200,
.iManufacturer = 1,
.iProduct = 2,
.iSerialNumber = 3,
.bNumConfigurations = 1,
};
const struct usb_dfu_descriptor dfu_function = {
.bLength = sizeof(struct usb_dfu_descriptor),
.bDescriptorType = DFU_FUNCTIONAL,
.bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
.wDetachTimeout = 255,
.wTransferSize = 1024,
.bcdDFUVersion = 0x011A,
};
const struct usb_interface_descriptor iface = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = 0xFE, /* Device Firmware Upgrade */
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 2,
/* The ST Microelectronics DfuSe application needs this string.
* The format isn't documented... */
.iInterface = 4,
.extra = &dfu_function,
.extralen = sizeof(dfu_function),
};
const struct usb_interface ifaces[] = {{
.num_altsetting = 1,
.altsetting = &iface,
}};
const struct usb_config_descriptor config = {
.bLength = USB_DT_CONFIGURATION_SIZE,
.bDescriptorType = USB_DT_CONFIGURATION,
.wTotalLength = 0,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0xC0,
.bMaxPower = 0x32,
.interface = ifaces,
};
static const char *usb_strings[] = {
"x",
"Black Sphere Technologies",
"DFU Demo",
"DEMO",
/* This string is used by ST Microelectronics' DfuSe utility */
"@Internal Flash /0x08000000/8*001Ka,56*001Kg"
};
static u8 usbdfu_getstatus(u32 *bwPollTimeout)
{
switch(usbdfu_state) {
case STATE_DFU_DNLOAD_SYNC:
usbdfu_state = STATE_DFU_DNBUSY;
*bwPollTimeout = 100;
return DFU_STATUS_OK;
case STATE_DFU_MANIFEST_SYNC:
/* Device will reset when read is complete */
usbdfu_state = STATE_DFU_MANIFEST;
return DFU_STATUS_OK;
default:
return DFU_STATUS_OK;
}
}
static void usbdfu_getstatus_complete(struct usb_setup_data *req)
{
int i;
(void)req;
switch(usbdfu_state) {
case STATE_DFU_DNBUSY:
flash_unlock();
if(prog.blocknum == 0) {
switch(prog.buf[0]) {
case CMD_ERASE:
flash_erase_page(*(u32*)(prog.buf+1));
case CMD_SETADDR:
prog.addr = *(u32*)(prog.buf+1);
}
} else {
u32 baseaddr = prog.addr +
((prog.blocknum - 2) *
dfu_function.wTransferSize);
for(i = 0; i < prog.len; i += 2)
flash_program_half_word(baseaddr + i,
*(u16*)(prog.buf+i));
}
flash_lock();
/* We jump straight to dfuDNLOAD-IDLE,
* skipping dfuDNLOAD-SYNC
*/
usbdfu_state = STATE_DFU_DNLOAD_IDLE;
return;
case STATE_DFU_MANIFEST:
/* USB device must detach, we just reset... */
scb_reset_system();
return; /* Will never return */
default:
return;
}
}
static int usbdfu_control_command(struct usb_setup_data *req,
void (**complete)(struct usb_setup_data *req))
{
(void)complete;
if(req->bmRequestType != 0x21)
return 0; /* Only accept class request */
switch(req->bRequest) {
case DFU_DNLOAD:
usbdfu_state = STATE_DFU_MANIFEST_SYNC;
return 1;
case DFU_CLRSTATUS:
/* Clear error and return to dfuIDLE */
if(usbdfu_state == STATE_DFU_ERROR)
usbdfu_state = STATE_DFU_IDLE;
return 1;
case DFU_ABORT:
/* Abort returns to dfuIDLE state */
usbdfu_state = STATE_DFU_IDLE;
return 1;
}
return 0;
}
static int usbdfu_control_read(struct usb_setup_data *req, u8 **buf, u16 *len,
void (**complete)(struct usb_setup_data *req))
{
if(req->bmRequestType != 0xA1)
return 0; /* Only accept class request */
switch(req->bRequest) {
case DFU_UPLOAD:
/* Upload not supported for now */
return 0;
case DFU_GETSTATUS: {
u32 bwPollTimeout = 0; /* 24-bit integer in DFU class spec */
(*buf)[0] = usbdfu_getstatus(&bwPollTimeout);
(*buf)[1] = bwPollTimeout & 0xFF;
(*buf)[2] = (bwPollTimeout >> 8) & 0xFF;
(*buf)[3] = (bwPollTimeout >> 16) & 0xFF;
(*buf)[4] = usbdfu_state;
(*buf)[5] = 0; /* iString not used here */
*len = 6;
*complete = usbdfu_getstatus_complete;
return 1;
}
case DFU_GETSTATE:
/* Return state with no state transision */
*buf[0] = usbdfu_state;
*len = 1;
return 1;
}
return 0;
}
static int usbdfu_control_write(struct usb_setup_data *req, u8 *buf, u16 len,
void (**complete)(struct usb_setup_data *req))
{
(void)complete;
if(req->bmRequestType != 0x21)
return 0; /* Only accept class request */
if(req->bRequest != DFU_DNLOAD)
return 0;
/* Copy download data for use on GET_STATUS */
prog.blocknum = req->wValue;
prog.len = len;
memcpy(prog.buf, buf, len);
usbdfu_state = STATE_DFU_DNLOAD_SYNC;
return 1;
}
int main(void)
{
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
if(!gpio_get(GPIOA, GPIO10)) {
/* Boot the application if it's valid */
if((*(volatile u32*)APP_ADDRESS & 0x2FFE0000) == 0x20000000) {
/* Set vector table base address */
SCB_VTOR = APP_ADDRESS & 0xFFFF;
/* Initialise master stack pointer */
asm volatile ("msr msp, %0"::"g"
(*(volatile u32*)APP_ADDRESS));
/* Jump to application */
(*(void(**)())(APP_ADDRESS + 4))();
}
}
rcc_clock_setup_in_hsi_out_48mhz();
rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN);
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_AFIOEN);
AFIO_MAPR |= AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON;
gpio_set_mode(GPIOA, GPIO_MODE_INPUT, 0, GPIO15);
usbd_init(&dev, &config, usb_strings);
usbd_set_control_buffer_size(sizeof(usbd_control_buffer));
usbd_register_control_command_callback(usbdfu_control_command);
usbd_register_control_write_callback(usbdfu_control_write);
usbd_register_control_read_callback(usbdfu_control_read);
gpio_set(GPIOA, GPIO15);
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO15);
while (1)
usbd_poll();
}

View File

@ -0,0 +1,29 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Thomas Otto <tommi@viadmin.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Define memory regions. */
MEMORY
{
rom (rx) : ORIGIN = 0x08000000, LENGTH =8K
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 4K
}
/* Include the common ld script from libopenstm32. */
INCLUDE libopenstm32.ld

View File

@ -0,0 +1,95 @@
##
## This file is part of the libopenstm32 project.
##
## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
##
BINARY = usbhid
# PREFIX ?= arm-none-eabi
PREFIX ?= arm-elf
CC = $(PREFIX)-gcc
LD = $(PREFIX)-gcc
OBJCOPY = $(PREFIX)-objcopy
OBJDUMP = $(PREFIX)-objdump
# Uncomment this line if you want to use the installed (not local) library.
#TOOLCHAIN_DIR = `dirname \`which $(CC)\``/..
TOOLCHAIN_DIR = ../../..
CFLAGS = -O0 -g3 -Wall -Wextra -I$(TOOLCHAIN_DIR)/include -fno-common \
-mcpu=cortex-m3 -mthumb -I../usb_dfu
LDSCRIPT = $(BINARY).ld
LDFLAGS = -L$(TOOLCHAIN_DIR)/lib -T$(LDSCRIPT) -nostartfiles -Wl,--defsym,_stack=0x20001000 -Wl,-Ttext,0x8002000
OBJS = $(BINARY).o
OPENOCD_BASE = /usr
OPENOCD = $(OPENOCD_BASE)/bin/openocd
OPENOCD_SCRIPTS = $(OPENOCD_BASE)/share/openocd/scripts
OPENOCD_FLASHER = $(OPENOCD_SCRIPTS)/interface/jtagkey.cfg
OPENOCD_BOARD = $(OPENOCD_SCRIPTS)/target/stm32.cfg
# Be silent per default, but 'make V=1' will show all compiler calls.
ifneq ($(V),1)
Q := @
NULL := 2>/dev/null
endif
all: images
images: $(BINARY)
@printf " OBJCOPY $(BINARY).bin\n"
$(Q)$(OBJCOPY) -Obinary $(BINARY) $(BINARY).bin
@printf " OBJCOPY $(BINARY).hex\n"
$(Q)$(OBJCOPY) -Oihex $(BINARY) $(BINARY).hex
@printf " OBJCOPY $(BINARY).srec\n"
$(Q)$(OBJCOPY) -Osrec $(BINARY) $(BINARY).srec
@printf " OBJDUMP $(BINARY).list\n"
$(Q)$(OBJDUMP) -S $(BINARY) > $(BINARY).list
$(BINARY): $(OBJS) $(LDSCRIPT)
@printf " LD $(subst $(shell pwd)/,,$(@))\n"
$(Q)$(LD) $(LDFLAGS) -o $(BINARY) $(OBJS) -lopenstm32
%.o: %.c Makefile
@printf " CC $(subst $(shell pwd)/,,$(@))\n"
$(Q)$(CC) $(CFLAGS) -o $@ -c $<
clean:
@printf " CLEAN $(subst $(shell pwd)/,,$(OBJS))\n"
$(Q)rm -f *.o
@printf " CLEAN $(BINARY)\n"
$(Q)rm -f $(BINARY)
@printf " CLEAN $(BINARY).bin\n"
$(Q)rm -f $(BINARY).bin
@printf " CLEAN $(BINARY).hex\n"
$(Q)rm -f $(BINARY).hex
@printf " CLEAN $(BINARY).srec\n"
$(Q)rm -f $(BINARY).srec
@printf " CLEAN $(BINARY).list\n"
$(Q)rm -f $(BINARY).list
flash: images
@printf " FLASH $(BINARY).bin\n"
@# IMPORTANT: Don't use "resume", only "reset" will work correctly!
$(Q)$(OPENOCD) -s $(OPENOCD_SCRIPTS) \
-f $(OPENOCD_FLASHER) \
-f $(OPENOCD_BOARD) \
-c "init" -c "reset halt" \
-c "flash write_image erase $(BINARY).hex" \
-c "reset" \
-c "shutdown" $(NULL)
.PHONY: images clean

View File

@ -0,0 +1,43 @@
------------------------------------------------------------------------------
README
------------------------------------------------------------------------------
This example implements a USB Human Interface Device (HID)
to demonstrate the use of the USB device stack.
Building
--------
$ make
Running 'make' on the top-level libopenstm32 directory will automatically
also build this example. Or you can build the library "manually" and
then run 'make' in this directory.
You may want to override the toolchain (e.g., arm-elf or arm-none-eabi):
$ PREFIX=arm-none-eabi make
For a more verbose build you can use
$ make V=1
Flashing
--------
You can flash the generated code using OpenOCD:
$ make flash
Or you can do the same manually via:
$ openocd -f interface/jtagkey-tiny.cfg -f target/stm32.cfg
$ telnet localhost 4444
> reset halt
> flash write_image erase systick.hex
> reset
Replace the "jtagkey-tiny.cfg" with whatever JTAG device you are using, and/or
replace "stm.cfg" with your respective config file.

View File

@ -0,0 +1,38 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __HID_H
#define __HID_H
#include <stdint.h>
#define USB_CLASS_HID 3
#define USB_DT_HID 0x21
#define USB_DT_REPORT 0x22
struct usb_hid_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdHID;
uint8_t bCountryCode;
uint8_t bNumDescriptors;
} __attribute__((packed));
#endif

View File

@ -0,0 +1,267 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libopenstm32/rcc.h>
#include <libopenstm32/gpio.h>
#include <libopenstm32/systick.h>
#include <usbd.h>
#include <stdlib.h>
#include "hid.h"
/* Define this to include the DFU APP interface. */
#define INCLUDE_DFU_INTERFACE
#ifdef INCLUDE_DFU_INTERFACE
# include <libopenstm32/scb.h>
# include "../usb_dfu/dfu.h"
#endif
const struct usb_device_descriptor dev = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = 0,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = 0x0483,
.idProduct = 0x5710,
.bcdDevice = 0x0200,
.iManufacturer = 1,
.iProduct = 2,
.iSerialNumber = 3,
.bNumConfigurations = 1,
};
/* I have no idea what this means. I haven't read the HID spec. */
static const u8 hid_report_descriptor[] = {
0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01,
0xA1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03,
0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01,
0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01,
0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38,
0x15, 0x81, 0x25, 0x7F, 0x75, 0x08, 0x95, 0x03,
0x81, 0x06, 0xC0, 0x09, 0x3c, 0x05, 0xff, 0x09,
0x01, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95,
0x02, 0xb1, 0x22, 0x75, 0x06, 0x95, 0x01, 0xb1,
0x01, 0xc0
};
static const struct {
struct usb_hid_descriptor hid_descriptor;
struct {
uint8_t bReportDescriptorType;
uint16_t wDescriptorLength;
} __attribute__((packed)) hid_report;
} __attribute__((packed)) hid_function = {
.hid_descriptor = {
.bLength = sizeof(hid_function),
.bDescriptorType = USB_DT_HID,
.bcdHID = 0x0100,
.bCountryCode = 0,
.bNumDescriptors = 1,
},
.hid_report = {
.bReportDescriptorType = USB_DT_REPORT,
.wDescriptorLength = sizeof(hid_report_descriptor),
}
};
const struct usb_endpoint_descriptor hid_endpoint = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x81,
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
.wMaxPacketSize = 4,
.bInterval = 0x20,
};
const struct usb_interface_descriptor hid_iface = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_HID,
.bInterfaceSubClass = 1, /* boot */
.bInterfaceProtocol = 2, /* mouse */
.iInterface = 0,
.endpoint = &hid_endpoint,
.extra = &hid_function,
.extralen = sizeof(hid_function),
};
#ifdef INCLUDE_DFU_INTERFACE
const struct usb_dfu_descriptor dfu_function = {
.bLength = sizeof(struct usb_dfu_descriptor),
.bDescriptorType = DFU_FUNCTIONAL,
.bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
.wDetachTimeout = 255,
.wTransferSize = 1024,
.bcdDFUVersion = 0x011A,
};
const struct usb_interface_descriptor dfu_iface = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 1,
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = 0xFE,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 1,
.iInterface = 0,
.extra = &dfu_function,
.extralen = sizeof(dfu_function),
};
#endif
const struct usb_interface ifaces[] = {{
.num_altsetting = 1,
.altsetting = &hid_iface,
#ifdef INCLUDE_DFU_INTERFACE
}, {
.num_altsetting = 1,
.altsetting = &dfu_iface,
#endif
}};
const struct usb_config_descriptor config = {
.bLength = USB_DT_CONFIGURATION_SIZE,
.bDescriptorType = USB_DT_CONFIGURATION,
.wTotalLength = 0,
#ifdef INCLUDE_DFU_INTERFACE
.bNumInterfaces = 2,
#else
.bNumInterfaces = 1,
#endif
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0xC0,
.bMaxPower = 0x32,
.interface = ifaces,
};
static const char *usb_strings[] = {
"x",
"Black Sphere Technologies",
"HID Demo",
"DEMO",
};
static int hid_control_read(struct usb_setup_data *req, u8 **buf, u16 *len,
void (**complete)(struct usb_setup_data *req))
{
(void)complete;
if((req->bmRequestType != 0x81) ||
(req->bRequest != USB_REQ_GET_DESCRIPTOR) ||
(req->wValue != 0x2200))
return 0;
/* Handle the HID report descriptor */
*buf = (u8*)hid_report_descriptor;
*len = sizeof(hid_report_descriptor);
return 1;
}
#ifdef INCLUDE_DFU_INTERFACE
static void dfu_detach_complete(struct usb_setup_data *req)
{
(void)req;
gpio_set_mode(GPIOA, GPIO_MODE_INPUT, 0, GPIO15);
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO10);
gpio_set(GPIOA, GPIO10);
scb_reset_core();
}
static int dfu_control_command(struct usb_setup_data *req,
void (**complete)(struct usb_setup_data *req))
{
if((req->bmRequestType != 0x21) || (req->bRequest != DFU_DETACH))
return 0; /* Only accept class request */
*complete = dfu_detach_complete;
return 1;
}
#endif
static void hid_set_config(u16 wValue)
{
(void)wValue;
usbd_ep_setup(0x81, USB_ENDPOINT_ATTR_INTERRUPT, 4, NULL);
usbd_register_control_read_callback(hid_control_read);
#ifdef INCLUDE_DFU_INTERFACE
usbd_register_control_command_callback(dfu_control_command);
#endif
systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB_DIV8);
systick_set_reload(100000);
systick_interrupt_enable();
systick_counter_enable();
}
int main(void)
{
rcc_clock_setup_in_hsi_out_48mhz();
rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN);
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_AFIOEN);
AFIO_MAPR |= AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON;
gpio_set_mode(GPIOA, GPIO_MODE_INPUT, 0, GPIO15);
usbd_init(&dev, &config, usb_strings);
usbd_register_set_config_callback(hid_set_config);
gpio_set(GPIOA, GPIO15);
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO15);
while (1)
usbd_poll();
}
void sys_tick_handler(void)
{
static int x = 0;
static int dir = 1;
uint8_t buf[4] = {0, 0, 0, 0};
buf[1] = dir;
x += dir;
if(x > 30) dir = -dir;
if(x < -30) dir = -dir;
usbd_ep_write_packet(0x81, buf, 4);
}

View File

@ -0,0 +1,29 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Thomas Otto <tommi@viadmin.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Define memory regions. */
MEMORY
{
rom (rx) : ORIGIN = 0x08000000, LENGTH =64K
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
}
/* Include the common ld script from libopenstm32. */
INCLUDE libopenstm32.ld

View File

@ -396,8 +396,10 @@ void rcc_set_adcpre(u32 adcpre);
void rcc_set_ppre2(u32 ppre2);
void rcc_set_ppre1(u32 ppre1);
void rcc_set_hpre(u32 hpre);
void rcc_set_usbpre(u32 usbpre);
u32 rcc_get_system_clock_source(int i);
void rcc_clock_setup_in_hsi_out_64mhz(void);
void rcc_clock_setup_in_hsi_out_48mhz(void);
void rcc_clock_setup_in_hse_8mhz_out_72mhz(void);
void rcc_clock_setup_in_hse_16mhz_out_72mhz(void);
void rcc_backupdomain_reset(void);

View File

@ -122,6 +122,7 @@
/* VECTKEYSTAT[31:16]/ VECTKEY[31:16] Register key */
#define SCB_AIRCR_VECTKEYSTAT_LSB 16
#define SCB_AIRCR_VECTKEY 0x05FA0000
/* ENDIANESS Data endianness bit */
#define SCB_AIRCR_ENDIANESS (1 << 15)
/* Bits [14:11]: reserved - must be kept cleared */
@ -291,6 +292,8 @@
/* BFAR [31:0]: Bus fault address */
/* --- SCB functions ------------------------------------------------------- */
void scb_reset_core(void);
void scb_reset_system(void);
/* TODO: */

View File

@ -51,7 +51,7 @@
* TODO: We may need a faster implementation of that one?
*/
#define TOG_SET_REG_BIT_MSK(REG, MSK, BIT) \
{ \
do { \
register u16 toggle_mask = GET_REG(REG) & MSK; \
register u16 bit_selector; \
for (bit_selector = 1; bit_selector; bit_selector <<= 1) { \
@ -59,6 +59,6 @@
toggle_mask ^= bit_selector; \
} \
SET_REG(REG, toggle_mask); \
}
} while(0)
#endif

View File

@ -20,6 +20,10 @@
#ifndef LIBOPENSTM32_USB_H
#define LIBOPENSTM32_USB_H
#include <libopenstm32/memorymap.h>
#include <libopenstm32/common.h>
#include <libopenstm32/tools.h>
/******************************************************************************
* USB base addresses
******************************************************************************/
@ -42,7 +46,7 @@
/* USB Buffer table address register */
#define USB_BTABLE_REG ((volatile u32 *)(USB_DEV_FS_BASE + 0x50))
/* USB EP register */
#define USB_EP_REG(EP) ((volatile u32 *)(USB_DEV_FS_BASE + EP))
#define USB_EP_REG(EP) ((volatile u32 *)(USB_DEV_FS_BASE) + (EP))
/******************************************************************************
* USB control register masks / bits

73
include/usbd.h Normal file
View File

@ -0,0 +1,73 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __USBD_H
#define __USBD_H
#include <usbstd.h>
/* Static buffer for control transactions:
* This is defined as weak in the library, applicaiton
* may provide if a larger buffer is requred. */
extern uint8_t usbd_control_buffer[];
/* <usb.c> */
extern int usbd_init(const struct usb_device_descriptor *dev,
const struct usb_config_descriptor *conf,
const char **strings);
extern void usbd_set_control_buffer_size(uint16_t size);
extern void usbd_register_reset_callback(void (*callback)(void));
extern void usbd_register_suspend_callback(void (*callback)(void));
extern void usbd_register_resume_callback(void (*callback)(void));
/* <usb_control.c> */
extern void usbd_register_control_command_callback(
int (*callback)(struct usb_setup_data *req,
void (**complete)(struct usb_setup_data *req)));
extern void usbd_register_control_read_callback(
int (*callback)(struct usb_setup_data *req, uint8_t **buf,
uint16_t *len, void (**complete)(struct usb_setup_data *req)));
extern void usbd_register_control_write_callback(
int (*callback)(struct usb_setup_data *req, uint8_t *buf,
uint16_t len, void (**complete)(struct usb_setup_data *req)));
/* <usb_standard.c> */
extern void
usbd_register_set_config_callback(void (*callback)(uint16_t wValue));
/* Functions to be provided by the hardware abstraction layer */
extern void usbd_poll(void);
extern void usbd_ep_setup(uint8_t addr, uint8_t type, uint16_t max_size,
void (*callback)(uint8_t ep));
extern uint16_t
usbd_ep_write_packet(uint8_t addr, const void *buf, uint16_t len);
extern uint16_t
usbd_ep_read_packet(uint8_t addr, void *buf, uint16_t len);
extern void usbd_ep_stall(uint8_t addr);
/* Optional */
extern void usbd_cable_connect(uint8_t on);
#endif

198
include/usbstd.h Normal file
View File

@ -0,0 +1,198 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __USBSTD_H
#define __USBSTD_H
/*
* This file contains structure definitions for the USB control structures
* defined in chapter 9 of the "Univeral Serial Bus Specification Revision 2.0"
* Available from the USB Implementers Forum - http://www.usb.org/
*/
#include <stdint.h>
/* USB Setup Data structure - Table 9-2 */
struct usb_setup_data {
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} __attribute__((packed));
/* bmRequestType bit definitions */
#define USB_REQ_TYPE_IN 0x80
#define USB_REQ_TYPE_STANDARD 0x00
#define USB_REQ_TYPE_CLASS 0x20
#define USB_REQ_TYPE_VENDOR 0x40
#define USB_REQ_TYPE_DEVICE 0x00
#define USB_REQ_TYPE_INTERFACE 0x01
#define USB_REQ_TYPE_ENDPOINT 0x02
/* USB Standard Request Codes - Table 9-4 */
#define USB_REQ_GET_STATUS 0
#define USB_REQ_CLEAR_FEATURE 1
/* Reserved for future use: 2 */
#define USB_REQ_SET_FEATURE 3
/* Reserved for future use: 3 */
#define USB_REQ_SET_ADDRESS 5
#define USB_REQ_GET_DESCRIPTOR 6
#define USB_REQ_SET_DESCRIPTOR 7
#define USB_REQ_GET_CONFIGURATION 8
#define USB_REQ_SET_CONFIGURATION 9
#define USB_REQ_GET_INTERFACE 10
#define USB_REQ_SET_INTERFACE 11
#define USB_REQ_SET_SYNCH_FRAME 12
/* USB Descriptor Types - Table 9-5 */
#define USB_DT_DEVICE 1
#define USB_DT_CONFIGURATION 2
#define USB_DT_STRING 3
#define USB_DT_INTERFACE 4
#define USB_DT_ENDPOINT 5
#define USB_DT_DEVICE_QUALIFIER 6
#define USB_DT_OTHER_SPEED_CONFIGURATION 7
#define USB_DT_INTERFACE_POWER 8
/* USB Standard Feature Selectors - Table 9-6 */
#define USB_FEAT_DEVICE_REMOTE_WAKEUP 1
#define USB_FEAT_ENDPOINT_HALT 0
#define USB_FEAT_TEST_MOED 2
/* Information Returned by a GetStatus() Request to a Device - Figure 9-4 */
#define USB_DEV_STATUS_SELF_POWERED 0x01
#define USB_DEV_STATUS_REMOTE_WAKEUP 0x02
/* USB Standard Device Descriptor - Table 9-8 */
struct usb_device_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t iManufacturer;
uint8_t iProduct;
uint8_t iSerialNumber;
uint8_t bNumConfigurations;
} __attribute__((packed));
#define USB_DT_DEVICE_SIZE sizeof(struct usb_device_descriptor)
/* USB Device_Qualifier Descriptor - Table 9-9
* Not used in this implementation.
*/
struct usb_device_qualifier_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint8_t bNumConfigurations;
uint8_t bReserved;
} __attribute__((packed));
/* USB Standard Configuration Descriptor - Table 9-10 */
struct usb_config_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
/* Descriptor ends here. The following are used internally: */
const struct usb_interface {
int num_altsetting;
const struct usb_interface_descriptor *altsetting;
} *interface;
} __attribute__((packed));
#define USB_DT_CONFIGURATION_SIZE 9
/* USB Configuration Descriptor bmAttributes bit definitions */
#define USB_CONFIG_ATTR_SELF_POWERED 0x40
#define USB_CONFIG_ATTR_REMOTE_WAKEUP 0x20
/* Other Speed Configuration is the same as Configuration Descriptor.
* - Table 9-11
*/
/* USB Standard Interface Descriptor - Table 9-12 */
struct usb_interface_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bNumEndpoints;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t iInterface;
/* Descriptor ends here. The following are used internally: */
const struct usb_endpoint_descriptor *endpoint;
const void *extra;
int extralen;
} __attribute__((packed));
#define USB_DT_INTERFACE_SIZE 9
/* USB Standard Endpoint Descriptor - Table 9-13 */
struct usb_endpoint_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
} __attribute__((packed));
#define USB_DT_ENDPOINT_SIZE sizeof(struct usb_endpoint_descriptor)
/* USB Endpoint Descriptor bmAttributes bit definitions */
#define USB_ENDPOINT_ATTR_CONTROL 0x00
#define USB_ENDPOINT_ATTR_ISOCHRONOUS 0x01
#define USB_ENDPOINT_ATTR_BULK 0x02
#define USB_ENDPOINT_ATTR_INTERRUPT 0x03
#define USB_ENDPOINT_ATTR_NOSYNC 0x00
#define USB_ENDPOINT_ATTR_ASYNC 0x04
#define USB_ENDPOINT_ATTR_ADAPTIVE 0x08
#define USB_ENDPOINT_ATTR_SYNC 0x0C
#define USB_ENDPOINT_ATTR_DATA 0x00
#define USB_ENDPOINT_ATTR_FEEDBACK 0x10
#define USB_ENDPOINT_ATTR_IMPLICIT_FEEDBACK_DATA 0x20
/* Table 9-15 specifies String Descriptor Zero.
* Table 9-16 specified UNICODE String Descriptor.
*/
struct usb_string_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wData[];
} __attribute__((packed));
#endif

View File

@ -28,7 +28,10 @@ CFLAGS = -Os -g -Wall -Wextra -I../include -fno-common \
# ARFLAGS = rcsv
ARFLAGS = rcs
OBJS = vector.o rcc.o gpio.o usart.o adc.o spi.o flash.o nvic.o \
rtc.o i2c.o dma.o systick.o exti.o
rtc.o i2c.o dma.o systick.o exti.o scb.o \
usb_f103.o usb.o usb_control.o usb_standard.o
VPATH += usb
# Be silent per default, but 'make V=1' will show all compiler calls.
ifneq ($(V),1)

View File

@ -287,7 +287,7 @@ void rcc_set_adcpre(u32 adcpre)
u32 reg32;
reg32 = RCC_CFGR;
reg32 &= ((1 << 14) | (1 << 15));
reg32 &= ~((1 << 14) | (1 << 15));
RCC_CFGR = (reg32 | (adcpre << 14));
}
@ -296,7 +296,7 @@ void rcc_set_ppre2(u32 ppre2)
u32 reg32;
reg32 = RCC_CFGR;
reg32 &= ((1 << 11) | (1 << 12) | (1 << 13));
reg32 &= ~((1 << 11) | (1 << 12) | (1 << 13));
RCC_CFGR = (reg32 | (ppre2 << 11));
}
@ -305,7 +305,7 @@ void rcc_set_ppre1(u32 ppre1)
u32 reg32;
reg32 = RCC_CFGR;
reg32 &= ((1 << 8) | (1 << 9) | (1 << 10));
reg32 &= ~((1 << 8) | (1 << 9) | (1 << 10));
RCC_CFGR = (reg32 | (ppre1 << 8));
}
@ -314,10 +314,19 @@ void rcc_set_hpre(u32 hpre)
u32 reg32;
reg32 = RCC_CFGR;
reg32 &= ((1 << 4) | (1 << 5) | (1 << 6) | (1 << 7));
reg32 &= ~((1 << 4) | (1 << 5) | (1 << 6) | (1 << 7));
RCC_CFGR = (reg32 | (hpre << 4));
}
void rcc_set_usbpre(u32 usbpre)
{
u32 reg32;
reg32 = RCC_CFGR;
reg32 &= ~(1 << 22);
RCC_CFGR = (reg32 | (usbpre << 22));
}
u32 rcc_system_clock_source(void)
{
/* Return the clock source which is used as system clock. */
@ -371,6 +380,50 @@ void rcc_clock_setup_in_hsi_out_64mhz(void)
rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_PLLCLK);
}
void rcc_clock_setup_in_hsi_out_48mhz(void)
{
/* Enable internal high-speed oscillator. */
rcc_osc_on(HSI);
rcc_wait_for_osc_ready(HSI);
/* Select HSI as SYSCLK source. */
rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_HSICLK);
/*
* Set prescalers for AHB, ADC, ABP1, ABP2.
* Do this before touching the PLL (TODO: why?).
*/
rcc_set_hpre(RCC_CFGR_HPRE_SYSCLK_NODIV); /* Max. 72MHz */
rcc_set_adcpre(RCC_CFGR_ADCPRE_PCLK2_DIV8); /* Max. 14MHz */
rcc_set_ppre1(RCC_CFGR_PPRE1_HCLK_DIV2); /* Max. 36MHz */
rcc_set_ppre2(RCC_CFGR_PPRE2_HCLK_NODIV); /* Max. 72MHz */
rcc_set_usbpre(RCC_CFGR_USBPRE_PLL_CLK_NODIV); /* 48 MHz */
/*
* Sysclk runs with 48MHz -> 1 waitstates.
* 0WS from 0-24MHz
* 1WS from 24-48MHz
* 2WS from 48-72MHz
*/
flash_set_ws(FLASH_LATENCY_1WS);
/*
* Set the PLL multiplication factor to 12.
* 8MHz (internal) * 12 (multiplier) / 2 (PLLSRC_HSI_CLK_DIV2) = 48MHz
*/
rcc_set_pll_multiplication_factor(RCC_CFGR_PLLMUL_PLL_CLK_MUL12);
/* Select HSI/2 as PLL source. */
rcc_set_pll_source(RCC_CFGR_PLLSRC_HSI_CLK_DIV2);
/* Enable PLL oscillator and wait for it to stabilize. */
rcc_osc_on(PLL);
rcc_wait_for_osc_ready(PLL);
/* Select PLL as SYSCLK source. */
rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_PLLCLK);
}
void rcc_clock_setup_in_hse_8mhz_out_72mhz(void)
{
/* Enable internal high-speed oscillator. */

31
lib/scb.c Normal file
View File

@ -0,0 +1,31 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libopenstm32/scb.h>
void scb_reset_core(void)
{
SCB_AIRCR = SCB_AIRCR_VECTKEY | SCB_AIRCR_VECTRESET;
}
void scb_reset_system(void)
{
SCB_AIRCR = SCB_AIRCR_VECTKEY | SCB_AIRCR_SYSRESETREQ;
}

95
lib/usb/usb.c Normal file
View File

@ -0,0 +1,95 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <usbd.h>
#include <string.h>
#include "usb_private.h"
struct _usbd_device _usbd_device;
uint8_t usbd_control_buffer[128] __attribute__((weak));
/** Main initialization entry point.
*
* Initialize the USB firmware library to implement the USB device described
* by the descriptors provided.
*
* It is required that the 48MHz USB clock is already available.
*
* @param dev Pointer to USB Device descriptor. This must not be changed
* while the device is in use.
* @param conf Pointer to array of USB Configuration descriptors. These
* must not be changed while the device is in use. The length
* of this array is determined by the bNumConfigurations field
* in the device descriptor.
* @return Zero on success (currently cannot fail)
*/
int usbd_init(const struct usb_device_descriptor *dev,
const struct usb_config_descriptor *conf,
const char **strings)
{
_usbd_device.desc = dev;
_usbd_device.config = conf;
_usbd_device.strings = strings;
_usbd_device.ctrl_buf = usbd_control_buffer;
_usbd_device.ctrl_buf_len = sizeof(usbd_control_buffer);
_usbd_hw_init();
_usbd_device.user_callback_ctr[0][USB_TRANSACTION_SETUP] =
_usbd_control_setup;
_usbd_device.user_callback_ctr[0][USB_TRANSACTION_OUT] =
_usbd_control_out;
_usbd_device.user_callback_ctr[0][USB_TRANSACTION_IN] =
_usbd_control_in;
return 0;
}
void usbd_register_reset_callback(void (*callback)(void))
{
_usbd_device.user_callback_reset = callback;
}
void usbd_register_suspend_callback(void (*callback)(void))
{
_usbd_device.user_callback_suspend = callback;
}
void usbd_register_resume_callback(void (*callback)(void))
{
_usbd_device.user_callback_resume = callback;
}
void usbd_set_control_buffer_size(uint16_t size)
{
_usbd_device.ctrl_buf_len = size;
}
void _usbd_reset(void)
{
_usbd_device.current_address = 0;
_usbd_device.current_config = 0;
usbd_ep_setup(0, USB_ENDPOINT_ATTR_CONTROL, 64, NULL);
_usbd_hw_set_address(0);
if(_usbd_device.user_callback_reset)
_usbd_device.user_callback_reset();
}

280
lib/usb/usb_control.c Normal file
View File

@ -0,0 +1,280 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <usbd.h>
#include <stdlib.h>
#include "usb_private.h"
static struct usb_control_state {
enum { IDLE, STALLED,
DATA_IN, LAST_DATA_IN, STATUS_IN,
DATA_OUT, LAST_DATA_OUT, STATUS_OUT
} state;
struct usb_setup_data req;
uint8_t *ctrl_buf;
uint16_t ctrl_len;
void (*complete)(struct usb_setup_data *req);
} control_state;
/** Register application callback function for handling of usb control
* request with no data. */
void usbd_register_control_command_callback(
int (*callback)(struct usb_setup_data *req,
void (**complete)(struct usb_setup_data *req)))
{
_usbd_device.user_callback_control_command = callback;
}
/** Register application callback function for handling of usb control
* request to read data. */
void usbd_register_control_read_callback(
int (*callback)(struct usb_setup_data *req, uint8_t **buf,
uint16_t *len, void (**complete)(struct usb_setup_data *req)))
{
_usbd_device.user_callback_control_read = callback;
}
/** Register application callback function for handling of usb control
* request with received data. */
void usbd_register_control_write_callback(
int (*callback)(struct usb_setup_data *req, uint8_t *buf, uint16_t len,
void (**complete)(struct usb_setup_data *req)))
{
_usbd_device.user_callback_control_write = callback;
}
static void usb_control_send_chunk(void)
{
if(_usbd_device.desc->bMaxPacketSize0 < control_state.ctrl_len) {
/* Data stage, normal transmission */
usbd_ep_write_packet(0, control_state.ctrl_buf,
_usbd_device.desc->bMaxPacketSize0);
control_state.state = DATA_IN;
control_state.ctrl_buf += _usbd_device.desc->bMaxPacketSize0;
control_state.ctrl_len -= _usbd_device.desc->bMaxPacketSize0;
} else {
/* Data stage, end of transmission */
usbd_ep_write_packet(0, control_state.ctrl_buf,
control_state.ctrl_len);
control_state.state = LAST_DATA_IN;
control_state.ctrl_len = 0;
control_state.ctrl_buf = NULL;
}
}
static int usb_control_recv_chunk(void)
{
uint16_t packetsize = MIN(_usbd_device.desc->bMaxPacketSize0,
control_state.req.wLength -
control_state.ctrl_len);
uint16_t size = usbd_ep_read_packet(0,
control_state.ctrl_buf + control_state.ctrl_len,
packetsize);
if (size != packetsize) {
usbd_ep_stall(0);
return -1;
}
control_state.ctrl_len += size;
return packetsize;
}
static void usb_control_setup_nodata(struct usb_setup_data *req)
{
int result = 0;
/* Call user command hook function */
if(_usbd_device.user_callback_control_command)
result = _usbd_device.user_callback_control_command(req,
&control_state.complete);
/* Try standard command if not already handled */
if(!result)
result = _usbd_standard_request_command(req);
if(result) {
/* Go to status stage if handled */
usbd_ep_write_packet(0, NULL, 0);
control_state.state = STATUS_IN;
} else {
/* Stall endpoint on failure */
usbd_ep_stall(0);
}
}
static void usb_control_setup_read(struct usb_setup_data *req)
{
int result = 0;
control_state.ctrl_buf = _usbd_device.ctrl_buf;
control_state.ctrl_len = req->wLength;
/* Call user command hook function */
if(_usbd_device.user_callback_control_read)
result = _usbd_device.user_callback_control_read(req,
&control_state.ctrl_buf,
&control_state.ctrl_len,
&control_state.complete);
/* Try standard request if not already handled */
if(!result)
result = _usbd_standard_request_read(req,
&control_state.ctrl_buf,
&control_state.ctrl_len);
if(result) {
/* Go to status stage if handled */
usb_control_send_chunk();
} else {
/* Stall endpoint on failure */
usbd_ep_stall(0);
}
}
static void usb_control_setup_write(struct usb_setup_data *req)
{
if(req->wLength > _usbd_device.ctrl_buf_len) {
usbd_ep_stall(0);
return;
}
/* Buffer into which to write received data */
control_state.ctrl_buf = _usbd_device.ctrl_buf;
control_state.ctrl_len = 0;
/* Wait for DATA OUT Stage */
if(req->wLength > _usbd_device.desc->bMaxPacketSize0)
control_state.state = DATA_OUT;
else control_state.state = LAST_DATA_OUT;
}
void _usbd_control_setup(uint8_t ea)
{
struct usb_setup_data *req = &control_state.req;
(void)ea;
control_state.complete = NULL;
if(usbd_ep_read_packet(0, req, 8) != 8) {
usbd_ep_stall(0);
return;
}
if(req->wLength == 0) {
usb_control_setup_nodata(req);
} else if(req->bmRequestType & 0x80) {
usb_control_setup_read(req);
} else {
usb_control_setup_write(req);
}
}
void _usbd_control_out(uint8_t ea)
{
(void)ea;
switch(control_state.state) {
case DATA_OUT:
if(usb_control_recv_chunk() < 0) break;
if((control_state.req.wLength - control_state.ctrl_len) <=
_usbd_device.desc->bMaxPacketSize0)
control_state.state = LAST_DATA_OUT;
break;
case LAST_DATA_OUT: {
int result = 0;
if(usb_control_recv_chunk() < 0) break;
/* We have now received the full data payload.
* Invoke callback to process.
*/
if(_usbd_device.user_callback_control_write)
result = _usbd_device.user_callback_control_write(
&control_state.req,
control_state.ctrl_buf,
control_state.ctrl_len,
&control_state.complete);
if(!result)
result = _usbd_standard_request_write(
&control_state.req,
control_state.ctrl_buf,
control_state.ctrl_len);
if(result) {
usbd_ep_write_packet(0, NULL, 0);
control_state.state = STATUS_IN;
} else {
usbd_ep_stall(0);
}
break;
}
case STATUS_OUT: {
usbd_ep_read_packet(0, NULL, 0);
control_state.state = IDLE;
if (control_state.complete)
control_state.complete(&control_state.req);
control_state.complete = NULL;
break;
}
default:
usbd_ep_stall(0);
}
}
void _usbd_control_in(uint8_t ea)
{
(void)ea;
switch(control_state.state) {
case DATA_IN:
usb_control_send_chunk();
break;
case LAST_DATA_IN:
control_state.state = STATUS_OUT;
break;
case STATUS_IN: {
struct usb_setup_data *req = &control_state.req;
if (control_state.complete)
control_state.complete(&control_state.req);
/* Exception: Handle SET ADDRESS function here... */
if((req->bmRequestType == 0) &&
(req->bRequest == USB_REQ_SET_ADDRESS))
_usbd_hw_set_address(req->wValue);
control_state.state = IDLE;
break;
}
default:
usbd_ep_stall(0);
}
}

223
lib/usb/usb_f103.c Normal file
View File

@ -0,0 +1,223 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libopenstm32/common.h>
#include <libopenstm32/tools.h>
#include <libopenstm32/usb.h>
#include <usbd.h>
#include "usb_private.h"
/** Initialize USB Device Controller.
*
* Function initializes the USB Device controller hardware
* of the STM32 microcontroller.
*/
void _usbd_hw_init(void)
{
SET_REG(USB_CNTR_REG, 0);
SET_REG(USB_BTABLE_REG, 0);
SET_REG(USB_ISTR_REG, 0);
/* Enable RESET, SUSPEND, RESUME and CTR interrupts */
SET_REG(USB_CNTR_REG, USB_CNTR_RESETM | USB_CNTR_CTRM |
USB_CNTR_SUSPM | USB_CNTR_WKUPM);
}
void _usbd_hw_set_address(u8 addr)
{
/* Set device address and enable. */
SET_REG(USB_DADDR_REG, (addr & USB_DADDR_ADDR) | USB_DADDR_ENABLE);
}
/** Set the receive buffer size for a given USB endpoint
* @param ep Index of Endpoint to configure
* @param addr Size in bytes of RX buffer
*/
static void usb_set_ep_rx_bufsize(u8 ep, u32 size)
{
if(size > 62) {
if(size & 0x1f) size -= 32;
USB_SET_EP_RX_COUNT(ep, (size << 5) | 0x8000);
} else {
if(size & 1) size++;
USB_SET_EP_RX_COUNT(ep, size << 10);
}
}
void usbd_ep_setup(u8 addr, u8 type, u16 max_size, void (*callback)(u8 ep))
{
/* Translate USB standard type codes to stm32 */
const u16 typelookup[] = {
[USB_ENDPOINT_ATTR_CONTROL] = USB_EP_TYPE_CONTROL,
[USB_ENDPOINT_ATTR_ISOCHRONOUS] = USB_EP_TYPE_ISO,
[USB_ENDPOINT_ATTR_BULK] = USB_EP_TYPE_BULK,
[USB_ENDPOINT_ATTR_INTERRUPT] = USB_EP_TYPE_INTERRUPT,
};
u8 dir = addr & 0x80;
addr &= 0x7f;
/* Assign address */
USB_SET_EP_ADDR(addr, addr);
USB_SET_EP_TYPE(addr, typelookup[type]);
if(dir || (addr == 0)) {
USB_SET_EP_TX_ADDR(addr, _usbd_device.pm_top);
if(callback)
_usbd_device.user_callback_ctr[addr][USB_TRANSACTION_IN] = (void*)callback;
USB_SET_EP_TX_STAT(addr, USB_EP_TX_STAT_NAK);
_usbd_device.pm_top += max_size;
}
if(!dir) {
USB_SET_EP_RX_ADDR(addr, _usbd_device.pm_top);
usb_set_ep_rx_bufsize(addr, max_size);
if(callback)
_usbd_device.user_callback_ctr[addr][USB_TRANSACTION_OUT] = (void*)callback;
USB_SET_EP_RX_STAT(addr, USB_EP_RX_STAT_VALID);
_usbd_device.pm_top += max_size;
}
}
void _usbd_hw_endpoints_reset(void)
{
int i;
/* Reset all endpoints */
for(i = 1; i < 8; i++) {
USB_SET_EP_TX_STAT(i, USB_EP_TX_STAT_DISABLED);
USB_SET_EP_RX_STAT(i, USB_EP_RX_STAT_DISABLED);
}
_usbd_device.pm_top = 0x40 + (2*_usbd_device.desc->bMaxPacketSize0);
}
void usbd_ep_stall(u8 addr)
{
if(addr == 0)
USB_SET_EP_TX_STAT(addr, USB_EP_TX_STAT_STALL);
if(addr & 0x80)
USB_SET_EP_TX_STAT(addr, USB_EP_TX_STAT_STALL);
else
USB_SET_EP_RX_STAT(addr & 0x7F, USB_EP_RX_STAT_STALL);
}
/** Copy a data buffer to Packet Memory.
* @param PM Destination pointer into packet memory.
* @param buf Source pointer to data buffer.
* @param len Number of bytes to copy.
*/
static inline void
usb_copy_to_pm(volatile void *vPM, const void *buf, uint16_t len)
{
const uint16_t *lbuf = buf;
volatile uint16_t *PM = vPM;
for(len = (len + 1) >> 1; len; PM += 2, lbuf++, len--)
*PM = *lbuf;
}
u16 usbd_ep_write_packet(u8 addr, const void *buf, u16 len)
{
addr &= 0x7F;
if((*USB_EP_REG(addr) & USB_EP_TX_STAT) == USB_EP_TX_STAT_VALID)
return 0;
usb_copy_to_pm(USB_GET_EP_TX_BUFF(addr), buf, len);
USB_SET_EP_TX_COUNT(addr, len);
USB_SET_EP_TX_STAT(addr, USB_EP_TX_STAT_VALID);
return len;
}
/** Copy a data buffer from Packet Memory.
* @param buf Source pointer to data buffer.
* @param PM Destination pointer into packet memory.
* @param len Number of bytes to copy.
*/
static inline void
usb_copy_from_pm(void *buf, const volatile void *vPM, uint16_t len)
{
uint16_t *lbuf = buf;
const volatile uint16_t *PM = vPM;
uint8_t odd = len & 1;
for(len >>= 1; len; PM += 2, lbuf++, len--)
*lbuf = *PM;
if(odd) *(uint8_t*)lbuf = *(uint8_t*)PM;
}
u16 usbd_ep_read_packet(u8 addr, void *buf, u16 len)
{
if((*USB_EP_REG(addr) & USB_EP_RX_STAT) == USB_EP_RX_STAT_VALID)
return 0;
len = MIN(USB_GET_EP_RX_COUNT(addr) & 0x3ff, len);
usb_copy_from_pm(buf, USB_GET_EP_RX_BUFF(addr), len);
USB_CLR_EP_RX_CTR(addr);
USB_SET_EP_RX_STAT(addr, USB_EP_RX_STAT_VALID);
return len;
}
void usbd_poll(void)
{
u16 istr = *USB_ISTR_REG;
if(istr & USB_ISTR_RESET) {
_usbd_device.pm_top = 0x40;
_usbd_reset();
USB_CLR_ISTR_RESET();
return;
}
if(istr & USB_ISTR_CTR) {
u8 ep = istr & USB_ISTR_EP_ID;
u8 type = (istr & USB_ISTR_DIR) ? 1 : 0;
if(type) { /* OUT or SETUP transaction */
type += (*USB_EP_REG(ep) & USB_EP_SETUP) ? 1 : 0;
} else { /* IN transaction */
USB_CLR_EP_TX_CTR(ep);
}
if(_usbd_device.user_callback_ctr[ep][type])
_usbd_device.user_callback_ctr[ep][type](ep);
}
if(istr & USB_ISTR_SUSP) {
USB_CLR_ISTR_SUSP();
if(_usbd_device.user_callback_suspend)
_usbd_device.user_callback_suspend();
}
if(istr & USB_ISTR_WKUP) {
USB_CLR_ISTR_WKUP();
if(_usbd_device.user_callback_resume)
_usbd_device.user_callback_resume();
}
if(istr & USB_ISTR_SOF)
USB_CLR_ISTR_SOF();
}

84
lib/usb/usb_private.h Normal file
View File

@ -0,0 +1,84 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __USB_PRIVATE_H
#define __USB_PRIVATE_H
#define MIN(a, b) ((a)<(b) ? (a) : (b))
/** Internal collection of device information. */
extern struct _usbd_device {
const struct usb_device_descriptor *desc;
const struct usb_config_descriptor *config;
const char **strings;
uint8_t *ctrl_buf; /**< Internal buffer used for control transfers */
uint16_t ctrl_buf_len;
uint8_t current_address;
uint8_t current_config;
uint16_t pm_top; /**< Top of allocated endpoint buffer memory */
/* User callback functions for various USB events */
void (*user_callback_reset)(void);
void (*user_callback_suspend)(void);
void (*user_callback_resume)(void);
int (*user_callback_control_command)(struct usb_setup_data *req,
void (**complete)(struct usb_setup_data *req));
int (*user_callback_control_read)(struct usb_setup_data *req,
uint8_t **buf, uint16_t *len,
void (**complete)(struct usb_setup_data *req));
int (*user_callback_control_write)(struct usb_setup_data *req,
uint8_t *buf, uint16_t len,
void (**complete)(struct usb_setup_data *req));
void (*user_callback_ctr[8][3])(uint8_t ea);
/* User callback function for some standard USB function hooks */
void (*user_callback_set_config)(uint16_t wValue);
} _usbd_device;
enum _usbd_transaction {
USB_TRANSACTION_IN,
USB_TRANSACTION_OUT,
USB_TRANSACTION_SETUP,
};
void _usbd_control_in(uint8_t ea);
void _usbd_control_out(uint8_t ea);
void _usbd_control_setup(uint8_t ea);
int _usbd_standard_request_command(struct usb_setup_data *req);
int _usbd_standard_request_read(struct usb_setup_data *req,
uint8_t **buf, uint16_t *len);
int _usbd_standard_request_write(struct usb_setup_data *req,
uint8_t *buf, uint16_t len);
void _usbd_reset(void);
/* Functions provided by the hardware abstraction */
void _usbd_hw_init(void);
void _usbd_hw_set_address(uint8_t addr);
void _usbd_hw_endpoints_reset(void);
#endif

268
lib/usb/usb_standard.c Normal file
View File

@ -0,0 +1,268 @@
/*
* This file is part of the libopenstm32 project.
*
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <usbd.h>
#include "usb_private.h"
void usbd_register_set_config_callback(void (*callback)(uint16_t wValue))
{
_usbd_device.user_callback_set_config = callback;
}
static uint16_t
build_config_descriptor(uint8_t index, uint8_t *buf, uint16_t len)
{
uint8_t *tmpbuf = buf;
const struct usb_config_descriptor *cfg = &_usbd_device.config[index];
uint16_t count, total = 0, totallen = 0;
uint16_t i, j, k;
memcpy(buf, cfg, count = MIN(len, cfg->bLength));
buf += count; len -= count; total += count; totallen += cfg->bLength;
/* For each interface... */
for(i = 0; i < cfg->bNumInterfaces; i++) {
/* For each alternate setting... */
for(j = 0; j < cfg->interface[i].num_altsetting; j++) {
const struct usb_interface_descriptor *iface =
&cfg->interface[i].altsetting[j];
/* Copy interface descriptor */
memcpy(buf, iface, count = MIN(len, iface->bLength));
buf += count; len -= count;
total += count; totallen += iface->bLength;
/* Copy extra bytes (function descriptors) */
memcpy(buf, iface->extra,
count = MIN(len, iface->extralen));
buf += count; len -= count;
total += count; totallen += iface->extralen;
/* For each endpoint... */
for(k = 0; k < iface->bNumEndpoints; k++) {
const struct usb_endpoint_descriptor *ep =
&iface->endpoint[k];
memcpy(buf, ep, count = MIN(len, ep->bLength));
buf += count; len -= count;
total += count; totallen += ep->bLength;
}
}
}
/* Fill in wTotalLength */
*(uint16_t*)(tmpbuf+2) = totallen;
return total;
}
static int usb_standard_get_descriptor(struct usb_setup_data *req,
uint8_t **buf, uint16_t *len)
{
int i;
switch(req->wValue >> 8) {
case USB_DT_DEVICE:
*buf = (uint8_t *)_usbd_device.desc;
*len = MIN(*len, _usbd_device.desc->bLength);
return 1;
case USB_DT_CONFIGURATION: {
*buf = _usbd_device.ctrl_buf;
*len = build_config_descriptor(req->wValue & 0xff, *buf, *len);
return 1;
}
case USB_DT_STRING: {
struct usb_string_descriptor *sd =
(struct usb_string_descriptor *)_usbd_device.ctrl_buf;
if(!_usbd_device.strings)
return 0; /* Device doesn't support strings */
sd->bLength = strlen(_usbd_device.strings[req->wValue & 0xff])
* 2 + 2;
sd->bDescriptorType = USB_DT_STRING;
*buf = (uint8_t *)sd;
*len = MIN(*len, sd->bLength);
for(i = 0; i < (*len / 2) - 1; i++)
sd->wData[i] =
_usbd_device.strings[req->wValue & 0xff][i];
/* Send sane Language ID descriptor... */
if((req->wValue & 0xff) == 0)
sd->wData[0] = 0x409;
return 1;
}
}
return 0;
}
static int usb_standard_set_address(struct usb_setup_data *req)
{
/* The actual address is only latched at the STATUS IN stage */
if((req->bmRequestType != 0) || (req->wValue >= 128)) return 0;
_usbd_device.current_address = req->wValue;
return 1;
}
static int usb_standard_set_configuration(struct usb_setup_data *req)
{
/* Is this correct, or should we reset alternate settings */
if(req->wValue == _usbd_device.current_config) return 1;
_usbd_device.current_config = req->wValue;
/* Reset all endpoints */
_usbd_hw_endpoints_reset();
if(_usbd_device.user_callback_set_config)
_usbd_device.user_callback_set_config(req->wValue);
return 1;
}
static int usb_standard_get_configuration(struct usb_setup_data *req,
uint8_t **buf, uint16_t *len)
{
(void)req;
if(*len > 1) *len = 1;
(*buf)[0] = _usbd_device.current_config;
return 1;
}
static int usb_standard_set_interface(struct usb_setup_data *req)
{
(void)req;
/* FIXME: Do something meaningful here: call app */
return 1;
}
static int usb_standard_get_status(struct usb_setup_data *req, uint8_t **buf,
uint16_t *len)
{
(void)req;
/* FIXME: Return some meaningful status */
if(*len > 2) *len = 2;
(*buf)[0] = 0;
(*buf)[1] = 0;
return 1;
}
int _usbd_standard_request_command(struct usb_setup_data *req)
{
int (*command)(struct usb_setup_data *req) = NULL;
if((req->bmRequestType & 0x60) != USB_REQ_TYPE_STANDARD)
return 0;
switch(req->bRequest) {
case USB_REQ_CLEAR_FEATURE:
/* FIXME: Implement CLEAR_FEATURE */
/* TODO: Check what standard features are.
* Maybe this is the application's responsibility. */
break;
case USB_REQ_SET_ADDRESS:
/* SET ADDRESS is an exception.
* It is only processed at STATUS stage */
command = usb_standard_set_address;
break;
case USB_REQ_SET_CONFIGURATION:
command = usb_standard_set_configuration;
break;
case USB_REQ_SET_FEATURE:
/* FIXME: Implement SET_FEATURE */
/* TODO: Check what standard features are.
* Maybe this is the application's responsibility. */
break;
case USB_REQ_SET_INTERFACE:
command = usb_standard_set_interface;
break;
}
if(!command) return 0;
return command(req);
}
int _usbd_standard_request_read(struct usb_setup_data *req, uint8_t **buf,
uint16_t *len)
{
int (*command)(struct usb_setup_data *req, uint8_t **buf,
uint16_t *len) = NULL;
/* Handle standard requests */
if((req->bmRequestType & 0x60) != USB_REQ_TYPE_STANDARD)
return 0;
switch(req->bRequest) {
case USB_REQ_GET_CONFIGURATION:
command = usb_standard_get_configuration;
break;
case USB_REQ_GET_DESCRIPTOR:
command = usb_standard_get_descriptor;
break;
case USB_REQ_GET_INTERFACE:
/* FIXME: Implement GET_INTERFACE */
break;
case USB_REQ_GET_STATUS:
/* GET_STATUS always responds with zero reply.
* The application may override this behaviour. */
command = usb_standard_get_status;
break;
}
if(!command) return 0;
return command(req, buf, len);
}
int _usbd_standard_request_write(struct usb_setup_data *req, uint8_t *buf,
uint16_t len)
{
int (*command)(struct usb_setup_data *req, uint8_t *buf, uint16_t len)
= NULL;
/* Handle standard requests */
if((req->bmRequestType & 0x60) != USB_REQ_TYPE_STANDARD)
return 0;
switch(req->bRequest) {
case USB_REQ_SET_DESCRIPTOR:
/* SET_DESCRIPTOR is optional and not implemented. */
break;
case USB_REQ_SET_SYNCH_FRAME:
/* FIXME: SYNCH_FRAME is not implemented. */
/* SYNCH_FRAME is used for synchronization of isochronous
* endpoints which are not yet implemented. */
break;
}
if(!command) return 0;
return command(req, buf, len);
}