From 74405de4a5a1beab4023731de41c9f6e1c7f69a4 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 6 Nov 2012 11:03:12 -0800 Subject: [PATCH 1/5] Enable OTG clock during initialization Add a call to enable USB clock, otherwise the dive will not enumerate. --- examples/stm32/f1/other/usb_dfu/usbdfu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/stm32/f1/other/usb_dfu/usbdfu.c b/examples/stm32/f1/other/usb_dfu/usbdfu.c index 0211a47e..f30783f3 100644 --- a/examples/stm32/f1/other/usb_dfu/usbdfu.c +++ b/examples/stm32/f1/other/usb_dfu/usbdfu.c @@ -244,6 +244,8 @@ int main(void) AFIO_MAPR |= AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON; gpio_set_mode(GPIOA, GPIO_MODE_INPUT, 0, GPIO15); + rcc_peripheral_enable_clock(&RCC_AHBENR, RCC_AHBENR_OTGFSEN); + usbd_init(&stm32f107_usb_driver, &dev, &config, usb_strings); usbd_set_control_buffer_size(sizeof(usbd_control_buffer)); usbd_register_control_callback( From 7a5da60e2669c57d7b615aabe16ab851606f8bf1 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 6 Nov 2012 16:46:55 -0800 Subject: [PATCH 2/5] Change USB strings handling code This commit add an extra field to the _usbd_device, that allows to keep track of the number of USB strings which allows simplify boundaries checking code in usb_standard_get_descriptor. This commit also changes the index base for strings in usb_standard_get_descriptor which allows to get rid of necessity to have a dummy one-character string in a strings array. --- include/libopencm3/usb/usbd.h | 2 +- lib/usb/usb.c | 4 +++- lib/usb/usb_private.h | 1 + lib/usb/usb_standard.c | 22 +++++++++++----------- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/include/libopencm3/usb/usbd.h b/include/libopencm3/usb/usbd.h index e4b35785..9da83796 100644 --- a/include/libopencm3/usb/usbd.h +++ b/include/libopencm3/usb/usbd.h @@ -38,7 +38,7 @@ extern u8 usbd_control_buffer[]; extern int usbd_init(const usbd_driver *driver, const struct usb_device_descriptor *dev, const struct usb_config_descriptor *conf, - const char **strings); + const char **strings, int num_strings); extern void usbd_set_control_buffer_size(u16 size); extern void usbd_register_reset_callback(void (*callback)(void)); diff --git a/lib/usb/usb.c b/lib/usb/usb.c index 1ebb6ec5..d5ec9800 100644 --- a/lib/usb/usb.c +++ b/lib/usb/usb.c @@ -45,12 +45,14 @@ u8 usbd_control_buffer[128] __attribute__((weak)); */ int usbd_init(const usbd_driver *driver, const struct usb_device_descriptor *dev, - const struct usb_config_descriptor *conf, const char **strings) + const struct usb_config_descriptor *conf, + const char **strings, int num_strings) { _usbd_device.driver = driver; _usbd_device.desc = dev; _usbd_device.config = conf; _usbd_device.strings = strings; + _usbd_device.num_strings = num_strings; _usbd_device.ctrl_buf = usbd_control_buffer; _usbd_device.ctrl_buf_len = sizeof(usbd_control_buffer); diff --git a/lib/usb/usb_private.h b/lib/usb/usb_private.h index a1e5e4c8..238f14f3 100644 --- a/lib/usb/usb_private.h +++ b/lib/usb/usb_private.h @@ -29,6 +29,7 @@ extern struct _usbd_device { const struct usb_device_descriptor *desc; const struct usb_config_descriptor *config; const char **strings; + int num_strings; u8 *ctrl_buf; /**< Internal buffer used for control transfers */ u16 ctrl_buf_len; diff --git a/lib/usb/usb_standard.c b/lib/usb/usb_standard.c index 2d7c619e..1c8b952e 100644 --- a/lib/usb/usb_standard.c +++ b/lib/usb/usb_standard.c @@ -90,7 +90,7 @@ static u16 build_config_descriptor(u8 index, u8 *buf, u16 len) static int usb_standard_get_descriptor(struct usb_setup_data *req, u8 **buf, u16 *len) { - int i; + int i, index; struct usb_string_descriptor *sd; switch (req->wValue >> 8) { @@ -105,16 +105,20 @@ static int usb_standard_get_descriptor(struct usb_setup_data *req, case USB_DT_STRING: sd = (struct usb_string_descriptor *)_usbd_device.ctrl_buf; + /* Send sane Language ID descriptor... */ + if ((req->wValue & 0xff) == 0) + sd->wData[0] = 0x409; + + index = (req->wValue & 0xff) - 1; + if (!_usbd_device.strings) return 0; /* Device doesn't support strings. */ /* Check that string index is in range. */ - for (i = 0; i <= (req->wValue & 0xff); i++) - if (_usbd_device.strings[i] == NULL) - return 0; + if (index >= _usbd_device.num_strings) + return 0; - sd->bLength = strlen(_usbd_device.strings[req->wValue & 0xff]) - * 2 + 2; + sd->bLength = strlen(_usbd_device.strings[index]) * 2 + 2; sd->bDescriptorType = USB_DT_STRING; *buf = (u8 *)sd; @@ -122,11 +126,7 @@ static int usb_standard_get_descriptor(struct usb_setup_data *req, 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; + _usbd_device.strings[index][i]; return 1; } From 12e178686331fd0a8e3564a9f0e77fece4a04617 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 6 Nov 2012 16:48:40 -0800 Subject: [PATCH 3/5] Add a desig_get_unique_id_as_string This commit adds desig_get_unique_id_as_string which is useful if one wants to use device ID as USB serial number(iSerialNumber), for example. --- include/libopencm3/stm32/f1/desig.h | 9 +++++++++ lib/stm32/f1/desig.c | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/include/libopencm3/stm32/f1/desig.h b/include/libopencm3/stm32/f1/desig.h index 74cfb353..6ceb6658 100644 --- a/include/libopencm3/stm32/f1/desig.h +++ b/include/libopencm3/stm32/f1/desig.h @@ -51,6 +51,15 @@ u16 desig_get_flash_size(void); */ void desig_get_unique_id(u32 result[]); +/** + * Read the full 96 bit unique identifier and return it as a + * zero-terminated string + * @param string memory region to write the result to + 8 @param string_len the size of string in bytes + */ +void desig_get_unique_id_as_string(char *string, + unsigned int string_len); + END_DECLS #endif diff --git a/lib/stm32/f1/desig.c b/lib/stm32/f1/desig.c index 7ae968e1..7f213fab 100644 --- a/lib/stm32/f1/desig.c +++ b/lib/stm32/f1/desig.c @@ -35,3 +35,25 @@ void desig_get_unique_id(u32 result[]) result[1] = bits63_32; result[2] = bits31_16 << 16 | bits15_0; } + +void desig_get_unique_id_as_string(char *string, + unsigned int string_len) +{ + int i, len; + u8 device_id[12]; + static const char chars[] = "0123456789ABCDEF"; + + desig_get_unique_id((u32 *)device_id); + + /* Each byte produces two characters */ + len = (2 * sizeof(device_id) < string_len) ? + 2 * sizeof(device_id) : string_len - 1; + + for (i = 0; i < len; i += 2) { + string[i] = chars[(device_id[i / 2] >> 0) & 0x0F]; + string[i + 1] = chars[(device_id[i / 2] >> 4) & 0x0F]; + } + + string[len] = '\0'; +} + From c5c4db01969608ac19bba290b9f6ec965ef01e5c Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 7 Nov 2012 10:33:51 -0800 Subject: [PATCH 4/5] Extend control hook framework This commits adds a new error code that can be return from a registered control callback: USBD_REQ_NEXT_CALLBACK. This return code signifies that the callback is done processing the data successfully, but user would like to have all matching callbacks down the callback chain to be executed too. This change allows for example to intercept standard requests like GET_DESCRIPTOR, do some small action upon receiving of one, but still have the standard callback executed and do it's job. This way user doesn't have to re-implement standard GET_DESCRIPTOR functionality if they want to intercept that request to do some small thing. --- include/libopencm3/usb/usbd.h | 7 +++++++ lib/usb/usb_control.c | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/libopencm3/usb/usbd.h b/include/libopencm3/usb/usbd.h index 9da83796..8f685557 100644 --- a/include/libopencm3/usb/usbd.h +++ b/include/libopencm3/usb/usbd.h @@ -24,6 +24,13 @@ BEGIN_DECLS + +enum usbd_request_return_codes { + USBD_REQ_NOTSUPP = 0, + USBD_REQ_HANDLED = 1, + USBD_REQ_NEXT_CALLBACK = 2, +}; + typedef struct _usbd_driver usbd_driver; extern const usbd_driver stm32f103_usb_driver; extern const usbd_driver stm32f107_usb_driver; diff --git a/lib/usb/usb_control.c b/lib/usb/usb_control.c index 3dd08578..b4ac57e9 100644 --- a/lib/usb/usb_control.c +++ b/lib/usb/usb_control.c @@ -102,7 +102,8 @@ static int usb_control_request_dispatch(struct usb_setup_data *req) result = cb[i].cb(req, &control_state.ctrl_buf, &control_state.ctrl_len, &control_state.complete); - if (result) + if (result == USBD_REQ_HANDLED || + result == USBD_REQ_NOTSUPP) return result; } } From 720e85f850064ad6ab3e79101b972e43d4e31ef6 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 7 Nov 2012 11:30:44 -0800 Subject: [PATCH 5/5] Further re-factor USB string handling code This commit refactors USB string code, making it, hopefully, less buggy and more easier to understand. It also removes "magic" bit manipulation and "magic" numbers; --- include/libopencm3/usb/usbstd.h | 3 ++ lib/usb/usb_standard.c | 70 ++++++++++++++++++++++----------- 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/include/libopencm3/usb/usbstd.h b/include/libopencm3/usb/usbstd.h index 8610fdbc..01fc7e3c 100644 --- a/include/libopencm3/usb/usbstd.h +++ b/include/libopencm3/usb/usbstd.h @@ -220,4 +220,7 @@ struct usb_iface_assoc_descriptor { #define USB_DT_INTERFACE_ASSOCIATION_SIZE \ sizeof(struct usb_iface_assoc_descriptor) +enum usb_language_id { + USB_LANGID_ENGLISH_US = 0x409, +}; #endif diff --git a/lib/usb/usb_standard.c b/lib/usb/usb_standard.c index 1c8b952e..08923d85 100644 --- a/lib/usb/usb_standard.c +++ b/lib/usb/usb_standard.c @@ -87,50 +87,74 @@ static u16 build_config_descriptor(u8 index, u8 *buf, u16 len) return total; } +static int usb_descriptor_type(u16 wValue) +{ + return wValue >> 8; +} + +static int usb_descriptor_index(u16 wValue) +{ + return wValue & 0xFF; +} + static int usb_standard_get_descriptor(struct usb_setup_data *req, u8 **buf, u16 *len) { - int i, index; + int i, array_idx, descr_idx; struct usb_string_descriptor *sd; - switch (req->wValue >> 8) { + descr_idx = usb_descriptor_index(req->wValue); + + switch (usb_descriptor_type(req->wValue)) { case USB_DT_DEVICE: *buf = (u8 *) _usbd_device.desc; *len = MIN(*len, _usbd_device.desc->bLength); - return 1; + return USBD_REQ_HANDLED; case USB_DT_CONFIGURATION: *buf = _usbd_device.ctrl_buf; - *len = build_config_descriptor(req->wValue & 0xff, *buf, *len); - return 1; + *len = build_config_descriptor(descr_idx, *buf, *len); + return USBD_REQ_HANDLED; case USB_DT_STRING: sd = (struct usb_string_descriptor *)_usbd_device.ctrl_buf; - /* Send sane Language ID descriptor... */ - if ((req->wValue & 0xff) == 0) - sd->wData[0] = 0x409; + if (descr_idx == 0) { + /* Send sane Language ID descriptor... */ + sd->wData[0] = USB_LANGID_ENGLISH_US; + sd->bLength = sizeof(sd->bLength) + sizeof(sd->bDescriptorType) + + sizeof(sd->wData[0]); - index = (req->wValue & 0xff) - 1; + *len = MIN(*len, sd->bLength); + } else { + array_idx = descr_idx - 1; - if (!_usbd_device.strings) - return 0; /* Device doesn't support strings. */ + if (!_usbd_device.strings) + return USBD_REQ_NOTSUPP; /* Device doesn't support strings. */ + /* Check that string index is in range. */ + if (array_idx >= _usbd_device.num_strings) + return USBD_REQ_NOTSUPP; - /* Check that string index is in range. */ - if (index >= _usbd_device.num_strings) - return 0; + /* Strings with Language ID differnet from + * USB_LANGID_ENGLISH_US are not supported */ + if (req->wIndex != USB_LANGID_ENGLISH_US) + return USBD_REQ_NOTSUPP; + + /* Ths string is returned as UTF16, hence the multiplication */ + sd->bLength = strlen(_usbd_device.strings[array_idx]) * 2 + + sizeof(sd->bLength) + sizeof(sd->bDescriptorType); + + *len = MIN(*len, sd->bLength); + + for (i = 0; i < (*len / 2) - 1; i++) + sd->wData[i] = + _usbd_device.strings[array_idx][i]; + } - sd->bLength = strlen(_usbd_device.strings[index]) * 2 + 2; sd->bDescriptorType = USB_DT_STRING; - *buf = (u8 *)sd; - *len = MIN(*len, sd->bLength); - for (i = 0; i < (*len / 2) - 1; i++) - sd->wData[i] = - _usbd_device.strings[index][i]; - - return 1; + return USBD_REQ_HANDLED; } - return 0; + return USBD_REQ_NOTSUPP; } static int usb_standard_set_address(struct usb_setup_data *req, u8 **buf,