diff --git a/class/audio/usbd_audio.c b/class/audio/usbd_audio.c index 0ec73c77..6d0fdeb7 100644 --- a/class/audio/usbd_audio.c +++ b/class/audio/usbd_audio.c @@ -1,136 +1,136 @@ -/** - * @file usbd_audio.c - * @brief - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#include "usbd_core.h" -#include "usbd_audio.h" - -struct usbd_audio_control_info audio_control_info = { 0xdb00, 0x0000, 0x0100, 0xf600, 0 }; - -int audio_class_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) -{ - USB_LOG_DBG("AUDIO Class request: " - "bRequest 0x%02x\r\n", - setup->bRequest); - - switch (setup->bRequest) { - case AUDIO_REQUEST_SET_CUR: - - if (LO_BYTE(setup->wValue) == 0x01) { - if (HI_BYTE(setup->wValue) == AUDIO_FU_CONTROL_MUTE) { - memcpy(&audio_control_info.mute, *data, *len); - } else if (HI_BYTE(setup->wValue) == AUDIO_FU_CONTROL_VOLUME) { - memcpy(&audio_control_info.vol_current, *data, *len); - int vol; - if (audio_control_info.vol_current == 0) { - vol = 100; - } else { - vol = (audio_control_info.vol_current - 0xDB00 + 1) * 100 / (0xFFFF - 0xDB00); - } - usbd_audio_set_volume(vol); - USB_LOG_INFO("current audio volume:%d\r\n", vol); - } - } - - break; - - case AUDIO_REQUEST_GET_CUR: - if (HI_BYTE(setup->wValue) == AUDIO_FU_CONTROL_MUTE) { - *data = (uint8_t *)&audio_control_info.mute; - *len = 1; - } else if (HI_BYTE(setup->wValue) == AUDIO_FU_CONTROL_VOLUME) { - *data = (uint8_t *)&audio_control_info.vol_current; - *len = 2; - } - - break; - - case AUDIO_REQUEST_SET_RES: - break; - - case AUDIO_REQUEST_SET_MEM: - break; - - case AUDIO_REQUEST_GET_MIN: - *data = (uint8_t *)&audio_control_info.vol_min; - *len = 2; - break; - - case AUDIO_REQUEST_GET_MAX: - *data = (uint8_t *)&audio_control_info.vol_max; - *len = 2; - break; - - case AUDIO_REQUEST_GET_RES: - *data = (uint8_t *)&audio_control_info.vol_res; - *len = 2; - break; - case AUDIO_REQUEST_GET_MEM: - *data[0] = 0; - *len = 1; - break; - - default: - USB_LOG_WRN("Unhandled Audio Class bRequest 0x%02x\r\n", setup->bRequest); - return -1; - } - - return 0; -} - -void audio_notify_handler(uint8_t event, void *arg) -{ - switch (event) { - case USBD_EVENT_RESET: - - break; - - case USBD_EVENT_SOF: - break; - - case USBD_EVENT_SET_INTERFACE: - usbd_audio_set_interface_callback(((uint8_t *)arg)[3]); - break; - - default: - break; - } -} - -void usbd_audio_add_interface(usbd_class_t *devclass, usbd_interface_t *intf) -{ - static usbd_class_t *last_class = NULL; - - if (last_class != devclass) { - last_class = devclass; - usbd_class_register(devclass); - } - - intf->class_handler = audio_class_request_handler; - intf->custom_handler = NULL; - intf->vendor_handler = NULL; - intf->notify_handler = audio_notify_handler; - usbd_class_add_interface(devclass, intf); -} - -__WEAK void usbd_audio_set_volume(uint8_t vol) -{ -} +/** + * @file usbd_audio.c + * @brief + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#include "usbd_core.h" +#include "usbd_audio.h" + +struct usbd_audio_control_info audio_control_info = { 0xdb00, 0x0000, 0x0100, 0xf600, 0 }; + +int audio_class_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) +{ + USB_LOG_DBG("AUDIO Class request: " + "bRequest 0x%02x\r\n", + setup->bRequest); + + switch (setup->bRequest) { + case AUDIO_REQUEST_SET_CUR: + + if (LO_BYTE(setup->wValue) == 0x01) { + if (HI_BYTE(setup->wValue) == AUDIO_FU_CONTROL_MUTE) { + memcpy(&audio_control_info.mute, *data, *len); + } else if (HI_BYTE(setup->wValue) == AUDIO_FU_CONTROL_VOLUME) { + memcpy(&audio_control_info.vol_current, *data, *len); + int vol; + if (audio_control_info.vol_current == 0) { + vol = 100; + } else { + vol = (audio_control_info.vol_current - 0xDB00 + 1) * 100 / (0xFFFF - 0xDB00); + } + usbd_audio_set_volume(vol); + USB_LOG_INFO("current audio volume:%d\r\n", vol); + } + } + + break; + + case AUDIO_REQUEST_GET_CUR: + if (HI_BYTE(setup->wValue) == AUDIO_FU_CONTROL_MUTE) { + *data = (uint8_t *)&audio_control_info.mute; + *len = 1; + } else if (HI_BYTE(setup->wValue) == AUDIO_FU_CONTROL_VOLUME) { + *data = (uint8_t *)&audio_control_info.vol_current; + *len = 2; + } + + break; + + case AUDIO_REQUEST_SET_RES: + break; + + case AUDIO_REQUEST_SET_MEM: + break; + + case AUDIO_REQUEST_GET_MIN: + *data = (uint8_t *)&audio_control_info.vol_min; + *len = 2; + break; + + case AUDIO_REQUEST_GET_MAX: + *data = (uint8_t *)&audio_control_info.vol_max; + *len = 2; + break; + + case AUDIO_REQUEST_GET_RES: + *data = (uint8_t *)&audio_control_info.vol_res; + *len = 2; + break; + case AUDIO_REQUEST_GET_MEM: + *data[0] = 0; + *len = 1; + break; + + default: + USB_LOG_WRN("Unhandled Audio Class bRequest 0x%02x\r\n", setup->bRequest); + return -1; + } + + return 0; +} + +void audio_notify_handler(uint8_t event, void *arg) +{ + switch (event) { + case USBD_EVENT_RESET: + + break; + + case USBD_EVENT_SOF: + break; + + case USBD_EVENT_SET_INTERFACE: + usbd_audio_set_interface_callback(((uint8_t *)arg)[3]); + break; + + default: + break; + } +} + +void usbd_audio_add_interface(usbd_class_t *devclass, usbd_interface_t *intf) +{ + static usbd_class_t *last_class = NULL; + + if (last_class != devclass) { + last_class = devclass; + usbd_class_register(devclass); + } + + intf->class_handler = audio_class_request_handler; + intf->custom_handler = NULL; + intf->vendor_handler = NULL; + intf->notify_handler = audio_notify_handler; + usbd_class_add_interface(devclass, intf); +} + +__WEAK void usbd_audio_set_volume(uint8_t vol) +{ +} diff --git a/class/cdc/usbd_cdc.c b/class/cdc/usbd_cdc.c index a47a35d0..008ca671 100644 --- a/class/cdc/usbd_cdc.c +++ b/class/cdc/usbd_cdc.c @@ -1,171 +1,171 @@ -/** - * @file usbd_cdc.c - * @brief - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#include "usbd_core.h" -#include "usbd_cdc.h" - -const char *stop_name[] = { "1", "1.5", "2" }; -const char *parity_name[] = { "N", "O", "E", "M", "S" }; - -/* Device data structure */ -struct cdc_acm_cfg_private { - /* CDC ACM line coding properties. LE order */ - struct cdc_line_coding line_coding; - /* CDC ACM line state bitmap, DTE side */ - uint8_t line_state; - /* CDC ACM serial state bitmap, DCE side */ - uint8_t serial_state; - /* CDC ACM notification sent status */ - uint8_t notification_sent; - /* CDC ACM configured flag */ - bool configured; - /* CDC ACM suspended flag */ - bool suspended; - uint32_t uart_first_init_flag; - -} usbd_cdc_acm_cfg; - -static void usbd_cdc_acm_reset(void) -{ - usbd_cdc_acm_cfg.line_coding.dwDTERate = 2000000; - usbd_cdc_acm_cfg.line_coding.bDataBits = 8; - usbd_cdc_acm_cfg.line_coding.bParityType = 0; - usbd_cdc_acm_cfg.line_coding.bCharFormat = 0; - usbd_cdc_acm_cfg.configured = false; - usbd_cdc_acm_cfg.uart_first_init_flag = 0; -} - -/** - * @brief Handler called for Class requests not handled by the USB stack. - * - * @param setup Information about the request to execute. - * @param len Size of the buffer. - * @param data Buffer containing the request result. - * - * @return 0 on success, negative errno code on fail. - */ -static int cdc_acm_class_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) -{ - USB_LOG_DBG("CDC Class request: " - "bRequest 0x%02x\r\n", - setup->bRequest); - - switch (setup->bRequest) { - case CDC_REQUEST_SET_LINE_CODING: - - /*******************************************************************************/ - /* Line Coding Structure */ - /*-----------------------------------------------------------------------------*/ - /* Offset | Field | Size | Value | Description */ - /* 0 | dwDTERate | 4 | Number |Data terminal rate, in bits per second*/ - /* 4 | bCharFormat | 1 | Number | Stop bits */ - /* 0 - 1 Stop bit */ - /* 1 - 1.5 Stop bits */ - /* 2 - 2 Stop bits */ - /* 5 | bParityType | 1 | Number | Parity */ - /* 0 - None */ - /* 1 - Odd */ - /* 2 - Even */ - /* 3 - Mark */ - /* 4 - Space */ - /* 6 | bDataBits | 1 | Number Data bits (5, 6, 7, 8 or 16). */ - /*******************************************************************************/ - if (usbd_cdc_acm_cfg.uart_first_init_flag == 0) { - usbd_cdc_acm_cfg.uart_first_init_flag = 1; - return 0; - } - - memcpy(&usbd_cdc_acm_cfg.line_coding, *data, sizeof(usbd_cdc_acm_cfg.line_coding)); - USB_LOG_DBG("CDC_SET_LINE_CODING <%d %d %s %s>\r\n", - usbd_cdc_acm_cfg.line_coding.dwDTERate, - usbd_cdc_acm_cfg.line_coding.bDataBits, - parity_name[usbd_cdc_acm_cfg.line_coding.bParityType], - stop_name[usbd_cdc_acm_cfg.line_coding.bCharFormat]); - usbd_cdc_acm_set_line_coding(usbd_cdc_acm_cfg.line_coding.dwDTERate, usbd_cdc_acm_cfg.line_coding.bDataBits, - usbd_cdc_acm_cfg.line_coding.bParityType, usbd_cdc_acm_cfg.line_coding.bCharFormat); - break; - - case CDC_REQUEST_SET_CONTROL_LINE_STATE: - usbd_cdc_acm_cfg.line_state = (uint8_t)setup->wValue; - bool dtr = (setup->wValue & 0x01); - bool rts = (setup->wValue & 0x02); - USB_LOG_DBG("DTR 0x%x,RTS 0x%x\r\n", - dtr, rts); - usbd_cdc_acm_set_dtr(dtr); - usbd_cdc_acm_set_rts(rts); - break; - - case CDC_REQUEST_GET_LINE_CODING: - *data = (uint8_t *)(&usbd_cdc_acm_cfg.line_coding); - *len = sizeof(usbd_cdc_acm_cfg.line_coding); - USB_LOG_DBG("CDC_GET_LINE_CODING %d %d %d %d\r\n", - usbd_cdc_acm_cfg.line_coding.dwDTERate, - usbd_cdc_acm_cfg.line_coding.bCharFormat, - usbd_cdc_acm_cfg.line_coding.bParityType, - usbd_cdc_acm_cfg.line_coding.bDataBits); - break; - - default: - USB_LOG_WRN("Unhandled CDC Class bRequest 0x%02x\r\n", setup->bRequest); - return -1; - } - - return 0; -} - -static void cdc_notify_handler(uint8_t event, void *arg) -{ - switch (event) { - case USBD_EVENT_RESET: - usbd_cdc_acm_reset(); - break; - - default: - break; - } -} - -__WEAK void usbd_cdc_acm_set_line_coding(uint32_t baudrate, uint8_t databits, uint8_t parity, uint8_t stopbits) -{ -} -__WEAK void usbd_cdc_acm_set_dtr(bool dtr) -{ -} -__WEAK void usbd_cdc_acm_set_rts(bool rts) -{ -} - -void usbd_cdc_add_acm_interface(usbd_class_t *devclass, usbd_interface_t *intf) -{ - static usbd_class_t *last_class = NULL; - - if (last_class != devclass) { - last_class = devclass; - usbd_class_register(devclass); - } - - intf->class_handler = cdc_acm_class_request_handler; - intf->custom_handler = NULL; - intf->vendor_handler = NULL; - intf->notify_handler = cdc_notify_handler; - usbd_class_add_interface(devclass, intf); -} +/** + * @file usbd_cdc.c + * @brief + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#include "usbd_core.h" +#include "usbd_cdc.h" + +const char *stop_name[] = { "1", "1.5", "2" }; +const char *parity_name[] = { "N", "O", "E", "M", "S" }; + +/* Device data structure */ +struct cdc_acm_cfg_private { + /* CDC ACM line coding properties. LE order */ + struct cdc_line_coding line_coding; + /* CDC ACM line state bitmap, DTE side */ + uint8_t line_state; + /* CDC ACM serial state bitmap, DCE side */ + uint8_t serial_state; + /* CDC ACM notification sent status */ + uint8_t notification_sent; + /* CDC ACM configured flag */ + bool configured; + /* CDC ACM suspended flag */ + bool suspended; + uint32_t uart_first_init_flag; + +} usbd_cdc_acm_cfg; + +static void usbd_cdc_acm_reset(void) +{ + usbd_cdc_acm_cfg.line_coding.dwDTERate = 2000000; + usbd_cdc_acm_cfg.line_coding.bDataBits = 8; + usbd_cdc_acm_cfg.line_coding.bParityType = 0; + usbd_cdc_acm_cfg.line_coding.bCharFormat = 0; + usbd_cdc_acm_cfg.configured = false; + usbd_cdc_acm_cfg.uart_first_init_flag = 0; +} + +/** + * @brief Handler called for Class requests not handled by the USB stack. + * + * @param setup Information about the request to execute. + * @param len Size of the buffer. + * @param data Buffer containing the request result. + * + * @return 0 on success, negative errno code on fail. + */ +static int cdc_acm_class_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) +{ + USB_LOG_DBG("CDC Class request: " + "bRequest 0x%02x\r\n", + setup->bRequest); + + switch (setup->bRequest) { + case CDC_REQUEST_SET_LINE_CODING: + + /*******************************************************************************/ + /* Line Coding Structure */ + /*-----------------------------------------------------------------------------*/ + /* Offset | Field | Size | Value | Description */ + /* 0 | dwDTERate | 4 | Number |Data terminal rate, in bits per second*/ + /* 4 | bCharFormat | 1 | Number | Stop bits */ + /* 0 - 1 Stop bit */ + /* 1 - 1.5 Stop bits */ + /* 2 - 2 Stop bits */ + /* 5 | bParityType | 1 | Number | Parity */ + /* 0 - None */ + /* 1 - Odd */ + /* 2 - Even */ + /* 3 - Mark */ + /* 4 - Space */ + /* 6 | bDataBits | 1 | Number Data bits (5, 6, 7, 8 or 16). */ + /*******************************************************************************/ + if (usbd_cdc_acm_cfg.uart_first_init_flag == 0) { + usbd_cdc_acm_cfg.uart_first_init_flag = 1; + return 0; + } + + memcpy(&usbd_cdc_acm_cfg.line_coding, *data, sizeof(usbd_cdc_acm_cfg.line_coding)); + USB_LOG_DBG("CDC_SET_LINE_CODING <%d %d %s %s>\r\n", + usbd_cdc_acm_cfg.line_coding.dwDTERate, + usbd_cdc_acm_cfg.line_coding.bDataBits, + parity_name[usbd_cdc_acm_cfg.line_coding.bParityType], + stop_name[usbd_cdc_acm_cfg.line_coding.bCharFormat]); + usbd_cdc_acm_set_line_coding(usbd_cdc_acm_cfg.line_coding.dwDTERate, usbd_cdc_acm_cfg.line_coding.bDataBits, + usbd_cdc_acm_cfg.line_coding.bParityType, usbd_cdc_acm_cfg.line_coding.bCharFormat); + break; + + case CDC_REQUEST_SET_CONTROL_LINE_STATE: + usbd_cdc_acm_cfg.line_state = (uint8_t)setup->wValue; + bool dtr = (setup->wValue & 0x01); + bool rts = (setup->wValue & 0x02); + USB_LOG_DBG("DTR 0x%x,RTS 0x%x\r\n", + dtr, rts); + usbd_cdc_acm_set_dtr(dtr); + usbd_cdc_acm_set_rts(rts); + break; + + case CDC_REQUEST_GET_LINE_CODING: + *data = (uint8_t *)(&usbd_cdc_acm_cfg.line_coding); + *len = sizeof(usbd_cdc_acm_cfg.line_coding); + USB_LOG_DBG("CDC_GET_LINE_CODING %d %d %d %d\r\n", + usbd_cdc_acm_cfg.line_coding.dwDTERate, + usbd_cdc_acm_cfg.line_coding.bCharFormat, + usbd_cdc_acm_cfg.line_coding.bParityType, + usbd_cdc_acm_cfg.line_coding.bDataBits); + break; + + default: + USB_LOG_WRN("Unhandled CDC Class bRequest 0x%02x\r\n", setup->bRequest); + return -1; + } + + return 0; +} + +static void cdc_notify_handler(uint8_t event, void *arg) +{ + switch (event) { + case USBD_EVENT_RESET: + usbd_cdc_acm_reset(); + break; + + default: + break; + } +} + +__WEAK void usbd_cdc_acm_set_line_coding(uint32_t baudrate, uint8_t databits, uint8_t parity, uint8_t stopbits) +{ +} +__WEAK void usbd_cdc_acm_set_dtr(bool dtr) +{ +} +__WEAK void usbd_cdc_acm_set_rts(bool rts) +{ +} + +void usbd_cdc_add_acm_interface(usbd_class_t *devclass, usbd_interface_t *intf) +{ + static usbd_class_t *last_class = NULL; + + if (last_class != devclass) { + last_class = devclass; + usbd_class_register(devclass); + } + + intf->class_handler = cdc_acm_class_request_handler; + intf->custom_handler = NULL; + intf->vendor_handler = NULL; + intf->notify_handler = cdc_notify_handler; + usbd_class_add_interface(devclass, intf); +} diff --git a/class/cdc/usbh_cdc_acm.c b/class/cdc/usbh_cdc_acm.c index fdc020a3..0b927381 100644 --- a/class/cdc/usbh_cdc_acm.c +++ b/class/cdc/usbh_cdc_acm.c @@ -1,336 +1,336 @@ -/** - * @file usbh_cdc_acm.c - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#include "usbh_core.h" -#include "usbh_cdc_acm.h" - -#define DEV_FORMAT "/dev/ttyACM%d" -#define DEV_NAMELEN 16 - -static uint32_t g_devinuse = 0; - -void usbh_cdc_acm_callback(void *arg, int nbytes); - -/**************************************************************************** - * Name: usbh_cdc_acm_devno_alloc - * - * Description: - * Allocate a unique /dev/ttyACM[n] minor number in the range 0-31. - * - ****************************************************************************/ - -static int usbh_cdc_acm_devno_alloc(struct usbh_cdc_acm *priv) -{ - uint32_t flags; - int devno; - - flags = usb_osal_enter_critical_section(); - for (devno = 0; devno < 32; devno++) { - uint32_t bitno = 1 << devno; - if ((g_devinuse & bitno) == 0) { - g_devinuse |= bitno; - priv->minor = devno; - usb_osal_leave_critical_section(flags); - return 0; - } - } - - usb_osal_leave_critical_section(flags); - return -EMFILE; -} - -/**************************************************************************** - * Name: usbh_cdc_acm_devno_free - * - * Description: - * Free a /dev/ttyACM[n] minor number so that it can be used. - * - ****************************************************************************/ - -static void usbh_cdc_acm_devno_free(struct usbh_cdc_acm *priv) -{ - int devno = priv->minor; - - if (devno >= 0 && devno < 32) { - uint32_t flags = usb_osal_enter_critical_section(); - g_devinuse &= ~(1 << devno); - usb_osal_leave_critical_section(flags); - } -} - -/**************************************************************************** - * Name: usbh_cdc_acm_mkdevname - * - * Description: - * Format a /dev/ttyACM[n] device name given a minor number. - * - ****************************************************************************/ - -static inline void usbh_cdc_acm_mkdevname(struct usbh_cdc_acm *priv, char *devname) -{ - snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->minor); -} - -int usbh_cdc_acm_set_line_coding(struct usbh_hubport *hport, uint8_t intf, struct cdc_line_coding *line_coding) -{ - int ret; - struct usb_setup_packet *setup; - struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)hport->config.intf[intf].priv; - - setup = cdc_acm_class->setup; - - if (cdc_acm_class->ctrl_intf != intf) { - return -1; - } - - setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; - setup->bRequest = CDC_REQUEST_SET_LINE_CODING; - setup->wValue = 0; - setup->wIndex = intf; - setup->wLength = 7; - - ret = usbh_control_transfer(hport->ep0, setup, (uint8_t *)line_coding); - if (ret < 0) { - return ret; - } - memcpy(cdc_acm_class->linecoding, line_coding, sizeof(struct cdc_line_coding)); - return 0; -} - -int usbh_cdc_acm_get_line_coding(struct usbh_hubport *hport, uint8_t intf, struct cdc_line_coding *line_coding) -{ - int ret; - struct usb_setup_packet *setup; - struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)hport->config.intf[intf].priv; - - setup = cdc_acm_class->setup; - - if (cdc_acm_class->ctrl_intf != intf) { - return -1; - } - - setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; - setup->bRequest = CDC_REQUEST_GET_LINE_CODING; - setup->wValue = 0; - setup->wIndex = intf; - setup->wLength = 7; - - ret = usbh_control_transfer(hport->ep0, setup, (uint8_t *)line_coding); - if (ret < 0) { - return ret; - } - memcpy(cdc_acm_class->linecoding, line_coding, sizeof(struct cdc_line_coding)); - return 0; -} - -int usbh_cdc_acm_set_line_state(struct usbh_hubport *hport, uint8_t intf, bool dtr, bool rts) -{ - int ret; - struct usb_setup_packet *setup; - struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)hport->config.intf[intf].priv; - - setup = cdc_acm_class->setup; - - if (cdc_acm_class->ctrl_intf != intf) { - return -1; - } - - setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; - setup->bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE; - setup->wValue = (dtr << 0) | (rts << 1); - setup->wIndex = intf; - setup->wLength = 0; - - ret = usbh_control_transfer(hport->ep0, setup, NULL); - if (ret < 0) { - return ret; - } - - cdc_acm_class->dtr = dtr; - cdc_acm_class->rts = rts; - - return 0; -} -USB_NOCACHE_RAM_SECTION uint8_t cdc_buffer[4096]; - -int usbh_cdc_acm_connect(struct usbh_hubport *hport, uint8_t intf) -{ - struct usbh_endpoint_cfg ep_cfg = { 0 }; - struct usb_endpoint_descriptor *ep_desc; - char devname[DEV_NAMELEN]; - int ret; - - struct usbh_cdc_acm *cdc_acm_class = usb_malloc(sizeof(struct usbh_cdc_acm)); - if (cdc_acm_class == NULL) { - USB_LOG_ERR("Fail to alloc cdc_acm_class\r\n"); - return -ENOMEM; - } - - memset(cdc_acm_class, 0, sizeof(struct usbh_cdc_acm)); - - usbh_cdc_acm_devno_alloc(cdc_acm_class); - usbh_cdc_acm_mkdevname(cdc_acm_class, devname); - - hport->config.intf[intf].priv = cdc_acm_class; - hport->config.intf[intf + 1].priv = cdc_acm_class; - - cdc_acm_class->setup = usb_iomalloc(sizeof(struct usb_setup_packet)); - if (cdc_acm_class->setup == NULL) { - USB_LOG_ERR("Fail to alloc setup\r\n"); - return -ENOMEM; - } - cdc_acm_class->linecoding = usb_iomalloc(sizeof(struct cdc_line_coding)); - if (cdc_acm_class->linecoding == NULL) { - USB_LOG_ERR("Fail to alloc linecoding\r\n"); - return -ENOMEM; - } - cdc_acm_class->ctrl_intf = intf; - cdc_acm_class->data_intf = intf + 1; - - cdc_acm_class->linecoding->dwDTERate = 115200; - cdc_acm_class->linecoding->bDataBits = 8; - cdc_acm_class->linecoding->bParityType = 0; - cdc_acm_class->linecoding->bCharFormat = 0; - ret = usbh_cdc_acm_set_line_coding(hport, intf, cdc_acm_class->linecoding); - if (ret < 0) { - return ret; - } - - ret = usbh_cdc_acm_set_line_state(hport, intf, true, true); - if (ret < 0) { - return ret; - } - -#if 0 - ep_desc = &hport->config.intf[intf].ep[0].ep_desc; - ep_cfg.ep_addr = ep_desc->bEndpointAddress; - ep_cfg.ep_type = ep_desc->bmAttributes & USB_ENDPOINT_TYPE_MASK; - ep_cfg.ep_mps = ep_desc->wMaxPacketSize; - ep_cfg.ep_interval = ep_desc->bInterval; - ep_cfg.hport = hport; - usbh_ep_alloc(&cdc_acm_class->intin, &ep_cfg); - -#endif - for (uint8_t i = 0; i < hport->config.intf[intf + 1].intf_desc.bNumEndpoints; i++) { - ep_desc = &hport->config.intf[intf + 1].ep[i].ep_desc; - - ep_cfg.ep_addr = ep_desc->bEndpointAddress; - ep_cfg.ep_type = ep_desc->bmAttributes & USB_ENDPOINT_TYPE_MASK; - ep_cfg.ep_mps = ep_desc->wMaxPacketSize; - ep_cfg.ep_interval = ep_desc->bInterval; - ep_cfg.hport = hport; - if (ep_desc->bEndpointAddress & 0x80) { - usbh_ep_alloc(&cdc_acm_class->bulkin, &ep_cfg); - } else { - usbh_ep_alloc(&cdc_acm_class->bulkout, &ep_cfg); - } - } - - USB_LOG_INFO("Register CDC ACM Class:%s\r\n", devname); - - memset(cdc_buffer, 0, 512); - ret = usbh_ep_bulk_transfer(cdc_acm_class->bulkin, cdc_buffer, 512); - if (ret < 0) { - printf("bulk in error\r\n"); - return ret; - } - printf("recv over:%d\r\n", ret); - for (size_t i = 0; i < ret; i++) { - printf("0x%02x ", cdc_buffer[i]); - } - printf("\r\n"); - const uint8_t data1[10] = { 0x02, 0x00, 0x00, 0x00, 0x02, 0x02, 0x08, 0x14 }; - - memcpy(cdc_buffer, data1, 8); - ret = usbh_ep_bulk_transfer(cdc_acm_class->bulkout, cdc_buffer, 8); - if (ret < 0) { - printf("bulk out error\r\n"); - return ret; - } - printf("send over:%d\r\n", ret); - -#if 0 - usbh_ep_bulk_async_transfer(cdc_acm_class->bulkin, cdc_buffer, 512, usbh_cdc_acm_callback, NULL); -#else - ret = usbh_ep_bulk_transfer(cdc_acm_class->bulkin, cdc_buffer, 512); - if (ret < 0) { - printf("bulk in error\r\n"); - return ret; - } - printf("recv over:%d\r\n", ret); - for (size_t i = 0; i < ret; i++) { - printf("0x%02x ", cdc_buffer[i]); - } - printf("\r\n"); -#endif - return ret; -} - -int usbh_cdc_acm_disconnect(struct usbh_hubport *hport, uint8_t intf) -{ - char devname[DEV_NAMELEN]; - int ret = 0; - - struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)hport->config.intf[intf].priv; - - if (cdc_acm_class) { - usbh_cdc_acm_devno_free(cdc_acm_class); - usbh_cdc_acm_mkdevname(cdc_acm_class, devname); - - if (cdc_acm_class->bulkin) { - ret = usb_ep_cancel(cdc_acm_class->bulkin); - if (ret < 0) { - } - usbh_ep_free(cdc_acm_class->bulkin); - } - - if (cdc_acm_class->bulkout) { - ret = usb_ep_cancel(cdc_acm_class->bulkout); - if (ret < 0) { - } - usbh_ep_free(cdc_acm_class->bulkout); - } - if (cdc_acm_class->setup) - usb_iofree(cdc_acm_class->setup); - if (cdc_acm_class->linecoding) - usb_iofree(cdc_acm_class->linecoding); - - usb_free(cdc_acm_class); - - hport->config.intf[intf].priv = NULL; - hport->config.intf[intf + 1].priv = NULL; - - USB_LOG_INFO("Unregister CDC ACM Class:%s\r\n", devname); - } - - return ret; -} - -void usbh_cdc_acm_callback(void *arg, int result) -{ - printf("result:%d\r\n", result); -} - -const struct usbh_class_driver cdc_acm_class_driver = { - .driver_name = "cdc_acm", - .connect = usbh_cdc_acm_connect, - .disconnect = usbh_cdc_acm_disconnect +/** + * @file usbh_cdc_acm.c + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#include "usbh_core.h" +#include "usbh_cdc_acm.h" + +#define DEV_FORMAT "/dev/ttyACM%d" +#define DEV_NAMELEN 16 + +static uint32_t g_devinuse = 0; + +void usbh_cdc_acm_callback(void *arg, int nbytes); + +/**************************************************************************** + * Name: usbh_cdc_acm_devno_alloc + * + * Description: + * Allocate a unique /dev/ttyACM[n] minor number in the range 0-31. + * + ****************************************************************************/ + +static int usbh_cdc_acm_devno_alloc(struct usbh_cdc_acm *priv) +{ + uint32_t flags; + int devno; + + flags = usb_osal_enter_critical_section(); + for (devno = 0; devno < 32; devno++) { + uint32_t bitno = 1 << devno; + if ((g_devinuse & bitno) == 0) { + g_devinuse |= bitno; + priv->minor = devno; + usb_osal_leave_critical_section(flags); + return 0; + } + } + + usb_osal_leave_critical_section(flags); + return -EMFILE; +} + +/**************************************************************************** + * Name: usbh_cdc_acm_devno_free + * + * Description: + * Free a /dev/ttyACM[n] minor number so that it can be used. + * + ****************************************************************************/ + +static void usbh_cdc_acm_devno_free(struct usbh_cdc_acm *priv) +{ + int devno = priv->minor; + + if (devno >= 0 && devno < 32) { + uint32_t flags = usb_osal_enter_critical_section(); + g_devinuse &= ~(1 << devno); + usb_osal_leave_critical_section(flags); + } +} + +/**************************************************************************** + * Name: usbh_cdc_acm_mkdevname + * + * Description: + * Format a /dev/ttyACM[n] device name given a minor number. + * + ****************************************************************************/ + +static inline void usbh_cdc_acm_mkdevname(struct usbh_cdc_acm *priv, char *devname) +{ + snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->minor); +} + +int usbh_cdc_acm_set_line_coding(struct usbh_hubport *hport, uint8_t intf, struct cdc_line_coding *line_coding) +{ + int ret; + struct usb_setup_packet *setup; + struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)hport->config.intf[intf].priv; + + setup = cdc_acm_class->setup; + + if (cdc_acm_class->ctrl_intf != intf) { + return -1; + } + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; + setup->bRequest = CDC_REQUEST_SET_LINE_CODING; + setup->wValue = 0; + setup->wIndex = intf; + setup->wLength = 7; + + ret = usbh_control_transfer(hport->ep0, setup, (uint8_t *)line_coding); + if (ret < 0) { + return ret; + } + memcpy(cdc_acm_class->linecoding, line_coding, sizeof(struct cdc_line_coding)); + return 0; +} + +int usbh_cdc_acm_get_line_coding(struct usbh_hubport *hport, uint8_t intf, struct cdc_line_coding *line_coding) +{ + int ret; + struct usb_setup_packet *setup; + struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)hport->config.intf[intf].priv; + + setup = cdc_acm_class->setup; + + if (cdc_acm_class->ctrl_intf != intf) { + return -1; + } + + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; + setup->bRequest = CDC_REQUEST_GET_LINE_CODING; + setup->wValue = 0; + setup->wIndex = intf; + setup->wLength = 7; + + ret = usbh_control_transfer(hport->ep0, setup, (uint8_t *)line_coding); + if (ret < 0) { + return ret; + } + memcpy(cdc_acm_class->linecoding, line_coding, sizeof(struct cdc_line_coding)); + return 0; +} + +int usbh_cdc_acm_set_line_state(struct usbh_hubport *hport, uint8_t intf, bool dtr, bool rts) +{ + int ret; + struct usb_setup_packet *setup; + struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)hport->config.intf[intf].priv; + + setup = cdc_acm_class->setup; + + if (cdc_acm_class->ctrl_intf != intf) { + return -1; + } + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; + setup->bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE; + setup->wValue = (dtr << 0) | (rts << 1); + setup->wIndex = intf; + setup->wLength = 0; + + ret = usbh_control_transfer(hport->ep0, setup, NULL); + if (ret < 0) { + return ret; + } + + cdc_acm_class->dtr = dtr; + cdc_acm_class->rts = rts; + + return 0; +} +USB_NOCACHE_RAM_SECTION uint8_t cdc_buffer[4096]; + +int usbh_cdc_acm_connect(struct usbh_hubport *hport, uint8_t intf) +{ + struct usbh_endpoint_cfg ep_cfg = { 0 }; + struct usb_endpoint_descriptor *ep_desc; + char devname[DEV_NAMELEN]; + int ret; + + struct usbh_cdc_acm *cdc_acm_class = usb_malloc(sizeof(struct usbh_cdc_acm)); + if (cdc_acm_class == NULL) { + USB_LOG_ERR("Fail to alloc cdc_acm_class\r\n"); + return -ENOMEM; + } + + memset(cdc_acm_class, 0, sizeof(struct usbh_cdc_acm)); + + usbh_cdc_acm_devno_alloc(cdc_acm_class); + usbh_cdc_acm_mkdevname(cdc_acm_class, devname); + + hport->config.intf[intf].priv = cdc_acm_class; + hport->config.intf[intf + 1].priv = cdc_acm_class; + + cdc_acm_class->setup = usb_iomalloc(sizeof(struct usb_setup_packet)); + if (cdc_acm_class->setup == NULL) { + USB_LOG_ERR("Fail to alloc setup\r\n"); + return -ENOMEM; + } + cdc_acm_class->linecoding = usb_iomalloc(sizeof(struct cdc_line_coding)); + if (cdc_acm_class->linecoding == NULL) { + USB_LOG_ERR("Fail to alloc linecoding\r\n"); + return -ENOMEM; + } + cdc_acm_class->ctrl_intf = intf; + cdc_acm_class->data_intf = intf + 1; + + cdc_acm_class->linecoding->dwDTERate = 115200; + cdc_acm_class->linecoding->bDataBits = 8; + cdc_acm_class->linecoding->bParityType = 0; + cdc_acm_class->linecoding->bCharFormat = 0; + ret = usbh_cdc_acm_set_line_coding(hport, intf, cdc_acm_class->linecoding); + if (ret < 0) { + return ret; + } + + ret = usbh_cdc_acm_set_line_state(hport, intf, true, true); + if (ret < 0) { + return ret; + } + +#if 0 + ep_desc = &hport->config.intf[intf].ep[0].ep_desc; + ep_cfg.ep_addr = ep_desc->bEndpointAddress; + ep_cfg.ep_type = ep_desc->bmAttributes & USB_ENDPOINT_TYPE_MASK; + ep_cfg.ep_mps = ep_desc->wMaxPacketSize; + ep_cfg.ep_interval = ep_desc->bInterval; + ep_cfg.hport = hport; + usbh_ep_alloc(&cdc_acm_class->intin, &ep_cfg); + +#endif + for (uint8_t i = 0; i < hport->config.intf[intf + 1].intf_desc.bNumEndpoints; i++) { + ep_desc = &hport->config.intf[intf + 1].ep[i].ep_desc; + + ep_cfg.ep_addr = ep_desc->bEndpointAddress; + ep_cfg.ep_type = ep_desc->bmAttributes & USB_ENDPOINT_TYPE_MASK; + ep_cfg.ep_mps = ep_desc->wMaxPacketSize; + ep_cfg.ep_interval = ep_desc->bInterval; + ep_cfg.hport = hport; + if (ep_desc->bEndpointAddress & 0x80) { + usbh_ep_alloc(&cdc_acm_class->bulkin, &ep_cfg); + } else { + usbh_ep_alloc(&cdc_acm_class->bulkout, &ep_cfg); + } + } + + USB_LOG_INFO("Register CDC ACM Class:%s\r\n", devname); + + memset(cdc_buffer, 0, 512); + ret = usbh_ep_bulk_transfer(cdc_acm_class->bulkin, cdc_buffer, 512); + if (ret < 0) { + printf("bulk in error\r\n"); + return ret; + } + printf("recv over:%d\r\n", ret); + for (size_t i = 0; i < ret; i++) { + printf("0x%02x ", cdc_buffer[i]); + } + printf("\r\n"); + const uint8_t data1[10] = { 0x02, 0x00, 0x00, 0x00, 0x02, 0x02, 0x08, 0x14 }; + + memcpy(cdc_buffer, data1, 8); + ret = usbh_ep_bulk_transfer(cdc_acm_class->bulkout, cdc_buffer, 8); + if (ret < 0) { + printf("bulk out error\r\n"); + return ret; + } + printf("send over:%d\r\n", ret); + +#if 0 + usbh_ep_bulk_async_transfer(cdc_acm_class->bulkin, cdc_buffer, 512, usbh_cdc_acm_callback, NULL); +#else + ret = usbh_ep_bulk_transfer(cdc_acm_class->bulkin, cdc_buffer, 512); + if (ret < 0) { + printf("bulk in error\r\n"); + return ret; + } + printf("recv over:%d\r\n", ret); + for (size_t i = 0; i < ret; i++) { + printf("0x%02x ", cdc_buffer[i]); + } + printf("\r\n"); +#endif + return ret; +} + +int usbh_cdc_acm_disconnect(struct usbh_hubport *hport, uint8_t intf) +{ + char devname[DEV_NAMELEN]; + int ret = 0; + + struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)hport->config.intf[intf].priv; + + if (cdc_acm_class) { + usbh_cdc_acm_devno_free(cdc_acm_class); + usbh_cdc_acm_mkdevname(cdc_acm_class, devname); + + if (cdc_acm_class->bulkin) { + ret = usb_ep_cancel(cdc_acm_class->bulkin); + if (ret < 0) { + } + usbh_ep_free(cdc_acm_class->bulkin); + } + + if (cdc_acm_class->bulkout) { + ret = usb_ep_cancel(cdc_acm_class->bulkout); + if (ret < 0) { + } + usbh_ep_free(cdc_acm_class->bulkout); + } + if (cdc_acm_class->setup) + usb_iofree(cdc_acm_class->setup); + if (cdc_acm_class->linecoding) + usb_iofree(cdc_acm_class->linecoding); + + usb_free(cdc_acm_class); + + hport->config.intf[intf].priv = NULL; + hport->config.intf[intf + 1].priv = NULL; + + USB_LOG_INFO("Unregister CDC ACM Class:%s\r\n", devname); + } + + return ret; +} + +void usbh_cdc_acm_callback(void *arg, int result) +{ + printf("result:%d\r\n", result); +} + +const struct usbh_class_driver cdc_acm_class_driver = { + .driver_name = "cdc_acm", + .connect = usbh_cdc_acm_connect, + .disconnect = usbh_cdc_acm_disconnect }; \ No newline at end of file diff --git a/class/cdc/usbh_cdc_acm.h b/class/cdc/usbh_cdc_acm.h index 0de425e8..e1283504 100644 --- a/class/cdc/usbh_cdc_acm.h +++ b/class/cdc/usbh_cdc_acm.h @@ -1,52 +1,52 @@ -/** - * @file usbh_cdc_acm.h - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#ifndef _USBH_CDC_ACM_H -#define _USBH_CDC_ACM_H - -#include "usb_cdc.h" - -struct usbh_cdc_acm { - struct usb_setup_packet *setup; - struct cdc_line_coding *linecoding; - uint8_t ctrl_intf; /* Control interface number */ - uint8_t data_intf; /* Data interface number */ - bool dtr; - bool rts; - uint8_t minor; - usbh_epinfo_t bulkin; /* Bulk IN endpoint */ - usbh_epinfo_t bulkout; /* Bulk OUT endpoint */ -#ifdef HAVE_INTIN_ENDPOINT - usbh_epinfo_t intin; /* Interrupt IN endpoint (optional) */ -#endif -}; - -extern const struct usbh_class_driver cdc_acm_class_driver; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif - +/** + * @file usbh_cdc_acm.h + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _USBH_CDC_ACM_H +#define _USBH_CDC_ACM_H + +#include "usb_cdc.h" + +struct usbh_cdc_acm { + struct usb_setup_packet *setup; + struct cdc_line_coding *linecoding; + uint8_t ctrl_intf; /* Control interface number */ + uint8_t data_intf; /* Data interface number */ + bool dtr; + bool rts; + uint8_t minor; + usbh_epinfo_t bulkin; /* Bulk IN endpoint */ + usbh_epinfo_t bulkout; /* Bulk OUT endpoint */ +#ifdef HAVE_INTIN_ENDPOINT + usbh_epinfo_t intin; /* Interrupt IN endpoint (optional) */ +#endif +}; + +extern const struct usbh_class_driver cdc_acm_class_driver; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + #endif \ No newline at end of file diff --git a/class/hid/usbd_hid.c b/class/hid/usbd_hid.c index 52a1b265..7aa0f60d 100644 --- a/class/hid/usbd_hid.c +++ b/class/hid/usbd_hid.c @@ -1,286 +1,286 @@ -/** - * @file usbd_hid.c - * @brief - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#include "usbd_core.h" -#include "usbd_hid.h" - -#define HID_STATE_IDLE 0 -#define HID_STATE_BUSY 1 - -struct usbd_hid_cfg_private { - const uint8_t *hid_descriptor; - const uint8_t *hid_report_descriptor; - uint32_t hid_report_descriptor_len; - uint8_t current_intf_num; - uint8_t hid_state; - uint8_t report; - uint8_t idle_state; - uint8_t protocol; - - uint8_t (*get_report_callback)(uint8_t report_id, uint8_t report_type); - void (*set_report_callback)(uint8_t report_id, uint8_t report_type, uint8_t *report, uint8_t report_len); - uint8_t (*get_idle_callback)(uint8_t report_id); - void (*set_idle_callback)(uint8_t report_id, uint8_t duration); - void (*set_protocol_callback)(uint8_t protocol); - uint8_t (*get_protocol_callback)(void); - - usb_slist_t list; -} usbd_hid_cfg[4]; - -static usb_slist_t usbd_hid_class_head = USB_SLIST_OBJECT_INIT(usbd_hid_class_head); - -static void usbd_hid_reset(void) -{ - usb_slist_t *i; - usb_slist_for_each(i, &usbd_hid_class_head) - { - struct usbd_hid_cfg_private *hid_intf = usb_slist_entry(i, struct usbd_hid_cfg_private, list); - hid_intf->hid_state = HID_STATE_IDLE; - hid_intf->report = 0; - hid_intf->idle_state = 0; - hid_intf->protocol = 0; - } -} - -int hid_custom_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) -{ - USB_LOG_DBG("HID Custom request: " - "bRequest 0x%02x\r\n", - setup->bRequest); - - if (((setup->bmRequestType & USB_REQUEST_DIR_MASK) == USB_REQUEST_DIR_IN) && - setup->bRequest == USB_REQUEST_GET_DESCRIPTOR) { - uint8_t value = (uint8_t)(setup->wValue >> 8); - uint8_t intf_num = (uint8_t)setup->wIndex; - - struct usbd_hid_cfg_private *current_hid_intf = NULL; - usb_slist_t *i; - usb_slist_for_each(i, &usbd_hid_class_head) - { - struct usbd_hid_cfg_private *hid_intf = usb_slist_entry(i, struct usbd_hid_cfg_private, list); - - if (hid_intf->current_intf_num == intf_num) { - current_hid_intf = hid_intf; - break; - } - } - - if (current_hid_intf == NULL) { - return -2; - } - - switch (value) { - case HID_DESCRIPTOR_TYPE_HID: - USB_LOG_INFO("get HID Descriptor\r\n"); - *data = (uint8_t *)current_hid_intf->hid_descriptor; - *len = current_hid_intf->hid_descriptor[0]; - break; - - case HID_DESCRIPTOR_TYPE_HID_REPORT: - USB_LOG_INFO("get Report Descriptor\r\n"); - *data = (uint8_t *)current_hid_intf->hid_report_descriptor; - *len = current_hid_intf->hid_report_descriptor_len; - break; - - case HID_DESCRIPTOR_TYPE_HID_PHYSICAL: - USB_LOG_INFO("get PHYSICAL Descriptor\r\n"); - - break; - - default: - return -2; - } - - return 0; - } - - return -1; -} - -int hid_class_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) -{ - USB_LOG_DBG("HID Class request: " - "bRequest 0x%02x\r\n", - setup->bRequest); - - struct usbd_hid_cfg_private *current_hid_intf = NULL; - usb_slist_t *i; - usb_slist_for_each(i, &usbd_hid_class_head) - { - struct usbd_hid_cfg_private *hid_intf = usb_slist_entry(i, struct usbd_hid_cfg_private, list); - uint8_t intf_num = (uint8_t)setup->wIndex; - if (hid_intf->current_intf_num == intf_num) { - current_hid_intf = hid_intf; - break; - } - } - - if (current_hid_intf == NULL) { - return -2; - } - - switch (setup->bRequest) { - case HID_REQUEST_GET_REPORT: - if (current_hid_intf->get_report_callback) - current_hid_intf->report = current_hid_intf->get_report_callback(LO_BYTE(setup->wValue), HI_BYTE(setup->wValue)); /*report id ,report type*/ - - *data = (uint8_t *)¤t_hid_intf->report; - *len = 1; - break; - case HID_REQUEST_GET_IDLE: - if (current_hid_intf->get_idle_callback) - current_hid_intf->idle_state = current_hid_intf->get_idle_callback(LO_BYTE(setup->wValue)); - - *data = (uint8_t *)¤t_hid_intf->idle_state; - *len = 1; - break; - case HID_REQUEST_GET_PROTOCOL: - if (current_hid_intf->get_protocol_callback) - current_hid_intf->protocol = current_hid_intf->get_protocol_callback(); - - *data = (uint8_t *)¤t_hid_intf->protocol; - *len = 1; - break; - case HID_REQUEST_SET_REPORT: - if (current_hid_intf->set_report_callback) - current_hid_intf->set_report_callback(LO_BYTE(setup->wValue), HI_BYTE(setup->wValue), *data, *len); /*report id ,report type,report,report len*/ - - current_hid_intf->report = **data; - break; - case HID_REQUEST_SET_IDLE: - if (current_hid_intf->set_idle_callback) - current_hid_intf->set_idle_callback(LO_BYTE(setup->wValue), HI_BYTE(setup->wIndex)); /*report id ,duration*/ - - current_hid_intf->idle_state = HI_BYTE(setup->wIndex); - break; - case HID_REQUEST_SET_PROTOCOL: - if (current_hid_intf->set_protocol_callback) - current_hid_intf->set_protocol_callback(LO_BYTE(setup->wValue)); /*protocol*/ - - current_hid_intf->protocol = LO_BYTE(setup->wValue); - break; - - default: - USB_LOG_WRN("Unhandled HID Class bRequest 0x%02x\r\n", setup->bRequest); - return -1; - } - - return 0; -} - -static void hid_notify_handler(uint8_t event, void *arg) -{ - switch (event) { - case USBD_EVENT_RESET: - usbd_hid_reset(); - break; - - default: - break; - } -} - -void usbd_hid_reset_state(void) -{ - // usbd_hid_cfg.hid_state = HID_STATE_IDLE; -} - -void usbd_hid_send_report(uint8_t ep, uint8_t *data, uint8_t len) -{ - // if(usbd_hid_cfg.hid_state == HID_STATE_IDLE) - // { - // usbd_hid_cfg.hid_state = HID_STATE_BUSY; - // usbd_ep_write(ep, data, len, NULL); - // } -} - -void usbd_hid_descriptor_register(uint8_t intf_num, const uint8_t *desc) -{ - // usbd_hid_cfg.hid_descriptor = desc; -} - -void usbd_hid_report_descriptor_register(uint8_t intf_num, const uint8_t *desc, uint32_t desc_len) -{ - usb_slist_t *i; - usb_slist_for_each(i, &usbd_hid_class_head) - { - struct usbd_hid_cfg_private *hid_intf = usb_slist_entry(i, struct usbd_hid_cfg_private, list); - - if (hid_intf->current_intf_num == intf_num) { - hid_intf->hid_report_descriptor = desc; - hid_intf->hid_report_descriptor_len = desc_len; - return; - } - } -} -// clang-format off -void usbd_hid_set_request_callback( uint8_t intf_num, - uint8_t (*get_report_callback)(uint8_t report_id, uint8_t report_type), - void (*set_report_callback)(uint8_t report_id, uint8_t report_type, uint8_t *report, uint8_t report_len), - uint8_t (*get_idle_callback)(uint8_t report_id), - void (*set_idle_callback)(uint8_t report_id, uint8_t duration), - void (*set_protocol_callback)(uint8_t protocol), - uint8_t (*get_protocol_callback)(void)) -// clang-format on -{ - usb_slist_t *i; - usb_slist_for_each(i, &usbd_hid_class_head) - { - struct usbd_hid_cfg_private *hid_intf = usb_slist_entry(i, struct usbd_hid_cfg_private, list); - - if (hid_intf->current_intf_num == intf_num) { - if (get_report_callback) - hid_intf->get_report_callback = get_report_callback; - if (set_report_callback) - hid_intf->set_report_callback = set_report_callback; - if (get_idle_callback) - hid_intf->get_idle_callback = get_idle_callback; - if (set_idle_callback) - hid_intf->set_idle_callback = set_idle_callback; - if (set_protocol_callback) - hid_intf->set_protocol_callback = set_protocol_callback; - if (get_protocol_callback) - hid_intf->get_protocol_callback = get_protocol_callback; - return; - } - } -} - -void usbd_hid_add_interface(usbd_class_t *devclass, usbd_interface_t *intf) -{ - static usbd_class_t *last_class = NULL; - static uint8_t hid_num = 0; - if (last_class != devclass) { - last_class = devclass; - usbd_class_register(devclass); - } - - intf->class_handler = hid_class_request_handler; - intf->custom_handler = hid_custom_request_handler; - intf->vendor_handler = NULL; - intf->notify_handler = hid_notify_handler; - usbd_class_add_interface(devclass, intf); - - usbd_hid_cfg[hid_num].current_intf_num = intf->intf_num; - usb_slist_add_tail(&usbd_hid_class_head, &usbd_hid_cfg[hid_num].list); - hid_num++; -} +/** + * @file usbd_hid.c + * @brief + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#include "usbd_core.h" +#include "usbd_hid.h" + +#define HID_STATE_IDLE 0 +#define HID_STATE_BUSY 1 + +struct usbd_hid_cfg_private { + const uint8_t *hid_descriptor; + const uint8_t *hid_report_descriptor; + uint32_t hid_report_descriptor_len; + uint8_t current_intf_num; + uint8_t hid_state; + uint8_t report; + uint8_t idle_state; + uint8_t protocol; + + uint8_t (*get_report_callback)(uint8_t report_id, uint8_t report_type); + void (*set_report_callback)(uint8_t report_id, uint8_t report_type, uint8_t *report, uint8_t report_len); + uint8_t (*get_idle_callback)(uint8_t report_id); + void (*set_idle_callback)(uint8_t report_id, uint8_t duration); + void (*set_protocol_callback)(uint8_t protocol); + uint8_t (*get_protocol_callback)(void); + + usb_slist_t list; +} usbd_hid_cfg[4]; + +static usb_slist_t usbd_hid_class_head = USB_SLIST_OBJECT_INIT(usbd_hid_class_head); + +static void usbd_hid_reset(void) +{ + usb_slist_t *i; + usb_slist_for_each(i, &usbd_hid_class_head) + { + struct usbd_hid_cfg_private *hid_intf = usb_slist_entry(i, struct usbd_hid_cfg_private, list); + hid_intf->hid_state = HID_STATE_IDLE; + hid_intf->report = 0; + hid_intf->idle_state = 0; + hid_intf->protocol = 0; + } +} + +int hid_custom_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) +{ + USB_LOG_DBG("HID Custom request: " + "bRequest 0x%02x\r\n", + setup->bRequest); + + if (((setup->bmRequestType & USB_REQUEST_DIR_MASK) == USB_REQUEST_DIR_IN) && + setup->bRequest == USB_REQUEST_GET_DESCRIPTOR) { + uint8_t value = (uint8_t)(setup->wValue >> 8); + uint8_t intf_num = (uint8_t)setup->wIndex; + + struct usbd_hid_cfg_private *current_hid_intf = NULL; + usb_slist_t *i; + usb_slist_for_each(i, &usbd_hid_class_head) + { + struct usbd_hid_cfg_private *hid_intf = usb_slist_entry(i, struct usbd_hid_cfg_private, list); + + if (hid_intf->current_intf_num == intf_num) { + current_hid_intf = hid_intf; + break; + } + } + + if (current_hid_intf == NULL) { + return -2; + } + + switch (value) { + case HID_DESCRIPTOR_TYPE_HID: + USB_LOG_INFO("get HID Descriptor\r\n"); + *data = (uint8_t *)current_hid_intf->hid_descriptor; + *len = current_hid_intf->hid_descriptor[0]; + break; + + case HID_DESCRIPTOR_TYPE_HID_REPORT: + USB_LOG_INFO("get Report Descriptor\r\n"); + *data = (uint8_t *)current_hid_intf->hid_report_descriptor; + *len = current_hid_intf->hid_report_descriptor_len; + break; + + case HID_DESCRIPTOR_TYPE_HID_PHYSICAL: + USB_LOG_INFO("get PHYSICAL Descriptor\r\n"); + + break; + + default: + return -2; + } + + return 0; + } + + return -1; +} + +int hid_class_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) +{ + USB_LOG_DBG("HID Class request: " + "bRequest 0x%02x\r\n", + setup->bRequest); + + struct usbd_hid_cfg_private *current_hid_intf = NULL; + usb_slist_t *i; + usb_slist_for_each(i, &usbd_hid_class_head) + { + struct usbd_hid_cfg_private *hid_intf = usb_slist_entry(i, struct usbd_hid_cfg_private, list); + uint8_t intf_num = (uint8_t)setup->wIndex; + if (hid_intf->current_intf_num == intf_num) { + current_hid_intf = hid_intf; + break; + } + } + + if (current_hid_intf == NULL) { + return -2; + } + + switch (setup->bRequest) { + case HID_REQUEST_GET_REPORT: + if (current_hid_intf->get_report_callback) + current_hid_intf->report = current_hid_intf->get_report_callback(LO_BYTE(setup->wValue), HI_BYTE(setup->wValue)); /*report id ,report type*/ + + *data = (uint8_t *)¤t_hid_intf->report; + *len = 1; + break; + case HID_REQUEST_GET_IDLE: + if (current_hid_intf->get_idle_callback) + current_hid_intf->idle_state = current_hid_intf->get_idle_callback(LO_BYTE(setup->wValue)); + + *data = (uint8_t *)¤t_hid_intf->idle_state; + *len = 1; + break; + case HID_REQUEST_GET_PROTOCOL: + if (current_hid_intf->get_protocol_callback) + current_hid_intf->protocol = current_hid_intf->get_protocol_callback(); + + *data = (uint8_t *)¤t_hid_intf->protocol; + *len = 1; + break; + case HID_REQUEST_SET_REPORT: + if (current_hid_intf->set_report_callback) + current_hid_intf->set_report_callback(LO_BYTE(setup->wValue), HI_BYTE(setup->wValue), *data, *len); /*report id ,report type,report,report len*/ + + current_hid_intf->report = **data; + break; + case HID_REQUEST_SET_IDLE: + if (current_hid_intf->set_idle_callback) + current_hid_intf->set_idle_callback(LO_BYTE(setup->wValue), HI_BYTE(setup->wIndex)); /*report id ,duration*/ + + current_hid_intf->idle_state = HI_BYTE(setup->wIndex); + break; + case HID_REQUEST_SET_PROTOCOL: + if (current_hid_intf->set_protocol_callback) + current_hid_intf->set_protocol_callback(LO_BYTE(setup->wValue)); /*protocol*/ + + current_hid_intf->protocol = LO_BYTE(setup->wValue); + break; + + default: + USB_LOG_WRN("Unhandled HID Class bRequest 0x%02x\r\n", setup->bRequest); + return -1; + } + + return 0; +} + +static void hid_notify_handler(uint8_t event, void *arg) +{ + switch (event) { + case USBD_EVENT_RESET: + usbd_hid_reset(); + break; + + default: + break; + } +} + +void usbd_hid_reset_state(void) +{ + // usbd_hid_cfg.hid_state = HID_STATE_IDLE; +} + +void usbd_hid_send_report(uint8_t ep, uint8_t *data, uint8_t len) +{ + // if(usbd_hid_cfg.hid_state == HID_STATE_IDLE) + // { + // usbd_hid_cfg.hid_state = HID_STATE_BUSY; + // usbd_ep_write(ep, data, len, NULL); + // } +} + +void usbd_hid_descriptor_register(uint8_t intf_num, const uint8_t *desc) +{ + // usbd_hid_cfg.hid_descriptor = desc; +} + +void usbd_hid_report_descriptor_register(uint8_t intf_num, const uint8_t *desc, uint32_t desc_len) +{ + usb_slist_t *i; + usb_slist_for_each(i, &usbd_hid_class_head) + { + struct usbd_hid_cfg_private *hid_intf = usb_slist_entry(i, struct usbd_hid_cfg_private, list); + + if (hid_intf->current_intf_num == intf_num) { + hid_intf->hid_report_descriptor = desc; + hid_intf->hid_report_descriptor_len = desc_len; + return; + } + } +} +// clang-format off +void usbd_hid_set_request_callback( uint8_t intf_num, + uint8_t (*get_report_callback)(uint8_t report_id, uint8_t report_type), + void (*set_report_callback)(uint8_t report_id, uint8_t report_type, uint8_t *report, uint8_t report_len), + uint8_t (*get_idle_callback)(uint8_t report_id), + void (*set_idle_callback)(uint8_t report_id, uint8_t duration), + void (*set_protocol_callback)(uint8_t protocol), + uint8_t (*get_protocol_callback)(void)) +// clang-format on +{ + usb_slist_t *i; + usb_slist_for_each(i, &usbd_hid_class_head) + { + struct usbd_hid_cfg_private *hid_intf = usb_slist_entry(i, struct usbd_hid_cfg_private, list); + + if (hid_intf->current_intf_num == intf_num) { + if (get_report_callback) + hid_intf->get_report_callback = get_report_callback; + if (set_report_callback) + hid_intf->set_report_callback = set_report_callback; + if (get_idle_callback) + hid_intf->get_idle_callback = get_idle_callback; + if (set_idle_callback) + hid_intf->set_idle_callback = set_idle_callback; + if (set_protocol_callback) + hid_intf->set_protocol_callback = set_protocol_callback; + if (get_protocol_callback) + hid_intf->get_protocol_callback = get_protocol_callback; + return; + } + } +} + +void usbd_hid_add_interface(usbd_class_t *devclass, usbd_interface_t *intf) +{ + static usbd_class_t *last_class = NULL; + static uint8_t hid_num = 0; + if (last_class != devclass) { + last_class = devclass; + usbd_class_register(devclass); + } + + intf->class_handler = hid_class_request_handler; + intf->custom_handler = hid_custom_request_handler; + intf->vendor_handler = NULL; + intf->notify_handler = hid_notify_handler; + usbd_class_add_interface(devclass, intf); + + usbd_hid_cfg[hid_num].current_intf_num = intf->intf_num; + usb_slist_add_tail(&usbd_hid_class_head, &usbd_hid_cfg[hid_num].list); + hid_num++; +} diff --git a/class/hid/usbh_hid.c b/class/hid/usbh_hid.c index 0a240318..5c6e4fc9 100644 --- a/class/hid/usbh_hid.c +++ b/class/hid/usbh_hid.c @@ -1,275 +1,275 @@ -/** - * @file usbh_hid.c - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#include "usbh_core.h" -#include "usbh_hid.h" - -#define DEV_FORMAT "/dev/input%d" -#define DEV_NAMELEN 16 - -static uint32_t g_devinuse = 0; - -/**************************************************************************** - * Name: usbh_hid_devno_alloc - * - * Description: - * Allocate a unique /dev/hid[n] minor number in the range 0-31. - * - ****************************************************************************/ - -static int usbh_hid_devno_alloc(struct usbh_hid *priv) -{ - uint32_t flags; - int devno; - - flags = usb_osal_enter_critical_section(); - for (devno = 0; devno < 32; devno++) { - uint32_t bitno = 1 << devno; - if ((g_devinuse & bitno) == 0) { - g_devinuse |= bitno; - priv->minor = devno; - usb_osal_leave_critical_section(flags); - return 0; - } - } - - usb_osal_leave_critical_section(flags); - return -EMFILE; -} - -/**************************************************************************** - * Name: usbh_hid_devno_free - * - * Description: - * Free a /dev/hid[n] minor number so that it can be used. - * - ****************************************************************************/ - -static void usbh_hid_devno_free(struct usbh_hid *priv) -{ - int devno = priv->minor; - - if (devno >= 0 && devno < 32) { - uint32_t flags = usb_osal_enter_critical_section(); - g_devinuse &= ~(1 << devno); - usb_osal_leave_critical_section(flags); - } -} - -/**************************************************************************** - * Name: usbh_hid_mkdevname - * - * Description: - * Format a /dev/hid[n] device name given a minor number. - * - ****************************************************************************/ - -static inline void usbh_hid_mkdevname(struct usbh_hid *priv, char *devname) -{ - snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->minor); -} - -int usbh_hid_get_report_descriptor(struct usbh_hubport *hport, uint8_t intf, uint8_t *buffer) -{ - struct usb_setup_packet *setup; - struct usbh_hid *hid_class = (struct usbh_hid *)hport->config.intf[intf].priv; - - setup = hid_class->setup; - - if (hid_class->intf != intf) { - return -1; - } - - setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_INTERFACE; - setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; - setup->wValue = HID_DESCRIPTOR_TYPE_HID_REPORT << 8; - setup->wIndex = intf; - setup->wLength = 128; - - return usbh_control_transfer(hport->ep0, setup, buffer); -} - -int usbh_hid_set_idle(struct usbh_hubport *hport, uint8_t intf, uint8_t report_id, uint8_t duration) -{ - int ret; - struct usb_setup_packet *setup; - struct usbh_hid *hid_class = (struct usbh_hid *)hport->config.intf[intf].priv; - - setup = hid_class->setup; - - if (hid_class->intf != intf) { - return -1; - } - - setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; - setup->bRequest = HID_REQUEST_SET_IDLE; - setup->wValue = report_id; - setup->wIndex = (duration << 8) | intf; - setup->wLength = 0; - - ret = usbh_control_transfer(hport->ep0, setup, NULL); - if (ret < 0) { - return ret; - } - - return 0; -} - -int usbh_hid_get_idle(struct usbh_hubport *hport, uint8_t intf, uint8_t *buffer) -{ - int ret; - struct usb_setup_packet *setup; - struct usbh_hid *hid_class = (struct usbh_hid *)hport->config.intf[intf].priv; - - setup = hid_class->setup; - - if (hid_class->intf != intf) { - return -1; - } - - setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; - setup->bRequest = HID_REQUEST_GET_IDLE; - setup->wValue = 0; - setup->wIndex = intf; - setup->wLength = 1; - - ret = usbh_control_transfer(hport->ep0, setup, buffer); - if (ret < 0) { - return ret; - } - - return 0; -} - -USB_NOCACHE_RAM_SECTION uint8_t report_buffer[128]; - -void usbh_hid_callback(void *arg, int nbytes) -{ - printf("nbytes:%d\r\n", nbytes); -} - -int usbh_hid_connect(struct usbh_hubport *hport, uint8_t intf) -{ - struct usbh_endpoint_cfg ep_cfg = { 0 }; - struct usb_endpoint_descriptor *ep_desc; - char devname[DEV_NAMELEN]; - int ret; - - struct usbh_hid *hid_class = usb_malloc(sizeof(struct usbh_hid)); - if (hid_class == NULL) { - USB_LOG_ERR("Fail to alloc hid_class\r\n"); - return -ENOMEM; - } - memset(hid_class, 0, sizeof(struct usbh_hid)); - - usbh_hid_devno_alloc(hid_class); - usbh_hid_mkdevname(hid_class, devname); - - hport->config.intf[intf].priv = hid_class; - - hid_class->setup = usb_iomalloc(sizeof(struct usb_setup_packet)); - if (hid_class->setup == NULL) { - USB_LOG_ERR("Fail to alloc setup\r\n"); - return -ENOMEM; - } - hid_class->intf = intf; - - ret = usbh_hid_set_idle(hport, intf, 0, 0); - if (ret < 0) { - return ret; - } - - ret = usbh_hid_get_report_descriptor(hport, intf, report_buffer); - if (ret < 0) { - return ret; - } - - for (uint8_t i = 0; i < hport->config.intf[intf].intf_desc.bNumEndpoints; i++) { - ep_desc = &hport->config.intf[intf].ep[i].ep_desc; - ep_cfg.ep_addr = ep_desc->bEndpointAddress; - ep_cfg.ep_type = ep_desc->bmAttributes & USB_ENDPOINT_TYPE_MASK; - ep_cfg.ep_mps = ep_desc->wMaxPacketSize; - ep_cfg.ep_interval = ep_desc->bInterval; - ep_cfg.hport = hport; - if (ep_desc->bEndpointAddress & 0x80) { - usbh_ep_alloc(&hid_class->intin, &ep_cfg); - } else { - usbh_ep_alloc(&hid_class->intout, &ep_cfg); - } - } - - USB_LOG_INFO("Register HID Class:%s\r\n", devname); - - ret = usbh_ep_intr_async_transfer(hid_class->intin, report_buffer, 128, usbh_hid_callback, NULL); - if (ret < 0) { - return ret; - } -#if 0 - ret = usbh_ep_intr_transfer(hid_class->intin, report_buffer, 128); - if (ret < 0) { - return ret; - } - USB_LOG_INFO("recv len:%d\r\n", ret); -#endif - return 0; -} - -int usbh_hid_disconnect(struct usbh_hubport *hport, uint8_t intf) -{ - char devname[DEV_NAMELEN]; - int ret = 0; - - struct usbh_hid *hid_class = (struct usbh_hid *)hport->config.intf[intf].priv; - - if (hid_class) { - usbh_hid_devno_free(hid_class); - usbh_hid_mkdevname(hid_class, devname); - - if (hid_class->intin) { - ret = usb_ep_cancel(hid_class->intin); - if (ret < 0) { - } - usbh_ep_free(hid_class->intin); - } - - if (hid_class->intout) { - ret = usb_ep_cancel(hid_class->intout); - if (ret < 0) { - } - usbh_ep_free(hid_class->intout); - } - if (hid_class->setup) - usb_iofree(hid_class->setup); - - usb_free(hid_class); - hport->config.intf[intf].priv = NULL; - - USB_LOG_INFO("Unregister HID Class:%s\r\n", devname); - } - - return ret; -} - -const struct usbh_class_driver hid_class_driver = { - .driver_name = "hid", - .connect = usbh_hid_connect, - .disconnect = usbh_hid_disconnect +/** + * @file usbh_hid.c + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#include "usbh_core.h" +#include "usbh_hid.h" + +#define DEV_FORMAT "/dev/input%d" +#define DEV_NAMELEN 16 + +static uint32_t g_devinuse = 0; + +/**************************************************************************** + * Name: usbh_hid_devno_alloc + * + * Description: + * Allocate a unique /dev/hid[n] minor number in the range 0-31. + * + ****************************************************************************/ + +static int usbh_hid_devno_alloc(struct usbh_hid *priv) +{ + uint32_t flags; + int devno; + + flags = usb_osal_enter_critical_section(); + for (devno = 0; devno < 32; devno++) { + uint32_t bitno = 1 << devno; + if ((g_devinuse & bitno) == 0) { + g_devinuse |= bitno; + priv->minor = devno; + usb_osal_leave_critical_section(flags); + return 0; + } + } + + usb_osal_leave_critical_section(flags); + return -EMFILE; +} + +/**************************************************************************** + * Name: usbh_hid_devno_free + * + * Description: + * Free a /dev/hid[n] minor number so that it can be used. + * + ****************************************************************************/ + +static void usbh_hid_devno_free(struct usbh_hid *priv) +{ + int devno = priv->minor; + + if (devno >= 0 && devno < 32) { + uint32_t flags = usb_osal_enter_critical_section(); + g_devinuse &= ~(1 << devno); + usb_osal_leave_critical_section(flags); + } +} + +/**************************************************************************** + * Name: usbh_hid_mkdevname + * + * Description: + * Format a /dev/hid[n] device name given a minor number. + * + ****************************************************************************/ + +static inline void usbh_hid_mkdevname(struct usbh_hid *priv, char *devname) +{ + snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->minor); +} + +int usbh_hid_get_report_descriptor(struct usbh_hubport *hport, uint8_t intf, uint8_t *buffer) +{ + struct usb_setup_packet *setup; + struct usbh_hid *hid_class = (struct usbh_hid *)hport->config.intf[intf].priv; + + setup = hid_class->setup; + + if (hid_class->intf != intf) { + return -1; + } + + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_INTERFACE; + setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; + setup->wValue = HID_DESCRIPTOR_TYPE_HID_REPORT << 8; + setup->wIndex = intf; + setup->wLength = 128; + + return usbh_control_transfer(hport->ep0, setup, buffer); +} + +int usbh_hid_set_idle(struct usbh_hubport *hport, uint8_t intf, uint8_t report_id, uint8_t duration) +{ + int ret; + struct usb_setup_packet *setup; + struct usbh_hid *hid_class = (struct usbh_hid *)hport->config.intf[intf].priv; + + setup = hid_class->setup; + + if (hid_class->intf != intf) { + return -1; + } + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; + setup->bRequest = HID_REQUEST_SET_IDLE; + setup->wValue = report_id; + setup->wIndex = (duration << 8) | intf; + setup->wLength = 0; + + ret = usbh_control_transfer(hport->ep0, setup, NULL); + if (ret < 0) { + return ret; + } + + return 0; +} + +int usbh_hid_get_idle(struct usbh_hubport *hport, uint8_t intf, uint8_t *buffer) +{ + int ret; + struct usb_setup_packet *setup; + struct usbh_hid *hid_class = (struct usbh_hid *)hport->config.intf[intf].priv; + + setup = hid_class->setup; + + if (hid_class->intf != intf) { + return -1; + } + + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; + setup->bRequest = HID_REQUEST_GET_IDLE; + setup->wValue = 0; + setup->wIndex = intf; + setup->wLength = 1; + + ret = usbh_control_transfer(hport->ep0, setup, buffer); + if (ret < 0) { + return ret; + } + + return 0; +} + +USB_NOCACHE_RAM_SECTION uint8_t report_buffer[128]; + +void usbh_hid_callback(void *arg, int nbytes) +{ + printf("nbytes:%d\r\n", nbytes); +} + +int usbh_hid_connect(struct usbh_hubport *hport, uint8_t intf) +{ + struct usbh_endpoint_cfg ep_cfg = { 0 }; + struct usb_endpoint_descriptor *ep_desc; + char devname[DEV_NAMELEN]; + int ret; + + struct usbh_hid *hid_class = usb_malloc(sizeof(struct usbh_hid)); + if (hid_class == NULL) { + USB_LOG_ERR("Fail to alloc hid_class\r\n"); + return -ENOMEM; + } + memset(hid_class, 0, sizeof(struct usbh_hid)); + + usbh_hid_devno_alloc(hid_class); + usbh_hid_mkdevname(hid_class, devname); + + hport->config.intf[intf].priv = hid_class; + + hid_class->setup = usb_iomalloc(sizeof(struct usb_setup_packet)); + if (hid_class->setup == NULL) { + USB_LOG_ERR("Fail to alloc setup\r\n"); + return -ENOMEM; + } + hid_class->intf = intf; + + ret = usbh_hid_set_idle(hport, intf, 0, 0); + if (ret < 0) { + return ret; + } + + ret = usbh_hid_get_report_descriptor(hport, intf, report_buffer); + if (ret < 0) { + return ret; + } + + for (uint8_t i = 0; i < hport->config.intf[intf].intf_desc.bNumEndpoints; i++) { + ep_desc = &hport->config.intf[intf].ep[i].ep_desc; + ep_cfg.ep_addr = ep_desc->bEndpointAddress; + ep_cfg.ep_type = ep_desc->bmAttributes & USB_ENDPOINT_TYPE_MASK; + ep_cfg.ep_mps = ep_desc->wMaxPacketSize; + ep_cfg.ep_interval = ep_desc->bInterval; + ep_cfg.hport = hport; + if (ep_desc->bEndpointAddress & 0x80) { + usbh_ep_alloc(&hid_class->intin, &ep_cfg); + } else { + usbh_ep_alloc(&hid_class->intout, &ep_cfg); + } + } + + USB_LOG_INFO("Register HID Class:%s\r\n", devname); + + ret = usbh_ep_intr_async_transfer(hid_class->intin, report_buffer, 128, usbh_hid_callback, NULL); + if (ret < 0) { + return ret; + } +#if 0 + ret = usbh_ep_intr_transfer(hid_class->intin, report_buffer, 128); + if (ret < 0) { + return ret; + } + USB_LOG_INFO("recv len:%d\r\n", ret); +#endif + return 0; +} + +int usbh_hid_disconnect(struct usbh_hubport *hport, uint8_t intf) +{ + char devname[DEV_NAMELEN]; + int ret = 0; + + struct usbh_hid *hid_class = (struct usbh_hid *)hport->config.intf[intf].priv; + + if (hid_class) { + usbh_hid_devno_free(hid_class); + usbh_hid_mkdevname(hid_class, devname); + + if (hid_class->intin) { + ret = usb_ep_cancel(hid_class->intin); + if (ret < 0) { + } + usbh_ep_free(hid_class->intin); + } + + if (hid_class->intout) { + ret = usb_ep_cancel(hid_class->intout); + if (ret < 0) { + } + usbh_ep_free(hid_class->intout); + } + if (hid_class->setup) + usb_iofree(hid_class->setup); + + usb_free(hid_class); + hport->config.intf[intf].priv = NULL; + + USB_LOG_INFO("Unregister HID Class:%s\r\n", devname); + } + + return ret; +} + +const struct usbh_class_driver hid_class_driver = { + .driver_name = "hid", + .connect = usbh_hid_connect, + .disconnect = usbh_hid_disconnect }; \ No newline at end of file diff --git a/class/hid/usbh_hid.h b/class/hid/usbh_hid.h index 7d01f100..a22c0653 100644 --- a/class/hid/usbh_hid.h +++ b/class/hid/usbh_hid.h @@ -1,45 +1,45 @@ -/** - * @file usbh_hid.h - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#ifndef _USBH_HID_H -#define _USBH_HID_H - -#include "usb_hid.h" - -struct usbh_hid { - struct usb_setup_packet *setup; - uint8_t intf; /* interface number */ - uint8_t minor; - usbh_epinfo_t intin; /* INTR IN endpoint */ - usbh_epinfo_t intout; /* INTR OUT endpoint */ -}; - -extern const struct usbh_class_driver hid_class_driver; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif - +/** + * @file usbh_hid.h + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _USBH_HID_H +#define _USBH_HID_H + +#include "usb_hid.h" + +struct usbh_hid { + struct usb_setup_packet *setup; + uint8_t intf; /* interface number */ + uint8_t minor; + usbh_epinfo_t intin; /* INTR IN endpoint */ + usbh_epinfo_t intout; /* INTR OUT endpoint */ +}; + +extern const struct usbh_class_driver hid_class_driver; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + #endif \ No newline at end of file diff --git a/class/hub/usbh_hub.c b/class/hub/usbh_hub.c index 5b010c58..8eb875d9 100644 --- a/class/hub/usbh_hub.c +++ b/class/hub/usbh_hub.c @@ -1,497 +1,497 @@ -/** - * @file usbh_hub.c - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#include "usbh_core.h" -#include "usbh_hub.h" - -#define DEV_FORMAT "/dev/hub%d" -#define DEV_NAMELEN 16 - -static uint32_t g_devinuse = 0; - -usb_slist_t hub_class_head = USB_SLIST_OBJECT_INIT(hub_class_head); - -USB_NOCACHE_RAM_SECTION uint8_t int_buffer[6][USBH_HUB_INTIN_BUFSIZE]; -extern void usbh_external_hport_connect(struct usbh_hubport *hport); -extern void usbh_external_hport_disconnect(struct usbh_hubport *hport); -extern void usbh_hport_activate(struct usbh_hubport *hport); -extern void usbh_hport_deactivate(struct usbh_hubport *hport); - -static void usbh_external_hub_callback(void *arg, int nbytes); - -static inline void usbh_hub_register(struct usbh_hub *hub) -{ - usb_slist_add_tail(&hub_class_head, &hub->list); -} - -static inline void usbh_hub_unregister(struct usbh_hub *hub) -{ - usb_slist_remove(&hub_class_head, &hub->list); -} - -/**************************************************************************** - * Name: usbh_hub_devno_alloc - * - * Description: - * Allocate a unique /dev/hub[n] minor number in the range 2-31. - * - ****************************************************************************/ - -static int usbh_hub_devno_alloc(struct usbh_hub *hub) -{ - uint32_t flags; - int devno; - - flags = usb_osal_enter_critical_section(); - for (devno = 2; devno < 32; devno++) { - uint32_t bitno = 1 << devno; - if ((g_devinuse & bitno) == 0) { - g_devinuse |= bitno; - hub->index = devno; - usb_osal_leave_critical_section(flags); - return 0; - } - } - - usb_osal_leave_critical_section(flags); - return -EMFILE; -} - -/**************************************************************************** - * Name: usbh_hub_devno_free - * - * Description: - * Free a /dev/hub[n] minor number so that it can be used. - * - ****************************************************************************/ - -static void usbh_hub_devno_free(struct usbh_hub *hub) -{ - int devno = hub->index; - - if (devno >= 2 && devno < 32) { - uint32_t flags = usb_osal_enter_critical_section(); - g_devinuse &= ~(1 << devno); - usb_osal_leave_critical_section(flags); - } -} - -/**************************************************************************** - * Name: usbh_hub_mkdevname - * - * Description: - * Format a /dev/hub[n] device name given a minor number. - * - ****************************************************************************/ - -static inline void usbh_hub_mkdevname(struct usbh_hub *hub, char *devname) -{ - snprintf(devname, DEV_NAMELEN, DEV_FORMAT, hub->index); -} - -int usbh_hub_get_hub_descriptor(struct usbh_hub *hub, uint8_t *buffer) -{ - struct usb_setup_packet *setup; - - setup = hub->setup; - - setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE; - setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; - setup->wValue = HUB_DESCRIPTOR_TYPE_HUB << 8; - setup->wIndex = 0; - setup->wLength = USB_SIZEOF_HUB_DESC; - - return usbh_control_transfer(hub->parent->ep0, setup, buffer); -} - -int usbh_hub_get_status(struct usbh_hub *hub, uint8_t *buffer) -{ - struct usb_setup_packet *setup; - - setup = hub->setup; - - setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE; - setup->bRequest = HUB_REQUEST_GET_STATUS; - setup->wValue = 0; - setup->wIndex = 0; - setup->wLength = 2; - - return usbh_control_transfer(hub->parent->ep0, setup, buffer); -} - -int usbh_hub_get_portstatus(struct usbh_hub *hub, uint8_t port, struct hub_port_status *port_status) -{ - struct usb_setup_packet *setup; - - setup = hub->setup; - - setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER; - setup->bRequest = HUB_REQUEST_GET_STATUS; - setup->wValue = 0; - setup->wIndex = port; - setup->wLength = 4; - - return usbh_control_transfer(hub->parent->ep0, setup, (uint8_t *)port_status); -} - -int usbh_hub_set_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature) -{ - struct usb_setup_packet *setup; - - setup = hub->setup; - - setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER; - setup->bRequest = HUB_REQUEST_SET_FEATURE; - setup->wValue = feature; - setup->wIndex = port; - setup->wLength = 0; - - return usbh_control_transfer(hub->parent->ep0, setup, NULL); -} - -int usbh_hub_clear_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature) -{ - struct usb_setup_packet *setup; - - setup = hub->setup; - - setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER; - setup->bRequest = HUB_REQUEST_CLEAR_FEATURE; - setup->wValue = feature; - setup->wIndex = port; - setup->wLength = 0; - - return usbh_control_transfer(hub->parent->ep0, setup, NULL); -} - -static int parse_hub_descriptor(struct usb_hub_descriptor *desc, uint16_t length) -{ - if (desc->bLength != USB_SIZEOF_HUB_DESC) { - USB_LOG_ERR("invalid device bLength 0x%02x\r\n", desc->bLength); - return -1; - } else if (desc->bDescriptorType != HUB_DESCRIPTOR_TYPE_HUB) { - USB_LOG_ERR("unexpected descriptor 0x%02x\r\n", desc->bDescriptorType); - return -2; - } else { - USB_LOG_INFO("Device Descriptor:\r\n"); - USB_LOG_INFO("bLength: 0x%02x \r\n", desc->bLength); - USB_LOG_INFO("bDescriptorType: 0x%02x \r\n", desc->bDescriptorType); - USB_LOG_INFO("bNbrPorts: 0x%02x \r\n", desc->bNbrPorts); - USB_LOG_INFO("wHubCharacteristics: 0x%04x \r\n", desc->wHubCharacteristics); - USB_LOG_INFO("bPwrOn2PwrGood: 0x%02x \r\n", desc->bPwrOn2PwrGood); - USB_LOG_INFO("bHubContrCurrent: 0x%02x \r\n", desc->bHubContrCurrent); - USB_LOG_INFO("DeviceRemovable: 0x%02x \r\n", desc->DeviceRemovable); - USB_LOG_INFO("PortPwrCtrlMask: 0x%02x \r\n", desc->PortPwrCtrlMask); - } - return 0; -} - -int usbh_hub_connect(struct usbh_hubport *hport, uint8_t intf) -{ - struct usbh_endpoint_cfg ep_cfg = { 0 }; - struct usb_endpoint_descriptor *ep_desc; - char devname[DEV_NAMELEN]; - int ret; - uint8_t *hub_desc_buffer; - struct usbh_hub *hub_class; - - hub_class = usb_malloc(sizeof(struct usbh_hub)); - if (hub_class == NULL) { - USB_LOG_ERR("Fail to alloc hub_class\r\n"); - return -ENOMEM; - } - memset(hub_class, 0, sizeof(struct usbh_hub)); - hub_class->setup = usb_iomalloc(sizeof(struct usb_setup_packet)); - if (hub_class->setup == NULL) { - USB_LOG_ERR("Fail to alloc setup\r\n"); - return -ENOMEM; - } - hub_class->port_status = usb_iomalloc(sizeof(struct hub_port_status)); - if (hub_class->port_status == NULL) { - USB_LOG_ERR("Fail to alloc port_status\r\n"); - return -ENOMEM; - } - - hub_desc_buffer = usb_iomalloc(32); - - usbh_hub_devno_alloc(hub_class); - usbh_hub_mkdevname(hub_class, devname); - - hub_class->dev_addr = hport->dev_addr; - hub_class->parent = hport; - hport->config.intf[0].priv = hub_class; - - ret = usbh_hub_get_hub_descriptor(hub_class, hub_desc_buffer); - if (ret != 0) { - usb_iofree(hub_desc_buffer); - return ret; - } - - parse_hub_descriptor((struct usb_hub_descriptor *)hub_desc_buffer, USB_SIZEOF_HUB_DESC); - memcpy(&hub_class->hub_desc, hub_desc_buffer, USB_SIZEOF_HUB_DESC); - usb_iofree(hub_desc_buffer); - - hub_class->nports = hub_class->hub_desc.bNbrPorts; - - for (uint8_t port = 1; port <= hub_class->nports; port++) { - hub_class->child[port - 1].port = port; - hub_class->child[port - 1].parent = hub_class; - } - - hub_class->int_buffer = int_buffer[hub_class->index - 2]; - usbh_hub_register(hub_class); - - ep_desc = &hport->config.intf[intf].ep[0].ep_desc; - ep_cfg.ep_addr = ep_desc->bEndpointAddress; - ep_cfg.ep_type = ep_desc->bmAttributes & USB_ENDPOINT_TYPE_MASK; - ep_cfg.ep_mps = ep_desc->wMaxPacketSize; - ep_cfg.ep_interval = ep_desc->bInterval; - ep_cfg.hport = hport; - if (ep_desc->bEndpointAddress & 0x80) { - usbh_ep_alloc(&hub_class->intin, &ep_cfg); - } else { - return -1; - } - - for (uint8_t port = 1; port <= hub_class->nports; port++) { - ret = usbh_hub_set_feature(hub_class, 1, HUB_PORT_FEATURE_POWER); - if (ret < 0) { - return ret; - } - } - - for (uint8_t port = 1; port <= hub_class->nports; port++) { - ret = usbh_hub_get_portstatus(hub_class, port, hub_class->port_status); - USB_LOG_INFO("Port:%d, status:0x%02x, change:0x%02x\r\n", port, hub_class->port_status->wPortStatus, hub_class->port_status->wPortChange); - if (ret < 0) { - return ret; - } - } - - USB_LOG_INFO("Register HUB Class:%s\r\n", devname); - - ret = usbh_ep_intr_async_transfer(hub_class->intin, hub_class->int_buffer, USBH_HUB_INTIN_BUFSIZE, usbh_external_hub_callback, hub_class); - return 0; -} - -int usbh_hub_disconnect(struct usbh_hubport *hport, uint8_t intf) -{ - struct usbh_hubport *child; - char devname[DEV_NAMELEN]; - int ret = 0; - - struct usbh_hub *hub_class = (struct usbh_hub *)hport->config.intf[intf].priv; - - if (hub_class) { - usbh_hub_devno_free(hub_class); - usbh_hub_mkdevname(hub_class, devname); - - if (hub_class->intin) { - ret = usb_ep_cancel(hub_class->intin); - if (ret < 0) { - } - usbh_ep_free(hub_class->intin); - } - - if (hub_class->setup) - usb_iofree(hub_class->setup); - if (hub_class->port_status) - usb_iofree(hub_class->port_status); - - for (uint8_t port = 1; port <= hub_class->nports; port++) { - child = &hub_class->child[port - 1]; - usbh_hport_deactivate(child); - for (uint8_t i = 0; i < child->config.config_desc.bNumInterfaces; i++) { - if (child->config.intf[i].class_driver && child->config.intf[i].class_driver->disconnect) { - ret = CLASS_DISCONNECT(child, i); - } - } - - child->config.config_desc.bNumInterfaces = 0; - child->parent = NULL; - } - - usbh_hub_unregister(hub_class); - usb_free(hub_class); - hport->config.intf[intf].priv = NULL; - - USB_LOG_INFO("Unregister HUB Class:%s\r\n", devname); - } - return ret; -} - -static void usbh_extern_hub_psc_event(void *arg) -{ - struct usbh_hub *hub_class; - struct usbh_hubport *connport; - uint8_t port_change; - uint16_t status; - uint16_t change; - uint16_t mask; - uint16_t feat; - uint32_t flags; - int ret; - - hub_class = (struct usbh_hub *)arg; - - /* Has the hub been disconnected? */ - if (!hub_class->parent->connected) { - return; - } - - port_change = hub_class->int_buffer[0]; - USB_LOG_DBG("port_change:0x%02x\r\n", port_change); - - /* Check for status change on any port */ - for (uint8_t port = USBH_HUB_PORT_START_INDEX; port <= hub_class->nports; port++) { - /* Check if port status has changed */ - if ((port_change & (1 << port)) == 0) { - continue; - } - USB_LOG_DBG("Port %d change\r\n", port); - - /* Port status changed, check what happened */ - port_change &= ~(1 << port); - - /* Read hub port status */ - ret = usbh_hub_get_portstatus(hub_class, port, hub_class->port_status); - if (ret < 0) { - USB_LOG_ERR("Failed to read port:%d status, errorcode: %d\r\n", port, ret); - continue; - } - status = hub_class->port_status->wPortStatus; - change = hub_class->port_status->wPortChange; - - USB_LOG_DBG("Port:%d, status:0x%02x, change:0x%02x\r\n", port, status, change); - - /* First, clear all change bits */ - mask = 1; - feat = HUB_PORT_FEATURE_C_CONNECTION; - while (change) { - if (change & mask) { - ret = usbh_hub_clear_feature(hub_class, port, feat); - if (ret < 0) { - USB_LOG_ERR("Failed to clear port:%d, change mask:%04x, errorcode:%d\r\n", port, mask, ret); - } - change &= (~mask); - } - mask <<= 1; - feat++; - } - - change = hub_class->port_status->wPortChange; - - /* Handle connect or disconnect, no power management */ - if (change & HUB_PORT_STATUS_C_CONNECTION) { - ret = usbh_hub_get_portstatus(hub_class, port, hub_class->port_status); - if (ret < 0) { - USB_LOG_ERR("Failed to read port:%d status, errorcode: %d\r\n", port, ret); - } - status = hub_class->port_status->wPortStatus; - change = hub_class->port_status->wPortChange; - - if (status & HUB_PORT_STATUS_CONNECTION) { - /* Device connected to a port on the hub */ - //USB_LOG_INFO("Connection on port:%d\n", port); - - ret = usbh_hub_set_feature(hub_class, port, HUB_PORT_FEATURE_RESET); - if (ret < 0) { - USB_LOG_ERR("Failed to reset port:%d,errorcode:%d\r\n", port, ret); - continue; - } - - usb_osal_msleep(100); - - ret = usbh_hub_get_portstatus(hub_class, port, hub_class->port_status); - if (ret < 0) { - USB_LOG_ERR("Failed to read port:%d status, errorcode: %d\r\n", port, ret); - continue; - } - status = hub_class->port_status->wPortStatus; - change = hub_class->port_status->wPortChange; - - USB_LOG_DBG("Port:%d, status:0x%02x, change:0x%02x after reset\r\n", port, status, change); - - if ((status & HUB_PORT_STATUS_RESET) == 0 && (status & HUB_PORT_STATUS_ENABLE) != 0) { - if (change & HUB_PORT_STATUS_C_RESET) { - ret = usbh_hub_clear_feature(hub_class, port, HUB_PORT_FEATURE_C_RESET); - if (ret < 0) { - USB_LOG_ERR("Failed to clear port:%d reset change, errorcode: %d\r\n", port, ret); - } - } - connport = &hub_class->child[port - 1]; - - if (status & HUB_PORT_STATUS_HIGH_SPEED) { - connport->speed = USB_SPEED_HIGH; - } else if (status & HUB_PORT_STATUS_LOW_SPEED) { - connport->speed = USB_SPEED_LOW; - } else { - connport->speed = USB_SPEED_FULL; - } - - /* Device connected from a port on the hub, wakeup psc thread. */ - usbh_external_hport_connect(connport); - - } else { - USB_LOG_ERR("Failed to enable port:%d\r\n", port); - continue; - } - } else { - /* Device disconnected from a port on the hub, wakeup psc thread. */ - connport = &hub_class->child[port - 1]; - usbh_external_hport_disconnect(connport); - } - } else { - USB_LOG_WRN("status %04x change %04x not handled\r\n", status, change); - } - } - - /* Check for hub status change */ - - if ((port_change & 1) != 0) { - /* Hub status changed */ - USB_LOG_WRN("Hub status changed, not handled\n"); - } - flags = usb_osal_enter_critical_section(); - if (hub_class->parent->connected) { - ret = usbh_ep_intr_async_transfer(hub_class->intin, hub_class->int_buffer, USBH_HUB_INTIN_BUFSIZE, usbh_external_hub_callback, hub_class); - } - usb_osal_leave_critical_section(flags); -} - -static void usbh_external_hub_callback(void *arg, int nbytes) -{ - struct usbh_hub *hub_class = (struct usbh_hub *)arg; - uint32_t delay = 0; - if (nbytes < 0) { - hub_class->int_buffer[0] = 0; - delay = 100; - } - if (hub_class->parent->connected) { - usb_workqueue_submit(&g_lpworkq, &hub_class->work, usbh_extern_hub_psc_event, (void *)hub_class, delay); - } -} - -const struct usbh_class_driver hub_class_driver = { - .driver_name = "hub", - .connect = usbh_hub_connect, - .disconnect = usbh_hub_disconnect +/** + * @file usbh_hub.c + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#include "usbh_core.h" +#include "usbh_hub.h" + +#define DEV_FORMAT "/dev/hub%d" +#define DEV_NAMELEN 16 + +static uint32_t g_devinuse = 0; + +usb_slist_t hub_class_head = USB_SLIST_OBJECT_INIT(hub_class_head); + +USB_NOCACHE_RAM_SECTION uint8_t int_buffer[6][USBH_HUB_INTIN_BUFSIZE]; +extern void usbh_external_hport_connect(struct usbh_hubport *hport); +extern void usbh_external_hport_disconnect(struct usbh_hubport *hport); +extern void usbh_hport_activate(struct usbh_hubport *hport); +extern void usbh_hport_deactivate(struct usbh_hubport *hport); + +static void usbh_external_hub_callback(void *arg, int nbytes); + +static inline void usbh_hub_register(struct usbh_hub *hub) +{ + usb_slist_add_tail(&hub_class_head, &hub->list); +} + +static inline void usbh_hub_unregister(struct usbh_hub *hub) +{ + usb_slist_remove(&hub_class_head, &hub->list); +} + +/**************************************************************************** + * Name: usbh_hub_devno_alloc + * + * Description: + * Allocate a unique /dev/hub[n] minor number in the range 2-31. + * + ****************************************************************************/ + +static int usbh_hub_devno_alloc(struct usbh_hub *hub) +{ + uint32_t flags; + int devno; + + flags = usb_osal_enter_critical_section(); + for (devno = 2; devno < 32; devno++) { + uint32_t bitno = 1 << devno; + if ((g_devinuse & bitno) == 0) { + g_devinuse |= bitno; + hub->index = devno; + usb_osal_leave_critical_section(flags); + return 0; + } + } + + usb_osal_leave_critical_section(flags); + return -EMFILE; +} + +/**************************************************************************** + * Name: usbh_hub_devno_free + * + * Description: + * Free a /dev/hub[n] minor number so that it can be used. + * + ****************************************************************************/ + +static void usbh_hub_devno_free(struct usbh_hub *hub) +{ + int devno = hub->index; + + if (devno >= 2 && devno < 32) { + uint32_t flags = usb_osal_enter_critical_section(); + g_devinuse &= ~(1 << devno); + usb_osal_leave_critical_section(flags); + } +} + +/**************************************************************************** + * Name: usbh_hub_mkdevname + * + * Description: + * Format a /dev/hub[n] device name given a minor number. + * + ****************************************************************************/ + +static inline void usbh_hub_mkdevname(struct usbh_hub *hub, char *devname) +{ + snprintf(devname, DEV_NAMELEN, DEV_FORMAT, hub->index); +} + +int usbh_hub_get_hub_descriptor(struct usbh_hub *hub, uint8_t *buffer) +{ + struct usb_setup_packet *setup; + + setup = hub->setup; + + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; + setup->wValue = HUB_DESCRIPTOR_TYPE_HUB << 8; + setup->wIndex = 0; + setup->wLength = USB_SIZEOF_HUB_DESC; + + return usbh_control_transfer(hub->parent->ep0, setup, buffer); +} + +int usbh_hub_get_status(struct usbh_hub *hub, uint8_t *buffer) +{ + struct usb_setup_packet *setup; + + setup = hub->setup; + + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = HUB_REQUEST_GET_STATUS; + setup->wValue = 0; + setup->wIndex = 0; + setup->wLength = 2; + + return usbh_control_transfer(hub->parent->ep0, setup, buffer); +} + +int usbh_hub_get_portstatus(struct usbh_hub *hub, uint8_t port, struct hub_port_status *port_status) +{ + struct usb_setup_packet *setup; + + setup = hub->setup; + + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER; + setup->bRequest = HUB_REQUEST_GET_STATUS; + setup->wValue = 0; + setup->wIndex = port; + setup->wLength = 4; + + return usbh_control_transfer(hub->parent->ep0, setup, (uint8_t *)port_status); +} + +int usbh_hub_set_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature) +{ + struct usb_setup_packet *setup; + + setup = hub->setup; + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER; + setup->bRequest = HUB_REQUEST_SET_FEATURE; + setup->wValue = feature; + setup->wIndex = port; + setup->wLength = 0; + + return usbh_control_transfer(hub->parent->ep0, setup, NULL); +} + +int usbh_hub_clear_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature) +{ + struct usb_setup_packet *setup; + + setup = hub->setup; + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER; + setup->bRequest = HUB_REQUEST_CLEAR_FEATURE; + setup->wValue = feature; + setup->wIndex = port; + setup->wLength = 0; + + return usbh_control_transfer(hub->parent->ep0, setup, NULL); +} + +static int parse_hub_descriptor(struct usb_hub_descriptor *desc, uint16_t length) +{ + if (desc->bLength != USB_SIZEOF_HUB_DESC) { + USB_LOG_ERR("invalid device bLength 0x%02x\r\n", desc->bLength); + return -1; + } else if (desc->bDescriptorType != HUB_DESCRIPTOR_TYPE_HUB) { + USB_LOG_ERR("unexpected descriptor 0x%02x\r\n", desc->bDescriptorType); + return -2; + } else { + USB_LOG_INFO("Device Descriptor:\r\n"); + USB_LOG_INFO("bLength: 0x%02x \r\n", desc->bLength); + USB_LOG_INFO("bDescriptorType: 0x%02x \r\n", desc->bDescriptorType); + USB_LOG_INFO("bNbrPorts: 0x%02x \r\n", desc->bNbrPorts); + USB_LOG_INFO("wHubCharacteristics: 0x%04x \r\n", desc->wHubCharacteristics); + USB_LOG_INFO("bPwrOn2PwrGood: 0x%02x \r\n", desc->bPwrOn2PwrGood); + USB_LOG_INFO("bHubContrCurrent: 0x%02x \r\n", desc->bHubContrCurrent); + USB_LOG_INFO("DeviceRemovable: 0x%02x \r\n", desc->DeviceRemovable); + USB_LOG_INFO("PortPwrCtrlMask: 0x%02x \r\n", desc->PortPwrCtrlMask); + } + return 0; +} + +int usbh_hub_connect(struct usbh_hubport *hport, uint8_t intf) +{ + struct usbh_endpoint_cfg ep_cfg = { 0 }; + struct usb_endpoint_descriptor *ep_desc; + char devname[DEV_NAMELEN]; + int ret; + uint8_t *hub_desc_buffer; + struct usbh_hub *hub_class; + + hub_class = usb_malloc(sizeof(struct usbh_hub)); + if (hub_class == NULL) { + USB_LOG_ERR("Fail to alloc hub_class\r\n"); + return -ENOMEM; + } + memset(hub_class, 0, sizeof(struct usbh_hub)); + hub_class->setup = usb_iomalloc(sizeof(struct usb_setup_packet)); + if (hub_class->setup == NULL) { + USB_LOG_ERR("Fail to alloc setup\r\n"); + return -ENOMEM; + } + hub_class->port_status = usb_iomalloc(sizeof(struct hub_port_status)); + if (hub_class->port_status == NULL) { + USB_LOG_ERR("Fail to alloc port_status\r\n"); + return -ENOMEM; + } + + hub_desc_buffer = usb_iomalloc(32); + + usbh_hub_devno_alloc(hub_class); + usbh_hub_mkdevname(hub_class, devname); + + hub_class->dev_addr = hport->dev_addr; + hub_class->parent = hport; + hport->config.intf[0].priv = hub_class; + + ret = usbh_hub_get_hub_descriptor(hub_class, hub_desc_buffer); + if (ret != 0) { + usb_iofree(hub_desc_buffer); + return ret; + } + + parse_hub_descriptor((struct usb_hub_descriptor *)hub_desc_buffer, USB_SIZEOF_HUB_DESC); + memcpy(&hub_class->hub_desc, hub_desc_buffer, USB_SIZEOF_HUB_DESC); + usb_iofree(hub_desc_buffer); + + hub_class->nports = hub_class->hub_desc.bNbrPorts; + + for (uint8_t port = 1; port <= hub_class->nports; port++) { + hub_class->child[port - 1].port = port; + hub_class->child[port - 1].parent = hub_class; + } + + hub_class->int_buffer = int_buffer[hub_class->index - 2]; + usbh_hub_register(hub_class); + + ep_desc = &hport->config.intf[intf].ep[0].ep_desc; + ep_cfg.ep_addr = ep_desc->bEndpointAddress; + ep_cfg.ep_type = ep_desc->bmAttributes & USB_ENDPOINT_TYPE_MASK; + ep_cfg.ep_mps = ep_desc->wMaxPacketSize; + ep_cfg.ep_interval = ep_desc->bInterval; + ep_cfg.hport = hport; + if (ep_desc->bEndpointAddress & 0x80) { + usbh_ep_alloc(&hub_class->intin, &ep_cfg); + } else { + return -1; + } + + for (uint8_t port = 1; port <= hub_class->nports; port++) { + ret = usbh_hub_set_feature(hub_class, 1, HUB_PORT_FEATURE_POWER); + if (ret < 0) { + return ret; + } + } + + for (uint8_t port = 1; port <= hub_class->nports; port++) { + ret = usbh_hub_get_portstatus(hub_class, port, hub_class->port_status); + USB_LOG_INFO("Port:%d, status:0x%02x, change:0x%02x\r\n", port, hub_class->port_status->wPortStatus, hub_class->port_status->wPortChange); + if (ret < 0) { + return ret; + } + } + + USB_LOG_INFO("Register HUB Class:%s\r\n", devname); + + ret = usbh_ep_intr_async_transfer(hub_class->intin, hub_class->int_buffer, USBH_HUB_INTIN_BUFSIZE, usbh_external_hub_callback, hub_class); + return 0; +} + +int usbh_hub_disconnect(struct usbh_hubport *hport, uint8_t intf) +{ + struct usbh_hubport *child; + char devname[DEV_NAMELEN]; + int ret = 0; + + struct usbh_hub *hub_class = (struct usbh_hub *)hport->config.intf[intf].priv; + + if (hub_class) { + usbh_hub_devno_free(hub_class); + usbh_hub_mkdevname(hub_class, devname); + + if (hub_class->intin) { + ret = usb_ep_cancel(hub_class->intin); + if (ret < 0) { + } + usbh_ep_free(hub_class->intin); + } + + if (hub_class->setup) + usb_iofree(hub_class->setup); + if (hub_class->port_status) + usb_iofree(hub_class->port_status); + + for (uint8_t port = 1; port <= hub_class->nports; port++) { + child = &hub_class->child[port - 1]; + usbh_hport_deactivate(child); + for (uint8_t i = 0; i < child->config.config_desc.bNumInterfaces; i++) { + if (child->config.intf[i].class_driver && child->config.intf[i].class_driver->disconnect) { + ret = CLASS_DISCONNECT(child, i); + } + } + + child->config.config_desc.bNumInterfaces = 0; + child->parent = NULL; + } + + usbh_hub_unregister(hub_class); + usb_free(hub_class); + hport->config.intf[intf].priv = NULL; + + USB_LOG_INFO("Unregister HUB Class:%s\r\n", devname); + } + return ret; +} + +static void usbh_extern_hub_psc_event(void *arg) +{ + struct usbh_hub *hub_class; + struct usbh_hubport *connport; + uint8_t port_change; + uint16_t status; + uint16_t change; + uint16_t mask; + uint16_t feat; + uint32_t flags; + int ret; + + hub_class = (struct usbh_hub *)arg; + + /* Has the hub been disconnected? */ + if (!hub_class->parent->connected) { + return; + } + + port_change = hub_class->int_buffer[0]; + USB_LOG_DBG("port_change:0x%02x\r\n", port_change); + + /* Check for status change on any port */ + for (uint8_t port = USBH_HUB_PORT_START_INDEX; port <= hub_class->nports; port++) { + /* Check if port status has changed */ + if ((port_change & (1 << port)) == 0) { + continue; + } + USB_LOG_DBG("Port %d change\r\n", port); + + /* Port status changed, check what happened */ + port_change &= ~(1 << port); + + /* Read hub port status */ + ret = usbh_hub_get_portstatus(hub_class, port, hub_class->port_status); + if (ret < 0) { + USB_LOG_ERR("Failed to read port:%d status, errorcode: %d\r\n", port, ret); + continue; + } + status = hub_class->port_status->wPortStatus; + change = hub_class->port_status->wPortChange; + + USB_LOG_DBG("Port:%d, status:0x%02x, change:0x%02x\r\n", port, status, change); + + /* First, clear all change bits */ + mask = 1; + feat = HUB_PORT_FEATURE_C_CONNECTION; + while (change) { + if (change & mask) { + ret = usbh_hub_clear_feature(hub_class, port, feat); + if (ret < 0) { + USB_LOG_ERR("Failed to clear port:%d, change mask:%04x, errorcode:%d\r\n", port, mask, ret); + } + change &= (~mask); + } + mask <<= 1; + feat++; + } + + change = hub_class->port_status->wPortChange; + + /* Handle connect or disconnect, no power management */ + if (change & HUB_PORT_STATUS_C_CONNECTION) { + ret = usbh_hub_get_portstatus(hub_class, port, hub_class->port_status); + if (ret < 0) { + USB_LOG_ERR("Failed to read port:%d status, errorcode: %d\r\n", port, ret); + } + status = hub_class->port_status->wPortStatus; + change = hub_class->port_status->wPortChange; + + if (status & HUB_PORT_STATUS_CONNECTION) { + /* Device connected to a port on the hub */ + //USB_LOG_INFO("Connection on port:%d\n", port); + + ret = usbh_hub_set_feature(hub_class, port, HUB_PORT_FEATURE_RESET); + if (ret < 0) { + USB_LOG_ERR("Failed to reset port:%d,errorcode:%d\r\n", port, ret); + continue; + } + + usb_osal_msleep(100); + + ret = usbh_hub_get_portstatus(hub_class, port, hub_class->port_status); + if (ret < 0) { + USB_LOG_ERR("Failed to read port:%d status, errorcode: %d\r\n", port, ret); + continue; + } + status = hub_class->port_status->wPortStatus; + change = hub_class->port_status->wPortChange; + + USB_LOG_DBG("Port:%d, status:0x%02x, change:0x%02x after reset\r\n", port, status, change); + + if ((status & HUB_PORT_STATUS_RESET) == 0 && (status & HUB_PORT_STATUS_ENABLE) != 0) { + if (change & HUB_PORT_STATUS_C_RESET) { + ret = usbh_hub_clear_feature(hub_class, port, HUB_PORT_FEATURE_C_RESET); + if (ret < 0) { + USB_LOG_ERR("Failed to clear port:%d reset change, errorcode: %d\r\n", port, ret); + } + } + connport = &hub_class->child[port - 1]; + + if (status & HUB_PORT_STATUS_HIGH_SPEED) { + connport->speed = USB_SPEED_HIGH; + } else if (status & HUB_PORT_STATUS_LOW_SPEED) { + connport->speed = USB_SPEED_LOW; + } else { + connport->speed = USB_SPEED_FULL; + } + + /* Device connected from a port on the hub, wakeup psc thread. */ + usbh_external_hport_connect(connport); + + } else { + USB_LOG_ERR("Failed to enable port:%d\r\n", port); + continue; + } + } else { + /* Device disconnected from a port on the hub, wakeup psc thread. */ + connport = &hub_class->child[port - 1]; + usbh_external_hport_disconnect(connport); + } + } else { + USB_LOG_WRN("status %04x change %04x not handled\r\n", status, change); + } + } + + /* Check for hub status change */ + + if ((port_change & 1) != 0) { + /* Hub status changed */ + USB_LOG_WRN("Hub status changed, not handled\n"); + } + flags = usb_osal_enter_critical_section(); + if (hub_class->parent->connected) { + ret = usbh_ep_intr_async_transfer(hub_class->intin, hub_class->int_buffer, USBH_HUB_INTIN_BUFSIZE, usbh_external_hub_callback, hub_class); + } + usb_osal_leave_critical_section(flags); +} + +static void usbh_external_hub_callback(void *arg, int nbytes) +{ + struct usbh_hub *hub_class = (struct usbh_hub *)arg; + uint32_t delay = 0; + if (nbytes < 0) { + hub_class->int_buffer[0] = 0; + delay = 100; + } + if (hub_class->parent->connected) { + usb_workqueue_submit(&g_lpworkq, &hub_class->work, usbh_extern_hub_psc_event, (void *)hub_class, delay); + } +} + +const struct usbh_class_driver hub_class_driver = { + .driver_name = "hub", + .connect = usbh_hub_connect, + .disconnect = usbh_hub_disconnect }; \ No newline at end of file diff --git a/class/hub/usbh_hub.h b/class/hub/usbh_hub.h index 98c46542..ee0df9e3 100644 --- a/class/hub/usbh_hub.h +++ b/class/hub/usbh_hub.h @@ -1,42 +1,42 @@ -/** - * @file usbh_hub.h - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#ifndef _USBH_HUB_H_ -#define _USBH_HUB_H_ - -#include "usb_hub.h" - -#define USBH_HUB_MAX_PORTS 4 -/* Maximum size of an interrupt IN transfer */ -#define USBH_HUB_INTIN_BUFSIZE ((USBH_HUB_MAX_PORTS + 8) >> 3) - -extern const struct usbh_class_driver hub_class_driver; -extern usb_slist_t hub_class_head; -extern usb_osal_thread_t hub_thread; -#ifdef __cplusplus -extern "C" { -#endif -int usbh_hub_initialize(void); -#ifdef __cplusplus -} -#endif - -#endif /* _USBH_HUB_H_ */ +/** + * @file usbh_hub.h + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _USBH_HUB_H_ +#define _USBH_HUB_H_ + +#include "usb_hub.h" + +#define USBH_HUB_MAX_PORTS 4 +/* Maximum size of an interrupt IN transfer */ +#define USBH_HUB_INTIN_BUFSIZE ((USBH_HUB_MAX_PORTS + 8) >> 3) + +extern const struct usbh_class_driver hub_class_driver; +extern usb_slist_t hub_class_head; +extern usb_osal_thread_t hub_thread; +#ifdef __cplusplus +extern "C" { +#endif +int usbh_hub_initialize(void); +#ifdef __cplusplus +} +#endif + +#endif /* _USBH_HUB_H_ */ diff --git a/class/msc/usbd_msc.c b/class/msc/usbd_msc.c index 563888d9..3f88cdd5 100644 --- a/class/msc/usbd_msc.c +++ b/class/msc/usbd_msc.c @@ -1,992 +1,992 @@ -/** - * @file usbd_msc.c - * @brief - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#include "usbd_core.h" -#include "usbd_msc.h" -#include "usb_scsi.h" - -/* max USB packet size */ -#ifndef CONFIG_USB_HS -#define MASS_STORAGE_BULK_EP_MPS 64 -#else -#define MASS_STORAGE_BULK_EP_MPS 512 -#endif - -#define MSD_OUT_EP_IDX 0 -#define MSD_IN_EP_IDX 1 - -/* Describe EndPoints configuration */ -static usbd_endpoint_t mass_ep_data[2]; - -/* MSC Bulk-only Stage */ -enum Stage { - MSC_READ_CBW = 0, /* Command Block Wrapper */ - MSC_DATA_OUT = 1, /* Data Out Phase */ - MSC_DATA_IN = 2, /* Data In Phase */ - MSC_SEND_CSW = 3, /* Command Status Wrapper */ - MSC_WAIT_CSW = 4, /* Command Status Wrapper */ -}; - -/* Device data structure */ -struct usbd_msc_cfg_private { - /* state of the bulk-only state machine */ - enum Stage stage; - struct CBW cbw; - struct CSW csw; - - uint8_t sKey; /* Sense key */ - uint8_t ASC; /* Additional Sense Code */ - uint8_t ASQ; /* Additional Sense Qualifier */ - uint8_t max_lun; - uint16_t scsi_blk_size; - uint32_t scsi_blk_nbr; - - uint32_t scsi_blk_addr; - uint32_t scsi_blk_len; - uint8_t *block_buffer; - -} usbd_msc_cfg; - -/*memory OK (after a usbd_msc_memory_verify)*/ -static bool memOK; - -static void usbd_msc_reset(void) -{ - usbd_msc_cfg.stage = MSC_READ_CBW; - usbd_msc_cfg.scsi_blk_addr = 0U; - usbd_msc_cfg.scsi_blk_len = 0U; - usbd_msc_cfg.max_lun = 0; - usbd_msc_cfg.sKey = 0; - usbd_msc_cfg.ASC = 0; - usbd_msc_cfg.ASQ = 0; - - (void)memset((void *)&usbd_msc_cfg.cbw, 0, sizeof(struct CBW)); - (void)memset((void *)&usbd_msc_cfg.csw, 0, sizeof(struct CSW)); - - usbd_msc_get_cap(0, &usbd_msc_cfg.scsi_blk_nbr, &usbd_msc_cfg.scsi_blk_size); - - if (usbd_msc_cfg.block_buffer) { - free(usbd_msc_cfg.block_buffer); - } - usbd_msc_cfg.block_buffer = malloc(usbd_msc_cfg.scsi_blk_size * sizeof(uint8_t)); - memset(usbd_msc_cfg.block_buffer, 0, usbd_msc_cfg.scsi_blk_size * sizeof(uint8_t)); -} - -/** - * @brief Handler called for Class requests not handled by the USB stack. - * - * @param setup Information about the request to execute. - * @param len Size of the buffer. - * @param data Buffer containing the request result. - * - * @return 0 on success, negative errno code on fail. - */ -static int msc_storage_class_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) -{ - USB_LOG_DBG("MSC Class request: " - "bRequest 0x%02x\r\n", - setup->bRequest); - - switch (setup->bRequest) { - case MSC_REQUEST_RESET: - USB_LOG_DBG("MSC_REQUEST_RESET\r\n"); - - if (setup->wLength) { - USB_LOG_WRN("Invalid length\r\n"); - return -1; - } - - usbd_msc_reset(); - break; - - case MSC_REQUEST_GET_MAX_LUN: - USB_LOG_DBG("MSC_REQUEST_GET_MAX_LUN\r\n"); - - if (setup->wLength != 1) { - USB_LOG_WRN("Invalid length\r\n"); - return -1; - } - - *data = (uint8_t *)(&usbd_msc_cfg.max_lun); - *len = 1; - break; - - default: - USB_LOG_WRN("Unhandled MSC Class bRequest 0x%02x\r\n", setup->bRequest); - return -1; - } - - return 0; -} - -static void usbd_msc_bot_abort(void) -{ - if ((usbd_msc_cfg.cbw.bmFlags == 0) && (usbd_msc_cfg.cbw.dDataLength != 0)) { - usbd_ep_set_stall(mass_ep_data[MSD_OUT_EP_IDX].ep_addr); - } - usbd_ep_set_stall(mass_ep_data[MSD_IN_EP_IDX].ep_addr); -} - -static void sendCSW(uint8_t CSW_Status) -{ - usbd_msc_cfg.csw.dSignature = MSC_CSW_Signature; - usbd_msc_cfg.csw.bStatus = CSW_Status; - - /* updating the State Machine , so that we wait CSW when this - * transfer is complete, ie when we get a bulk in callback - */ - usbd_msc_cfg.stage = MSC_WAIT_CSW; - - if (usbd_ep_write(mass_ep_data[MSD_IN_EP_IDX].ep_addr, (uint8_t *)&usbd_msc_cfg.csw, - sizeof(struct CSW), NULL) != 0) { - USB_LOG_ERR("usb write failure\r\n"); - } -} - -static void sendLastData(uint8_t *buffer, uint8_t size) -{ - size = MIN(size, usbd_msc_cfg.cbw.dDataLength); - - /* updating the State Machine , so that we send CSW when this - * transfer is complete, ie when we get a bulk in callback - */ - usbd_msc_cfg.stage = MSC_SEND_CSW; - - if (usbd_ep_write(mass_ep_data[MSD_IN_EP_IDX].ep_addr, buffer, size, NULL) != 0) { - USB_LOG_ERR("USB write failed\r\n"); - } - - usbd_msc_cfg.csw.dDataResidue -= size; - usbd_msc_cfg.csw.bStatus = CSW_STATUS_CMD_PASSED; -} - -/** - * @brief SCSI COMMAND - */ -static bool SCSI_testUnitReady(uint8_t **data, uint32_t *len); -static bool SCSI_requestSense(uint8_t **data, uint32_t *len); -static bool SCSI_inquiry(uint8_t **data, uint32_t *len); -static bool SCSI_startStopUnit(uint8_t **data, uint32_t *len); -static bool SCSI_preventAllowMediaRemoval(uint8_t **data, uint32_t *len); -static bool SCSI_modeSense6(uint8_t **data, uint32_t *len); -static bool SCSI_modeSense10(uint8_t **data, uint32_t *len); -static bool SCSI_readFormatCapacity(uint8_t **data, uint32_t *len); -static bool SCSI_readCapacity10(uint8_t **data, uint32_t *len); -static bool SCSI_read10(uint8_t **data, uint32_t *len); -static bool SCSI_read12(uint8_t **data, uint32_t *len); -static bool SCSI_write10(uint8_t **data, uint32_t *len); -static bool SCSI_write12(uint8_t **data, uint32_t *len); -static bool SCSI_verify10(uint8_t **data, uint32_t *len); - -/** -* @brief SCSI_SenseCode -* Load the last error code in the error list -* @param sKey: Sense Key -* @param ASC: Additional Sense Code -* @retval none - -*/ -static void SCSI_SenseCode(uint8_t sKey, uint8_t ASC) -{ - usbd_msc_cfg.sKey = sKey; - usbd_msc_cfg.ASC = ASC; -} - -static bool SCSI_processRead(void) -{ - uint32_t transfer_len; - - USB_LOG_DBG("read addr:%d\r\n", usbd_msc_cfg.scsi_blk_addr); - - transfer_len = MIN(usbd_msc_cfg.scsi_blk_len, MASS_STORAGE_BULK_EP_MPS); - - /* we read an entire block */ - if (!(usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size)) { - if (usbd_msc_sector_read((usbd_msc_cfg.scsi_blk_addr / usbd_msc_cfg.scsi_blk_size), usbd_msc_cfg.block_buffer, usbd_msc_cfg.scsi_blk_size) != 0) { - SCSI_SenseCode(SCSI_SENSE_HARDWARE_ERROR, SCSI_ASC_UNRECOVERED_READ_ERROR); - return false; - } - } - - usbd_ep_write(mass_ep_data[MSD_IN_EP_IDX].ep_addr, - &usbd_msc_cfg.block_buffer[usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size], transfer_len, NULL); - - usbd_msc_cfg.scsi_blk_addr += transfer_len; - usbd_msc_cfg.scsi_blk_len -= transfer_len; - usbd_msc_cfg.csw.dDataResidue -= transfer_len; - - if (usbd_msc_cfg.scsi_blk_len == 0) { - usbd_msc_cfg.stage = MSC_SEND_CSW; - } - - return true; -} - -static bool SCSI_processWrite() -{ - uint32_t bytes_read; - USB_LOG_DBG("write addr:%d\r\n", usbd_msc_cfg.scsi_blk_addr); - - /* we fill an array in RAM of 1 block before writing it in memory */ - usbd_ep_read(mass_ep_data[MSD_OUT_EP_IDX].ep_addr, &usbd_msc_cfg.block_buffer[usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size], MASS_STORAGE_BULK_EP_MPS, - &bytes_read); - - /* if the array is filled, write it in memory */ - if ((usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size) + bytes_read >= usbd_msc_cfg.scsi_blk_size) { - if (usbd_msc_sector_write((usbd_msc_cfg.scsi_blk_addr / usbd_msc_cfg.scsi_blk_size), usbd_msc_cfg.block_buffer, usbd_msc_cfg.scsi_blk_size) != 0) { - SCSI_SenseCode(SCSI_SENSE_HARDWARE_ERROR, SCSI_ASC_WRITE_FAULT); - return false; - } - } - - usbd_msc_cfg.scsi_blk_addr += bytes_read; - usbd_msc_cfg.scsi_blk_len -= bytes_read; - usbd_msc_cfg.csw.dDataResidue -= bytes_read; - - if (usbd_msc_cfg.scsi_blk_len == 0) { - sendCSW(CSW_STATUS_CMD_PASSED); - } - - return true; -} - -static bool SCSI_processVerify() -{ -#if 0 - uint32_t bytes_read; - uint8_t out_buffer[MASS_STORAGE_BULK_EP_MPS]; - - USB_LOG_DBG("verify addr:%d\r\n", usbd_msc_cfg.scsi_blk_addr); - - /* we fill an array in RAM of 1 block before writing it in memory */ - usbd_ep_read(mass_ep_data[MSD_OUT_EP_IDX].ep_addr, out_buffer, MASS_STORAGE_BULK_EP_MPS, - &bytes_read); - - /* we read an entire block */ - if (!(usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size)) { - if (usbd_msc_sector_read((usbd_msc_cfg.scsi_blk_addr / usbd_msc_cfg.scsi_blk_size), usbd_msc_cfg.block_buffer, usbd_msc_cfg.scsi_blk_size) != 0) { - SCSI_SenseCode(SCSI_SENSE_HARDWARE_ERROR, SCSI_ASC_UNRECOVERED_READ_ERROR); - return false; - } - } - - /* info are in RAM -> no need to re-read memory */ - for (uint16_t i = 0U; i < bytes_read; i++) { - if (usbd_msc_cfg.block_buffer[usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size + i] != out_buffer[i]) { - USB_LOG_DBG("Mismatch sector %d offset %d", - usbd_msc_cfg.scsi_blk_addr / usbd_msc_cfg.scsi_blk_size, i); - memOK = false; - break; - } - } - - usbd_msc_cfg.scsi_blk_addr += bytes_read; - usbd_msc_cfg.scsi_blk_len -= bytes_read; - usbd_msc_cfg.csw.dDataResidue -= bytes_read; - - if (usbd_msc_cfg.scsi_blk_len == 0) { - sendCSW(CSW_STATUS_CMD_PASSED); - } -#endif - return true; -} - -static bool SCSI_CBWDecode() -{ - uint8_t *buf2send = usbd_msc_cfg.block_buffer; - uint32_t len2send = 0; - uint32_t bytes_read; - bool ret = false; - - usbd_ep_read(mass_ep_data[MSD_OUT_EP_IDX].ep_addr, (uint8_t *)&usbd_msc_cfg.cbw, USB_SIZEOF_MSC_CBW, - &bytes_read); - - if (bytes_read != sizeof(struct CBW)) { - USB_LOG_ERR("size != sizeof(cbw)\r\n"); - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); - return false; - } - - usbd_msc_cfg.csw.dTag = usbd_msc_cfg.cbw.dTag; - usbd_msc_cfg.csw.dDataResidue = usbd_msc_cfg.cbw.dDataLength; - - if ((usbd_msc_cfg.cbw.bLUN > 1) || (usbd_msc_cfg.cbw.dSignature != MSC_CBW_Signature) || (usbd_msc_cfg.cbw.bCBLength < 1) || (usbd_msc_cfg.cbw.bCBLength > 16)) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); - return false; - } else { - switch (usbd_msc_cfg.cbw.CB[0]) { - case SCSI_TEST_UNIT_READY: - ret = SCSI_testUnitReady(&buf2send, &len2send); - break; - case SCSI_REQUEST_SENSE: - ret = SCSI_requestSense(&buf2send, &len2send); - break; - case SCSI_INQUIRY: - ret = SCSI_inquiry(&buf2send, &len2send); - break; - case SCSI_START_STOP_UNIT: - ret = SCSI_startStopUnit(&buf2send, &len2send); - break; - case SCSI_PREVENT_ALLOW_MEDIA_REMOVAL: - ret = SCSI_preventAllowMediaRemoval(&buf2send, &len2send); - break; - case SCSI_MODE_SENSE6: - ret = SCSI_modeSense6(&buf2send, &len2send); - break; - case SCSI_MODE_SENSE10: - ret = SCSI_modeSense10(&buf2send, &len2send); - break; - case SCSI_READ_FORMAT_CAPACITIES: - ret = SCSI_readFormatCapacity(&buf2send, &len2send); - break; - case SCSI_READ_CAPACITY10: - ret = SCSI_readCapacity10(&buf2send, &len2send); - break; - case SCSI_READ10: - ret = SCSI_read10(NULL, 0); - break; - case SCSI_READ12: - ret = SCSI_read12(NULL, 0); - break; - case SCSI_WRITE10: - ret = SCSI_write10(NULL, 0); - break; - case SCSI_WRITE12: - ret = SCSI_write12(NULL, 0); - break; - case SCSI_VERIFY10: - ret = SCSI_verify10(NULL, 0); - break; - - default: - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); - USB_LOG_WRN("unsupported cmd:0x%02x\r\n", usbd_msc_cfg.cbw.CB[0]); - ret = false; - break; - } - } - if (ret) { - if (usbd_msc_cfg.stage == MSC_READ_CBW) { - if (len2send) { - sendLastData(buf2send, len2send); - } else { - sendCSW(CSW_STATUS_CMD_PASSED); - } - } - } - return ret; -} - -static bool SCSI_testUnitReady(uint8_t **data, uint32_t *len) -{ - if (usbd_msc_cfg.cbw.dDataLength != 0U) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); - return false; - } - *data = NULL; - *len = 0; - return true; -} - -static bool SCSI_requestSense(uint8_t **data, uint32_t *len) -{ - uint8_t data_len = REQUEST_SENSE_DATA_LEN; - if (usbd_msc_cfg.cbw.dDataLength == 0U) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); - return false; - } - - if (usbd_msc_cfg.cbw.CB[4] < REQUEST_SENSE_DATA_LEN) { - data_len = usbd_msc_cfg.cbw.CB[4]; - } - - uint8_t request_sense[REQUEST_SENSE_DATA_LEN] = { - 0x70, - 0x00, - 0x00, /* Sense Key */ - 0x00, - 0x00, - 0x00, - 0x00, - REQUEST_SENSE_DATA_LEN - 8, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, /* Additional Sense Code */ - 0x00, /* Additional Sense Request */ - 0x00, - 0x00, - 0x00, - 0x00, - }; - - request_sense[2] = usbd_msc_cfg.sKey; - request_sense[12] = usbd_msc_cfg.ASC; - request_sense[13] = usbd_msc_cfg.ASQ; -#if 0 - request_sense[ 2] = 0x06; /* UNIT ATTENTION */ - request_sense[12] = 0x28; /* Additional Sense Code: Not ready to ready transition */ - request_sense[13] = 0x00; /* Additional Sense Code Qualifier */ -#endif -#if 0 - request_sense[ 2] = 0x02; /* NOT READY */ - request_sense[12] = 0x3A; /* Additional Sense Code: Medium not present */ - request_sense[13] = 0x00; /* Additional Sense Code Qualifier */ -#endif -#if 0 - request_sense[ 2] = 0x05; /* ILLEGAL REQUEST */ - request_sense[12] = 0x20; /* Additional Sense Code: Invalid command */ - request_sense[13] = 0x00; /* Additional Sense Code Qualifier */ -#endif -#if 0 - request_sense[ 2] = 0x00; /* NO SENSE */ - request_sense[12] = 0x00; /* Additional Sense Code: No additional code */ - request_sense[13] = 0x00; /* Additional Sense Code Qualifier */ -#endif - - memcpy(*data, (uint8_t *)request_sense, data_len); - *len = data_len; - return true; -} - -static bool SCSI_inquiry(uint8_t **data, uint32_t *len) -{ - uint8_t data_len = STANDARD_INQUIRY_DATA_LEN; - - uint8_t inquiry00[6] = { - 0x00, - 0x00, - 0x00, - (0x06 - 4U), - 0x00, - 0x80 - }; - - /* USB Mass storage VPD Page 0x80 Inquiry Data for Unit Serial Number */ - uint8_t inquiry80[8] = { - 0x00, - 0x80, - 0x00, - 0x08, - 0x20, /* Put Product Serial number */ - 0x20, - 0x20, - 0x20 - }; - - uint8_t inquiry[STANDARD_INQUIRY_DATA_LEN] = { - /* 36 */ - - /* LUN 0 */ - 0x00, - 0x80, - 0x02, - 0x02, - (STANDARD_INQUIRY_DATA_LEN - 5), - 0x00, - 0x00, - 0x00, - 'B', 'o', 'u', 'f', 'f', 'a', 'l', 'o', /* Manufacturer : 8 bytes */ - 'P', 'r', 'o', 'd', 'u', 'c', 't', ' ', /* Product : 16 Bytes */ - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', - '0', '.', '0', '1' /* Version : 4 Bytes */ - }; - - if (usbd_msc_cfg.cbw.dDataLength == 0U) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); - return false; - } - - if ((usbd_msc_cfg.cbw.CB[1] & 0x01U) != 0U) { /* Evpd is set */ - if (usbd_msc_cfg.cbw.CB[2] == 0U) { /* Request for Supported Vital Product Data Pages*/ - data_len = 0x06; - memcpy(*data, (uint8_t *)inquiry00, data_len); - } else if (usbd_msc_cfg.cbw.CB[2] == 0x80U) { /* Request for VPD page 0x80 Unit Serial Number */ - data_len = 0x08; - memcpy(*data, (uint8_t *)inquiry80, data_len); - } else { /* Request Not supported */ - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_FIELED_IN_COMMAND); - return false; - } - } else { - if (usbd_msc_cfg.cbw.CB[4] < STANDARD_INQUIRY_DATA_LEN) { - data_len = usbd_msc_cfg.cbw.CB[4]; - } - memcpy(*data, (uint8_t *)inquiry, data_len); - } - - *len = data_len; - return true; -} - -static bool SCSI_startStopUnit(uint8_t **data, uint32_t *len) -{ - if (usbd_msc_cfg.cbw.dDataLength != 0U) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); - return false; - } - - if ((usbd_msc_cfg.cbw.CB[4] & 0x3U) == 0x1U) /* START=1 */ - { - //SCSI_MEDIUM_UNLOCKED; - } else if ((usbd_msc_cfg.cbw.CB[4] & 0x3U) == 0x2U) /* START=0 and LOEJ Load Eject=1 */ - { - //SCSI_MEDIUM_EJECTED; - } else if ((usbd_msc_cfg.cbw.CB[4] & 0x3U) == 0x3U) /* START=1 and LOEJ Load Eject=1 */ - { - //SCSI_MEDIUM_UNLOCKED; - } else { - } - - *data = NULL; - *len = 0; - return true; -} - -static bool SCSI_preventAllowMediaRemoval(uint8_t **data, uint32_t *len) -{ - if (usbd_msc_cfg.cbw.dDataLength != 0U) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); - return false; - } - if (usbd_msc_cfg.cbw.CB[4] == 0U) { - //SCSI_MEDIUM_UNLOCKED; - } else { - //SCSI_MEDIUM_LOCKED; - } - *data = NULL; - *len = 0; - return true; -} - -static bool SCSI_modeSense6(uint8_t **data, uint32_t *len) -{ - uint8_t data_len = 4; - if (usbd_msc_cfg.cbw.dDataLength == 0U) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); - return false; - } - if (usbd_msc_cfg.cbw.CB[4] < MODE_SENSE6_DATA_LEN) { - data_len = usbd_msc_cfg.cbw.CB[4]; - } - - uint8_t sense6[] = { 0x03, 0x00, 0x00, 0x00 }; - - memcpy(*data, (uint8_t *)sense6, data_len); - *len = data_len; - return true; -} - -static bool SCSI_modeSense10(uint8_t **data, uint32_t *len) -{ - uint8_t data_len = MODE_SENSE10_DATA_LEN; - if (usbd_msc_cfg.cbw.dDataLength == 0U) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); - return false; - } - - if (usbd_msc_cfg.cbw.CB[8] < MODE_SENSE10_DATA_LEN) { - data_len = usbd_msc_cfg.cbw.CB[8]; - } - - uint8_t sense10[MODE_SENSE10_DATA_LEN] = { - 0x00, - 0x26, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x08, - 0x12, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00 - }; - - memcpy(*data, (uint8_t *)sense10, data_len); - *len = data_len; - return true; -} - -static bool SCSI_readFormatCapacity(uint8_t **data, uint32_t *len) -{ - if (usbd_msc_cfg.cbw.dDataLength == 0U) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); - return false; - } - uint8_t format_capacity[READ_FORMAT_CAPACITY_DATA_LEN] = { - 0x00, - 0x00, - 0x00, - 0x08, /* Capacity List Length */ - (uint8_t)((usbd_msc_cfg.scsi_blk_nbr >> 24) & 0xff), - (uint8_t)((usbd_msc_cfg.scsi_blk_nbr >> 16) & 0xff), - (uint8_t)((usbd_msc_cfg.scsi_blk_nbr >> 8) & 0xff), - (uint8_t)((usbd_msc_cfg.scsi_blk_nbr >> 0) & 0xff), - - 0x02, /* Descriptor Code: Formatted Media */ - 0x00, - (uint8_t)((usbd_msc_cfg.scsi_blk_size >> 8) & 0xff), - (uint8_t)((usbd_msc_cfg.scsi_blk_size >> 0) & 0xff), - }; - - memcpy(*data, (uint8_t *)format_capacity, READ_FORMAT_CAPACITY_DATA_LEN); - *len = READ_FORMAT_CAPACITY_DATA_LEN; - return true; -} - -static bool SCSI_readCapacity10(uint8_t **data, uint32_t *len) -{ - if (usbd_msc_cfg.cbw.dDataLength == 0U) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); - return false; - } - - uint8_t capacity10[READ_CAPACITY10_DATA_LEN] = { - (uint8_t)(((usbd_msc_cfg.scsi_blk_nbr - 1) >> 24) & 0xff), - (uint8_t)(((usbd_msc_cfg.scsi_blk_nbr - 1) >> 16) & 0xff), - (uint8_t)(((usbd_msc_cfg.scsi_blk_nbr - 1) >> 8) & 0xff), - (uint8_t)(((usbd_msc_cfg.scsi_blk_nbr - 1) >> 0) & 0xff), - - (uint8_t)((usbd_msc_cfg.scsi_blk_size >> 24) & 0xff), - (uint8_t)((usbd_msc_cfg.scsi_blk_size >> 16) & 0xff), - (uint8_t)((usbd_msc_cfg.scsi_blk_size >> 8) & 0xff), - (uint8_t)((usbd_msc_cfg.scsi_blk_size >> 0) & 0xff), - }; - - memcpy(*data, (uint8_t *)&capacity10, READ_CAPACITY10_DATA_LEN); - *len = READ_CAPACITY10_DATA_LEN; - return true; -} - -static bool SCSI_read10(uint8_t **data, uint32_t *len) -{ - /* Logical Block Address of First Block */ - uint32_t lba = 0; - uint32_t blk_num = 0; - if (((usbd_msc_cfg.cbw.bmFlags & 0x80U) != 0x80U) || (usbd_msc_cfg.cbw.dDataLength == 0U)) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); - return false; - } - - lba = GET_BE32(&usbd_msc_cfg.cbw.CB[2]); - USB_LOG_DBG("lba: 0x%x\r\n", lba); - - usbd_msc_cfg.scsi_blk_addr = lba * usbd_msc_cfg.scsi_blk_size; - - /* Number of Blocks to transfer */ - blk_num = GET_BE16(&usbd_msc_cfg.cbw.CB[7]); - - usbd_msc_cfg.scsi_blk_len = blk_num * usbd_msc_cfg.scsi_blk_size; - - if ((lba + blk_num) > usbd_msc_cfg.scsi_blk_nbr) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ADDRESS_OUT_OF_RANGE); - USB_LOG_ERR("LBA out of range\r\n"); - return false; - } - - if (usbd_msc_cfg.cbw.dDataLength != usbd_msc_cfg.scsi_blk_len) { - USB_LOG_ERR("scsi_blk_len does not match with dDataLength\r\n"); - return false; - } - usbd_msc_cfg.stage = MSC_DATA_IN; - return SCSI_processRead(); -} - -static bool SCSI_read12(uint8_t **data, uint32_t *len) -{ - /* Logical Block Address of First Block */ - uint32_t lba = 0; - uint32_t blk_num = 0; - if (((usbd_msc_cfg.cbw.bmFlags & 0x80U) != 0x80U) || (usbd_msc_cfg.cbw.dDataLength == 0U)) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); - return false; - } - - lba = GET_BE32(&usbd_msc_cfg.cbw.CB[2]); - USB_LOG_DBG("lba: 0x%x\r\n", lba); - - usbd_msc_cfg.scsi_blk_addr = lba * usbd_msc_cfg.scsi_blk_size; - - /* Number of Blocks to transfer */ - blk_num = GET_BE32(&usbd_msc_cfg.cbw.CB[6]); - - USB_LOG_DBG("num (block) : 0x%x\r\n", blk_num); - usbd_msc_cfg.scsi_blk_len = blk_num * usbd_msc_cfg.scsi_blk_size; - - if ((lba + blk_num) > usbd_msc_cfg.scsi_blk_nbr) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ADDRESS_OUT_OF_RANGE); - USB_LOG_ERR("LBA out of range\r\n"); - return false; - } - - if (usbd_msc_cfg.cbw.dDataLength != usbd_msc_cfg.scsi_blk_len) { - USB_LOG_ERR("scsi_blk_len does not match with dDataLength\r\n"); - return false; - } - usbd_msc_cfg.stage = MSC_DATA_IN; - return SCSI_processRead(); -} - -static bool SCSI_write10(uint8_t **data, uint32_t *len) -{ - /* Logical Block Address of First Block */ - uint32_t lba = 0; - uint32_t blk_num = 0; - if (((usbd_msc_cfg.cbw.bmFlags & 0x80U) != 0x00U) || (usbd_msc_cfg.cbw.dDataLength == 0U)) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); - return false; - } - - lba = GET_BE32(&usbd_msc_cfg.cbw.CB[2]); - USB_LOG_DBG("lba: 0x%x\r\n", lba); - - usbd_msc_cfg.scsi_blk_addr = lba * usbd_msc_cfg.scsi_blk_size; - - /* Number of Blocks to transfer */ - blk_num = GET_BE16(&usbd_msc_cfg.cbw.CB[7]); - - USB_LOG_DBG("num (block) : 0x%x\r\n", blk_num); - usbd_msc_cfg.scsi_blk_len = blk_num * usbd_msc_cfg.scsi_blk_size; - - if ((lba + blk_num) > usbd_msc_cfg.scsi_blk_nbr) { - USB_LOG_ERR("LBA out of range\r\n"); - return false; - } - - if (usbd_msc_cfg.cbw.dDataLength != usbd_msc_cfg.scsi_blk_len) { - return false; - } - usbd_msc_cfg.stage = MSC_DATA_OUT; - return true; -} - -static bool SCSI_write12(uint8_t **data, uint32_t *len) -{ - /* Logical Block Address of First Block */ - uint32_t lba = 0; - uint32_t blk_num = 0; - if (((usbd_msc_cfg.cbw.bmFlags & 0x80U) != 0x00U) || (usbd_msc_cfg.cbw.dDataLength == 0U)) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); - return false; - } - - lba = GET_BE32(&usbd_msc_cfg.cbw.CB[2]); - USB_LOG_DBG("lba: 0x%x\r\n", lba); - - usbd_msc_cfg.scsi_blk_addr = lba * usbd_msc_cfg.scsi_blk_size; - - /* Number of Blocks to transfer */ - blk_num = GET_BE32(&usbd_msc_cfg.cbw.CB[6]); - - USB_LOG_DBG("num (block) : 0x%x\r\n", blk_num); - usbd_msc_cfg.scsi_blk_len = blk_num * usbd_msc_cfg.scsi_blk_size; - - if ((lba + blk_num) > usbd_msc_cfg.scsi_blk_nbr) { - USB_LOG_ERR("LBA out of range\r\n"); - return false; - } - - if (usbd_msc_cfg.cbw.dDataLength != usbd_msc_cfg.scsi_blk_len) { - return false; - } - usbd_msc_cfg.stage = MSC_DATA_OUT; - return true; -} - -static bool SCSI_verify10(uint8_t **data, uint32_t *len) -{ - /* Logical Block Address of First Block */ - uint32_t lba = 0; - uint32_t blk_num = 0; - - if ((usbd_msc_cfg.cbw.CB[1] & 0x02U) == 0x00U) { - return true; - } - - if (((usbd_msc_cfg.cbw.bmFlags & 0x80U) != 0x00U) || (usbd_msc_cfg.cbw.dDataLength == 0U)) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); - return false; - } - - if ((usbd_msc_cfg.cbw.CB[1] & 0x02U) == 0x02U) { - SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_FIELED_IN_COMMAND); - return false; /* Error, Verify Mode Not supported*/ - } - - lba = GET_BE32(&usbd_msc_cfg.cbw.CB[2]); - USB_LOG_DBG("lba: 0x%x\r\n", lba); - - usbd_msc_cfg.scsi_blk_addr = lba * usbd_msc_cfg.scsi_blk_size; - - /* Number of Blocks to transfer */ - blk_num = GET_BE16(&usbd_msc_cfg.cbw.CB[7]); - - USB_LOG_DBG("num (block) : 0x%x\r\n", blk_num); - usbd_msc_cfg.scsi_blk_len = blk_num * usbd_msc_cfg.scsi_blk_size; - - if ((lba + blk_num) > usbd_msc_cfg.scsi_blk_nbr) { - USB_LOG_ERR("LBA out of range\r\n"); - return false; - } - - if (usbd_msc_cfg.cbw.dDataLength != usbd_msc_cfg.scsi_blk_len) { - return false; - } - - memOK = true; - usbd_msc_cfg.stage = MSC_DATA_OUT; - return true; -} - -static void mass_storage_bulk_out(uint8_t ep) -{ - switch (usbd_msc_cfg.stage) { - case MSC_READ_CBW: - if (SCSI_CBWDecode() == false) { - USB_LOG_ERR("Command:0x%02x decode err\r\n", usbd_msc_cfg.cbw.CB[0]); - usbd_msc_bot_abort(); - return; - } - break; - /* last command is write10 or write12,and has caculated blk_addr and blk_len,so the device start reading data from host*/ - case MSC_DATA_OUT: - switch (usbd_msc_cfg.cbw.CB[0]) { - case SCSI_WRITE10: - case SCSI_WRITE12: - if (SCSI_processWrite() == false) { - sendCSW(CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/ - //return; - } - break; - case SCSI_VERIFY10: - if (SCSI_processVerify() == false) { - sendCSW(CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/ - //return; - } - break; - default: - break; - } - break; - default: - break; - } - - /*set ep ack to recv next data*/ - usbd_ep_read(ep, NULL, 0, NULL); -} - -/** - * @brief EP Bulk IN handler, used to send data to the Host - * - * @param ep Endpoint address. - * @param ep_status Endpoint status code. - * - * @return N/A. - */ -static void mass_storage_bulk_in(uint8_t ep) -{ - switch (usbd_msc_cfg.stage) { - /* last command is read10 or read12,and has caculated blk_addr and blk_len,so the device has to send remain data to host*/ - case MSC_DATA_IN: - switch (usbd_msc_cfg.cbw.CB[0]) { - case SCSI_READ10: - case SCSI_READ12: - if (SCSI_processRead() == false) { - sendCSW(CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/ - return; - } - break; - default: - break; - } - - break; - - /*the device has to send a CSW*/ - case MSC_SEND_CSW: - sendCSW(CSW_STATUS_CMD_PASSED); - break; - - /*the host has received the CSW*/ - case MSC_WAIT_CSW: - usbd_msc_cfg.stage = MSC_READ_CBW; - break; - - default: - break; - } -} - -void msc_storage_notify_handler(uint8_t event, void *arg) -{ - switch (event) { - case USBD_EVENT_RESET: - usbd_msc_reset(); - break; - - default: - break; - } -} - -static usbd_class_t msc_class; - -static usbd_interface_t msc_intf = { - .class_handler = msc_storage_class_request_handler, - .vendor_handler = NULL, - .notify_handler = msc_storage_notify_handler, -}; - -void usbd_msc_class_init(uint8_t out_ep, uint8_t in_ep) -{ - msc_class.name = "usbd_msc"; - - usbd_class_register(&msc_class); - usbd_class_add_interface(&msc_class, &msc_intf); - - mass_ep_data[0].ep_addr = out_ep; - mass_ep_data[0].ep_cb = mass_storage_bulk_out; - mass_ep_data[1].ep_addr = in_ep; - mass_ep_data[1].ep_cb = mass_storage_bulk_in; - - usbd_interface_add_endpoint(&msc_intf, &mass_ep_data[0]); - usbd_interface_add_endpoint(&msc_intf, &mass_ep_data[1]); -} +/** + * @file usbd_msc.c + * @brief + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#include "usbd_core.h" +#include "usbd_msc.h" +#include "usb_scsi.h" + +/* max USB packet size */ +#ifndef CONFIG_USB_HS +#define MASS_STORAGE_BULK_EP_MPS 64 +#else +#define MASS_STORAGE_BULK_EP_MPS 512 +#endif + +#define MSD_OUT_EP_IDX 0 +#define MSD_IN_EP_IDX 1 + +/* Describe EndPoints configuration */ +static usbd_endpoint_t mass_ep_data[2]; + +/* MSC Bulk-only Stage */ +enum Stage { + MSC_READ_CBW = 0, /* Command Block Wrapper */ + MSC_DATA_OUT = 1, /* Data Out Phase */ + MSC_DATA_IN = 2, /* Data In Phase */ + MSC_SEND_CSW = 3, /* Command Status Wrapper */ + MSC_WAIT_CSW = 4, /* Command Status Wrapper */ +}; + +/* Device data structure */ +struct usbd_msc_cfg_private { + /* state of the bulk-only state machine */ + enum Stage stage; + struct CBW cbw; + struct CSW csw; + + uint8_t sKey; /* Sense key */ + uint8_t ASC; /* Additional Sense Code */ + uint8_t ASQ; /* Additional Sense Qualifier */ + uint8_t max_lun; + uint16_t scsi_blk_size; + uint32_t scsi_blk_nbr; + + uint32_t scsi_blk_addr; + uint32_t scsi_blk_len; + uint8_t *block_buffer; + +} usbd_msc_cfg; + +/*memory OK (after a usbd_msc_memory_verify)*/ +static bool memOK; + +static void usbd_msc_reset(void) +{ + usbd_msc_cfg.stage = MSC_READ_CBW; + usbd_msc_cfg.scsi_blk_addr = 0U; + usbd_msc_cfg.scsi_blk_len = 0U; + usbd_msc_cfg.max_lun = 0; + usbd_msc_cfg.sKey = 0; + usbd_msc_cfg.ASC = 0; + usbd_msc_cfg.ASQ = 0; + + (void)memset((void *)&usbd_msc_cfg.cbw, 0, sizeof(struct CBW)); + (void)memset((void *)&usbd_msc_cfg.csw, 0, sizeof(struct CSW)); + + usbd_msc_get_cap(0, &usbd_msc_cfg.scsi_blk_nbr, &usbd_msc_cfg.scsi_blk_size); + + if (usbd_msc_cfg.block_buffer) { + free(usbd_msc_cfg.block_buffer); + } + usbd_msc_cfg.block_buffer = malloc(usbd_msc_cfg.scsi_blk_size * sizeof(uint8_t)); + memset(usbd_msc_cfg.block_buffer, 0, usbd_msc_cfg.scsi_blk_size * sizeof(uint8_t)); +} + +/** + * @brief Handler called for Class requests not handled by the USB stack. + * + * @param setup Information about the request to execute. + * @param len Size of the buffer. + * @param data Buffer containing the request result. + * + * @return 0 on success, negative errno code on fail. + */ +static int msc_storage_class_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) +{ + USB_LOG_DBG("MSC Class request: " + "bRequest 0x%02x\r\n", + setup->bRequest); + + switch (setup->bRequest) { + case MSC_REQUEST_RESET: + USB_LOG_DBG("MSC_REQUEST_RESET\r\n"); + + if (setup->wLength) { + USB_LOG_WRN("Invalid length\r\n"); + return -1; + } + + usbd_msc_reset(); + break; + + case MSC_REQUEST_GET_MAX_LUN: + USB_LOG_DBG("MSC_REQUEST_GET_MAX_LUN\r\n"); + + if (setup->wLength != 1) { + USB_LOG_WRN("Invalid length\r\n"); + return -1; + } + + *data = (uint8_t *)(&usbd_msc_cfg.max_lun); + *len = 1; + break; + + default: + USB_LOG_WRN("Unhandled MSC Class bRequest 0x%02x\r\n", setup->bRequest); + return -1; + } + + return 0; +} + +static void usbd_msc_bot_abort(void) +{ + if ((usbd_msc_cfg.cbw.bmFlags == 0) && (usbd_msc_cfg.cbw.dDataLength != 0)) { + usbd_ep_set_stall(mass_ep_data[MSD_OUT_EP_IDX].ep_addr); + } + usbd_ep_set_stall(mass_ep_data[MSD_IN_EP_IDX].ep_addr); +} + +static void sendCSW(uint8_t CSW_Status) +{ + usbd_msc_cfg.csw.dSignature = MSC_CSW_Signature; + usbd_msc_cfg.csw.bStatus = CSW_Status; + + /* updating the State Machine , so that we wait CSW when this + * transfer is complete, ie when we get a bulk in callback + */ + usbd_msc_cfg.stage = MSC_WAIT_CSW; + + if (usbd_ep_write(mass_ep_data[MSD_IN_EP_IDX].ep_addr, (uint8_t *)&usbd_msc_cfg.csw, + sizeof(struct CSW), NULL) != 0) { + USB_LOG_ERR("usb write failure\r\n"); + } +} + +static void sendLastData(uint8_t *buffer, uint8_t size) +{ + size = MIN(size, usbd_msc_cfg.cbw.dDataLength); + + /* updating the State Machine , so that we send CSW when this + * transfer is complete, ie when we get a bulk in callback + */ + usbd_msc_cfg.stage = MSC_SEND_CSW; + + if (usbd_ep_write(mass_ep_data[MSD_IN_EP_IDX].ep_addr, buffer, size, NULL) != 0) { + USB_LOG_ERR("USB write failed\r\n"); + } + + usbd_msc_cfg.csw.dDataResidue -= size; + usbd_msc_cfg.csw.bStatus = CSW_STATUS_CMD_PASSED; +} + +/** + * @brief SCSI COMMAND + */ +static bool SCSI_testUnitReady(uint8_t **data, uint32_t *len); +static bool SCSI_requestSense(uint8_t **data, uint32_t *len); +static bool SCSI_inquiry(uint8_t **data, uint32_t *len); +static bool SCSI_startStopUnit(uint8_t **data, uint32_t *len); +static bool SCSI_preventAllowMediaRemoval(uint8_t **data, uint32_t *len); +static bool SCSI_modeSense6(uint8_t **data, uint32_t *len); +static bool SCSI_modeSense10(uint8_t **data, uint32_t *len); +static bool SCSI_readFormatCapacity(uint8_t **data, uint32_t *len); +static bool SCSI_readCapacity10(uint8_t **data, uint32_t *len); +static bool SCSI_read10(uint8_t **data, uint32_t *len); +static bool SCSI_read12(uint8_t **data, uint32_t *len); +static bool SCSI_write10(uint8_t **data, uint32_t *len); +static bool SCSI_write12(uint8_t **data, uint32_t *len); +static bool SCSI_verify10(uint8_t **data, uint32_t *len); + +/** +* @brief SCSI_SenseCode +* Load the last error code in the error list +* @param sKey: Sense Key +* @param ASC: Additional Sense Code +* @retval none + +*/ +static void SCSI_SenseCode(uint8_t sKey, uint8_t ASC) +{ + usbd_msc_cfg.sKey = sKey; + usbd_msc_cfg.ASC = ASC; +} + +static bool SCSI_processRead(void) +{ + uint32_t transfer_len; + + USB_LOG_DBG("read addr:%d\r\n", usbd_msc_cfg.scsi_blk_addr); + + transfer_len = MIN(usbd_msc_cfg.scsi_blk_len, MASS_STORAGE_BULK_EP_MPS); + + /* we read an entire block */ + if (!(usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size)) { + if (usbd_msc_sector_read((usbd_msc_cfg.scsi_blk_addr / usbd_msc_cfg.scsi_blk_size), usbd_msc_cfg.block_buffer, usbd_msc_cfg.scsi_blk_size) != 0) { + SCSI_SenseCode(SCSI_SENSE_HARDWARE_ERROR, SCSI_ASC_UNRECOVERED_READ_ERROR); + return false; + } + } + + usbd_ep_write(mass_ep_data[MSD_IN_EP_IDX].ep_addr, + &usbd_msc_cfg.block_buffer[usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size], transfer_len, NULL); + + usbd_msc_cfg.scsi_blk_addr += transfer_len; + usbd_msc_cfg.scsi_blk_len -= transfer_len; + usbd_msc_cfg.csw.dDataResidue -= transfer_len; + + if (usbd_msc_cfg.scsi_blk_len == 0) { + usbd_msc_cfg.stage = MSC_SEND_CSW; + } + + return true; +} + +static bool SCSI_processWrite() +{ + uint32_t bytes_read; + USB_LOG_DBG("write addr:%d\r\n", usbd_msc_cfg.scsi_blk_addr); + + /* we fill an array in RAM of 1 block before writing it in memory */ + usbd_ep_read(mass_ep_data[MSD_OUT_EP_IDX].ep_addr, &usbd_msc_cfg.block_buffer[usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size], MASS_STORAGE_BULK_EP_MPS, + &bytes_read); + + /* if the array is filled, write it in memory */ + if ((usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size) + bytes_read >= usbd_msc_cfg.scsi_blk_size) { + if (usbd_msc_sector_write((usbd_msc_cfg.scsi_blk_addr / usbd_msc_cfg.scsi_blk_size), usbd_msc_cfg.block_buffer, usbd_msc_cfg.scsi_blk_size) != 0) { + SCSI_SenseCode(SCSI_SENSE_HARDWARE_ERROR, SCSI_ASC_WRITE_FAULT); + return false; + } + } + + usbd_msc_cfg.scsi_blk_addr += bytes_read; + usbd_msc_cfg.scsi_blk_len -= bytes_read; + usbd_msc_cfg.csw.dDataResidue -= bytes_read; + + if (usbd_msc_cfg.scsi_blk_len == 0) { + sendCSW(CSW_STATUS_CMD_PASSED); + } + + return true; +} + +static bool SCSI_processVerify() +{ +#if 0 + uint32_t bytes_read; + uint8_t out_buffer[MASS_STORAGE_BULK_EP_MPS]; + + USB_LOG_DBG("verify addr:%d\r\n", usbd_msc_cfg.scsi_blk_addr); + + /* we fill an array in RAM of 1 block before writing it in memory */ + usbd_ep_read(mass_ep_data[MSD_OUT_EP_IDX].ep_addr, out_buffer, MASS_STORAGE_BULK_EP_MPS, + &bytes_read); + + /* we read an entire block */ + if (!(usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size)) { + if (usbd_msc_sector_read((usbd_msc_cfg.scsi_blk_addr / usbd_msc_cfg.scsi_blk_size), usbd_msc_cfg.block_buffer, usbd_msc_cfg.scsi_blk_size) != 0) { + SCSI_SenseCode(SCSI_SENSE_HARDWARE_ERROR, SCSI_ASC_UNRECOVERED_READ_ERROR); + return false; + } + } + + /* info are in RAM -> no need to re-read memory */ + for (uint16_t i = 0U; i < bytes_read; i++) { + if (usbd_msc_cfg.block_buffer[usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size + i] != out_buffer[i]) { + USB_LOG_DBG("Mismatch sector %d offset %d", + usbd_msc_cfg.scsi_blk_addr / usbd_msc_cfg.scsi_blk_size, i); + memOK = false; + break; + } + } + + usbd_msc_cfg.scsi_blk_addr += bytes_read; + usbd_msc_cfg.scsi_blk_len -= bytes_read; + usbd_msc_cfg.csw.dDataResidue -= bytes_read; + + if (usbd_msc_cfg.scsi_blk_len == 0) { + sendCSW(CSW_STATUS_CMD_PASSED); + } +#endif + return true; +} + +static bool SCSI_CBWDecode() +{ + uint8_t *buf2send = usbd_msc_cfg.block_buffer; + uint32_t len2send = 0; + uint32_t bytes_read; + bool ret = false; + + usbd_ep_read(mass_ep_data[MSD_OUT_EP_IDX].ep_addr, (uint8_t *)&usbd_msc_cfg.cbw, USB_SIZEOF_MSC_CBW, + &bytes_read); + + if (bytes_read != sizeof(struct CBW)) { + USB_LOG_ERR("size != sizeof(cbw)\r\n"); + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); + return false; + } + + usbd_msc_cfg.csw.dTag = usbd_msc_cfg.cbw.dTag; + usbd_msc_cfg.csw.dDataResidue = usbd_msc_cfg.cbw.dDataLength; + + if ((usbd_msc_cfg.cbw.bLUN > 1) || (usbd_msc_cfg.cbw.dSignature != MSC_CBW_Signature) || (usbd_msc_cfg.cbw.bCBLength < 1) || (usbd_msc_cfg.cbw.bCBLength > 16)) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); + return false; + } else { + switch (usbd_msc_cfg.cbw.CB[0]) { + case SCSI_TEST_UNIT_READY: + ret = SCSI_testUnitReady(&buf2send, &len2send); + break; + case SCSI_REQUEST_SENSE: + ret = SCSI_requestSense(&buf2send, &len2send); + break; + case SCSI_INQUIRY: + ret = SCSI_inquiry(&buf2send, &len2send); + break; + case SCSI_START_STOP_UNIT: + ret = SCSI_startStopUnit(&buf2send, &len2send); + break; + case SCSI_PREVENT_ALLOW_MEDIA_REMOVAL: + ret = SCSI_preventAllowMediaRemoval(&buf2send, &len2send); + break; + case SCSI_MODE_SENSE6: + ret = SCSI_modeSense6(&buf2send, &len2send); + break; + case SCSI_MODE_SENSE10: + ret = SCSI_modeSense10(&buf2send, &len2send); + break; + case SCSI_READ_FORMAT_CAPACITIES: + ret = SCSI_readFormatCapacity(&buf2send, &len2send); + break; + case SCSI_READ_CAPACITY10: + ret = SCSI_readCapacity10(&buf2send, &len2send); + break; + case SCSI_READ10: + ret = SCSI_read10(NULL, 0); + break; + case SCSI_READ12: + ret = SCSI_read12(NULL, 0); + break; + case SCSI_WRITE10: + ret = SCSI_write10(NULL, 0); + break; + case SCSI_WRITE12: + ret = SCSI_write12(NULL, 0); + break; + case SCSI_VERIFY10: + ret = SCSI_verify10(NULL, 0); + break; + + default: + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); + USB_LOG_WRN("unsupported cmd:0x%02x\r\n", usbd_msc_cfg.cbw.CB[0]); + ret = false; + break; + } + } + if (ret) { + if (usbd_msc_cfg.stage == MSC_READ_CBW) { + if (len2send) { + sendLastData(buf2send, len2send); + } else { + sendCSW(CSW_STATUS_CMD_PASSED); + } + } + } + return ret; +} + +static bool SCSI_testUnitReady(uint8_t **data, uint32_t *len) +{ + if (usbd_msc_cfg.cbw.dDataLength != 0U) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); + return false; + } + *data = NULL; + *len = 0; + return true; +} + +static bool SCSI_requestSense(uint8_t **data, uint32_t *len) +{ + uint8_t data_len = REQUEST_SENSE_DATA_LEN; + if (usbd_msc_cfg.cbw.dDataLength == 0U) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); + return false; + } + + if (usbd_msc_cfg.cbw.CB[4] < REQUEST_SENSE_DATA_LEN) { + data_len = usbd_msc_cfg.cbw.CB[4]; + } + + uint8_t request_sense[REQUEST_SENSE_DATA_LEN] = { + 0x70, + 0x00, + 0x00, /* Sense Key */ + 0x00, + 0x00, + 0x00, + 0x00, + REQUEST_SENSE_DATA_LEN - 8, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, /* Additional Sense Code */ + 0x00, /* Additional Sense Request */ + 0x00, + 0x00, + 0x00, + 0x00, + }; + + request_sense[2] = usbd_msc_cfg.sKey; + request_sense[12] = usbd_msc_cfg.ASC; + request_sense[13] = usbd_msc_cfg.ASQ; +#if 0 + request_sense[ 2] = 0x06; /* UNIT ATTENTION */ + request_sense[12] = 0x28; /* Additional Sense Code: Not ready to ready transition */ + request_sense[13] = 0x00; /* Additional Sense Code Qualifier */ +#endif +#if 0 + request_sense[ 2] = 0x02; /* NOT READY */ + request_sense[12] = 0x3A; /* Additional Sense Code: Medium not present */ + request_sense[13] = 0x00; /* Additional Sense Code Qualifier */ +#endif +#if 0 + request_sense[ 2] = 0x05; /* ILLEGAL REQUEST */ + request_sense[12] = 0x20; /* Additional Sense Code: Invalid command */ + request_sense[13] = 0x00; /* Additional Sense Code Qualifier */ +#endif +#if 0 + request_sense[ 2] = 0x00; /* NO SENSE */ + request_sense[12] = 0x00; /* Additional Sense Code: No additional code */ + request_sense[13] = 0x00; /* Additional Sense Code Qualifier */ +#endif + + memcpy(*data, (uint8_t *)request_sense, data_len); + *len = data_len; + return true; +} + +static bool SCSI_inquiry(uint8_t **data, uint32_t *len) +{ + uint8_t data_len = STANDARD_INQUIRY_DATA_LEN; + + uint8_t inquiry00[6] = { + 0x00, + 0x00, + 0x00, + (0x06 - 4U), + 0x00, + 0x80 + }; + + /* USB Mass storage VPD Page 0x80 Inquiry Data for Unit Serial Number */ + uint8_t inquiry80[8] = { + 0x00, + 0x80, + 0x00, + 0x08, + 0x20, /* Put Product Serial number */ + 0x20, + 0x20, + 0x20 + }; + + uint8_t inquiry[STANDARD_INQUIRY_DATA_LEN] = { + /* 36 */ + + /* LUN 0 */ + 0x00, + 0x80, + 0x02, + 0x02, + (STANDARD_INQUIRY_DATA_LEN - 5), + 0x00, + 0x00, + 0x00, + 'B', 'o', 'u', 'f', 'f', 'a', 'l', 'o', /* Manufacturer : 8 bytes */ + 'P', 'r', 'o', 'd', 'u', 'c', 't', ' ', /* Product : 16 Bytes */ + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + '0', '.', '0', '1' /* Version : 4 Bytes */ + }; + + if (usbd_msc_cfg.cbw.dDataLength == 0U) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); + return false; + } + + if ((usbd_msc_cfg.cbw.CB[1] & 0x01U) != 0U) { /* Evpd is set */ + if (usbd_msc_cfg.cbw.CB[2] == 0U) { /* Request for Supported Vital Product Data Pages*/ + data_len = 0x06; + memcpy(*data, (uint8_t *)inquiry00, data_len); + } else if (usbd_msc_cfg.cbw.CB[2] == 0x80U) { /* Request for VPD page 0x80 Unit Serial Number */ + data_len = 0x08; + memcpy(*data, (uint8_t *)inquiry80, data_len); + } else { /* Request Not supported */ + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_FIELED_IN_COMMAND); + return false; + } + } else { + if (usbd_msc_cfg.cbw.CB[4] < STANDARD_INQUIRY_DATA_LEN) { + data_len = usbd_msc_cfg.cbw.CB[4]; + } + memcpy(*data, (uint8_t *)inquiry, data_len); + } + + *len = data_len; + return true; +} + +static bool SCSI_startStopUnit(uint8_t **data, uint32_t *len) +{ + if (usbd_msc_cfg.cbw.dDataLength != 0U) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); + return false; + } + + if ((usbd_msc_cfg.cbw.CB[4] & 0x3U) == 0x1U) /* START=1 */ + { + //SCSI_MEDIUM_UNLOCKED; + } else if ((usbd_msc_cfg.cbw.CB[4] & 0x3U) == 0x2U) /* START=0 and LOEJ Load Eject=1 */ + { + //SCSI_MEDIUM_EJECTED; + } else if ((usbd_msc_cfg.cbw.CB[4] & 0x3U) == 0x3U) /* START=1 and LOEJ Load Eject=1 */ + { + //SCSI_MEDIUM_UNLOCKED; + } else { + } + + *data = NULL; + *len = 0; + return true; +} + +static bool SCSI_preventAllowMediaRemoval(uint8_t **data, uint32_t *len) +{ + if (usbd_msc_cfg.cbw.dDataLength != 0U) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); + return false; + } + if (usbd_msc_cfg.cbw.CB[4] == 0U) { + //SCSI_MEDIUM_UNLOCKED; + } else { + //SCSI_MEDIUM_LOCKED; + } + *data = NULL; + *len = 0; + return true; +} + +static bool SCSI_modeSense6(uint8_t **data, uint32_t *len) +{ + uint8_t data_len = 4; + if (usbd_msc_cfg.cbw.dDataLength == 0U) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); + return false; + } + if (usbd_msc_cfg.cbw.CB[4] < MODE_SENSE6_DATA_LEN) { + data_len = usbd_msc_cfg.cbw.CB[4]; + } + + uint8_t sense6[] = { 0x03, 0x00, 0x00, 0x00 }; + + memcpy(*data, (uint8_t *)sense6, data_len); + *len = data_len; + return true; +} + +static bool SCSI_modeSense10(uint8_t **data, uint32_t *len) +{ + uint8_t data_len = MODE_SENSE10_DATA_LEN; + if (usbd_msc_cfg.cbw.dDataLength == 0U) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); + return false; + } + + if (usbd_msc_cfg.cbw.CB[8] < MODE_SENSE10_DATA_LEN) { + data_len = usbd_msc_cfg.cbw.CB[8]; + } + + uint8_t sense10[MODE_SENSE10_DATA_LEN] = { + 0x00, + 0x26, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x08, + 0x12, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00 + }; + + memcpy(*data, (uint8_t *)sense10, data_len); + *len = data_len; + return true; +} + +static bool SCSI_readFormatCapacity(uint8_t **data, uint32_t *len) +{ + if (usbd_msc_cfg.cbw.dDataLength == 0U) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); + return false; + } + uint8_t format_capacity[READ_FORMAT_CAPACITY_DATA_LEN] = { + 0x00, + 0x00, + 0x00, + 0x08, /* Capacity List Length */ + (uint8_t)((usbd_msc_cfg.scsi_blk_nbr >> 24) & 0xff), + (uint8_t)((usbd_msc_cfg.scsi_blk_nbr >> 16) & 0xff), + (uint8_t)((usbd_msc_cfg.scsi_blk_nbr >> 8) & 0xff), + (uint8_t)((usbd_msc_cfg.scsi_blk_nbr >> 0) & 0xff), + + 0x02, /* Descriptor Code: Formatted Media */ + 0x00, + (uint8_t)((usbd_msc_cfg.scsi_blk_size >> 8) & 0xff), + (uint8_t)((usbd_msc_cfg.scsi_blk_size >> 0) & 0xff), + }; + + memcpy(*data, (uint8_t *)format_capacity, READ_FORMAT_CAPACITY_DATA_LEN); + *len = READ_FORMAT_CAPACITY_DATA_LEN; + return true; +} + +static bool SCSI_readCapacity10(uint8_t **data, uint32_t *len) +{ + if (usbd_msc_cfg.cbw.dDataLength == 0U) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); + return false; + } + + uint8_t capacity10[READ_CAPACITY10_DATA_LEN] = { + (uint8_t)(((usbd_msc_cfg.scsi_blk_nbr - 1) >> 24) & 0xff), + (uint8_t)(((usbd_msc_cfg.scsi_blk_nbr - 1) >> 16) & 0xff), + (uint8_t)(((usbd_msc_cfg.scsi_blk_nbr - 1) >> 8) & 0xff), + (uint8_t)(((usbd_msc_cfg.scsi_blk_nbr - 1) >> 0) & 0xff), + + (uint8_t)((usbd_msc_cfg.scsi_blk_size >> 24) & 0xff), + (uint8_t)((usbd_msc_cfg.scsi_blk_size >> 16) & 0xff), + (uint8_t)((usbd_msc_cfg.scsi_blk_size >> 8) & 0xff), + (uint8_t)((usbd_msc_cfg.scsi_blk_size >> 0) & 0xff), + }; + + memcpy(*data, (uint8_t *)&capacity10, READ_CAPACITY10_DATA_LEN); + *len = READ_CAPACITY10_DATA_LEN; + return true; +} + +static bool SCSI_read10(uint8_t **data, uint32_t *len) +{ + /* Logical Block Address of First Block */ + uint32_t lba = 0; + uint32_t blk_num = 0; + if (((usbd_msc_cfg.cbw.bmFlags & 0x80U) != 0x80U) || (usbd_msc_cfg.cbw.dDataLength == 0U)) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); + return false; + } + + lba = GET_BE32(&usbd_msc_cfg.cbw.CB[2]); + USB_LOG_DBG("lba: 0x%x\r\n", lba); + + usbd_msc_cfg.scsi_blk_addr = lba * usbd_msc_cfg.scsi_blk_size; + + /* Number of Blocks to transfer */ + blk_num = GET_BE16(&usbd_msc_cfg.cbw.CB[7]); + + usbd_msc_cfg.scsi_blk_len = blk_num * usbd_msc_cfg.scsi_blk_size; + + if ((lba + blk_num) > usbd_msc_cfg.scsi_blk_nbr) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ADDRESS_OUT_OF_RANGE); + USB_LOG_ERR("LBA out of range\r\n"); + return false; + } + + if (usbd_msc_cfg.cbw.dDataLength != usbd_msc_cfg.scsi_blk_len) { + USB_LOG_ERR("scsi_blk_len does not match with dDataLength\r\n"); + return false; + } + usbd_msc_cfg.stage = MSC_DATA_IN; + return SCSI_processRead(); +} + +static bool SCSI_read12(uint8_t **data, uint32_t *len) +{ + /* Logical Block Address of First Block */ + uint32_t lba = 0; + uint32_t blk_num = 0; + if (((usbd_msc_cfg.cbw.bmFlags & 0x80U) != 0x80U) || (usbd_msc_cfg.cbw.dDataLength == 0U)) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); + return false; + } + + lba = GET_BE32(&usbd_msc_cfg.cbw.CB[2]); + USB_LOG_DBG("lba: 0x%x\r\n", lba); + + usbd_msc_cfg.scsi_blk_addr = lba * usbd_msc_cfg.scsi_blk_size; + + /* Number of Blocks to transfer */ + blk_num = GET_BE32(&usbd_msc_cfg.cbw.CB[6]); + + USB_LOG_DBG("num (block) : 0x%x\r\n", blk_num); + usbd_msc_cfg.scsi_blk_len = blk_num * usbd_msc_cfg.scsi_blk_size; + + if ((lba + blk_num) > usbd_msc_cfg.scsi_blk_nbr) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ADDRESS_OUT_OF_RANGE); + USB_LOG_ERR("LBA out of range\r\n"); + return false; + } + + if (usbd_msc_cfg.cbw.dDataLength != usbd_msc_cfg.scsi_blk_len) { + USB_LOG_ERR("scsi_blk_len does not match with dDataLength\r\n"); + return false; + } + usbd_msc_cfg.stage = MSC_DATA_IN; + return SCSI_processRead(); +} + +static bool SCSI_write10(uint8_t **data, uint32_t *len) +{ + /* Logical Block Address of First Block */ + uint32_t lba = 0; + uint32_t blk_num = 0; + if (((usbd_msc_cfg.cbw.bmFlags & 0x80U) != 0x00U) || (usbd_msc_cfg.cbw.dDataLength == 0U)) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); + return false; + } + + lba = GET_BE32(&usbd_msc_cfg.cbw.CB[2]); + USB_LOG_DBG("lba: 0x%x\r\n", lba); + + usbd_msc_cfg.scsi_blk_addr = lba * usbd_msc_cfg.scsi_blk_size; + + /* Number of Blocks to transfer */ + blk_num = GET_BE16(&usbd_msc_cfg.cbw.CB[7]); + + USB_LOG_DBG("num (block) : 0x%x\r\n", blk_num); + usbd_msc_cfg.scsi_blk_len = blk_num * usbd_msc_cfg.scsi_blk_size; + + if ((lba + blk_num) > usbd_msc_cfg.scsi_blk_nbr) { + USB_LOG_ERR("LBA out of range\r\n"); + return false; + } + + if (usbd_msc_cfg.cbw.dDataLength != usbd_msc_cfg.scsi_blk_len) { + return false; + } + usbd_msc_cfg.stage = MSC_DATA_OUT; + return true; +} + +static bool SCSI_write12(uint8_t **data, uint32_t *len) +{ + /* Logical Block Address of First Block */ + uint32_t lba = 0; + uint32_t blk_num = 0; + if (((usbd_msc_cfg.cbw.bmFlags & 0x80U) != 0x00U) || (usbd_msc_cfg.cbw.dDataLength == 0U)) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); + return false; + } + + lba = GET_BE32(&usbd_msc_cfg.cbw.CB[2]); + USB_LOG_DBG("lba: 0x%x\r\n", lba); + + usbd_msc_cfg.scsi_blk_addr = lba * usbd_msc_cfg.scsi_blk_size; + + /* Number of Blocks to transfer */ + blk_num = GET_BE32(&usbd_msc_cfg.cbw.CB[6]); + + USB_LOG_DBG("num (block) : 0x%x\r\n", blk_num); + usbd_msc_cfg.scsi_blk_len = blk_num * usbd_msc_cfg.scsi_blk_size; + + if ((lba + blk_num) > usbd_msc_cfg.scsi_blk_nbr) { + USB_LOG_ERR("LBA out of range\r\n"); + return false; + } + + if (usbd_msc_cfg.cbw.dDataLength != usbd_msc_cfg.scsi_blk_len) { + return false; + } + usbd_msc_cfg.stage = MSC_DATA_OUT; + return true; +} + +static bool SCSI_verify10(uint8_t **data, uint32_t *len) +{ + /* Logical Block Address of First Block */ + uint32_t lba = 0; + uint32_t blk_num = 0; + + if ((usbd_msc_cfg.cbw.CB[1] & 0x02U) == 0x00U) { + return true; + } + + if (((usbd_msc_cfg.cbw.bmFlags & 0x80U) != 0x00U) || (usbd_msc_cfg.cbw.dDataLength == 0U)) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB); + return false; + } + + if ((usbd_msc_cfg.cbw.CB[1] & 0x02U) == 0x02U) { + SCSI_SenseCode(SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_FIELED_IN_COMMAND); + return false; /* Error, Verify Mode Not supported*/ + } + + lba = GET_BE32(&usbd_msc_cfg.cbw.CB[2]); + USB_LOG_DBG("lba: 0x%x\r\n", lba); + + usbd_msc_cfg.scsi_blk_addr = lba * usbd_msc_cfg.scsi_blk_size; + + /* Number of Blocks to transfer */ + blk_num = GET_BE16(&usbd_msc_cfg.cbw.CB[7]); + + USB_LOG_DBG("num (block) : 0x%x\r\n", blk_num); + usbd_msc_cfg.scsi_blk_len = blk_num * usbd_msc_cfg.scsi_blk_size; + + if ((lba + blk_num) > usbd_msc_cfg.scsi_blk_nbr) { + USB_LOG_ERR("LBA out of range\r\n"); + return false; + } + + if (usbd_msc_cfg.cbw.dDataLength != usbd_msc_cfg.scsi_blk_len) { + return false; + } + + memOK = true; + usbd_msc_cfg.stage = MSC_DATA_OUT; + return true; +} + +static void mass_storage_bulk_out(uint8_t ep) +{ + switch (usbd_msc_cfg.stage) { + case MSC_READ_CBW: + if (SCSI_CBWDecode() == false) { + USB_LOG_ERR("Command:0x%02x decode err\r\n", usbd_msc_cfg.cbw.CB[0]); + usbd_msc_bot_abort(); + return; + } + break; + /* last command is write10 or write12,and has caculated blk_addr and blk_len,so the device start reading data from host*/ + case MSC_DATA_OUT: + switch (usbd_msc_cfg.cbw.CB[0]) { + case SCSI_WRITE10: + case SCSI_WRITE12: + if (SCSI_processWrite() == false) { + sendCSW(CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/ + //return; + } + break; + case SCSI_VERIFY10: + if (SCSI_processVerify() == false) { + sendCSW(CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/ + //return; + } + break; + default: + break; + } + break; + default: + break; + } + + /*set ep ack to recv next data*/ + usbd_ep_read(ep, NULL, 0, NULL); +} + +/** + * @brief EP Bulk IN handler, used to send data to the Host + * + * @param ep Endpoint address. + * @param ep_status Endpoint status code. + * + * @return N/A. + */ +static void mass_storage_bulk_in(uint8_t ep) +{ + switch (usbd_msc_cfg.stage) { + /* last command is read10 or read12,and has caculated blk_addr and blk_len,so the device has to send remain data to host*/ + case MSC_DATA_IN: + switch (usbd_msc_cfg.cbw.CB[0]) { + case SCSI_READ10: + case SCSI_READ12: + if (SCSI_processRead() == false) { + sendCSW(CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/ + return; + } + break; + default: + break; + } + + break; + + /*the device has to send a CSW*/ + case MSC_SEND_CSW: + sendCSW(CSW_STATUS_CMD_PASSED); + break; + + /*the host has received the CSW*/ + case MSC_WAIT_CSW: + usbd_msc_cfg.stage = MSC_READ_CBW; + break; + + default: + break; + } +} + +void msc_storage_notify_handler(uint8_t event, void *arg) +{ + switch (event) { + case USBD_EVENT_RESET: + usbd_msc_reset(); + break; + + default: + break; + } +} + +static usbd_class_t msc_class; + +static usbd_interface_t msc_intf = { + .class_handler = msc_storage_class_request_handler, + .vendor_handler = NULL, + .notify_handler = msc_storage_notify_handler, +}; + +void usbd_msc_class_init(uint8_t out_ep, uint8_t in_ep) +{ + msc_class.name = "usbd_msc"; + + usbd_class_register(&msc_class); + usbd_class_add_interface(&msc_class, &msc_intf); + + mass_ep_data[0].ep_addr = out_ep; + mass_ep_data[0].ep_cb = mass_storage_bulk_out; + mass_ep_data[1].ep_addr = in_ep; + mass_ep_data[1].ep_cb = mass_storage_bulk_in; + + usbd_interface_add_endpoint(&msc_intf, &mass_ep_data[0]); + usbd_interface_add_endpoint(&msc_intf, &mass_ep_data[1]); +} diff --git a/class/msc/usbh_msc.c b/class/msc/usbh_msc.c index a7498fa8..f47ecf4a 100644 --- a/class/msc/usbh_msc.c +++ b/class/msc/usbh_msc.c @@ -1,438 +1,438 @@ -/** - * @file usbh_msc.c - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#include "usbh_core.h" -#include "usbh_msc.h" -#include "scsi.h" - -#define DEV_FORMAT "/dev/sd%c" -#define DEV_NAMELEN 16 - -static uint32_t g_devinuse = 0; - -/**************************************************************************** - * Name: usbh_msc_devno_alloc - * - * Description: - * Allocate a unique /dev/ttyACM[n] minor number in the range 0-31. - * - ****************************************************************************/ - -static int usbh_msc_devno_alloc(struct usbh_msc *priv) -{ - uint32_t flags; - int devno; - - flags = usb_osal_enter_critical_section(); - for (devno = 0; devno < 26; devno++) { - uint32_t bitno = 1 << devno; - if ((g_devinuse & bitno) == 0) { - g_devinuse |= bitno; - priv->sdchar = 'a' + devno; - usb_osal_leave_critical_section(flags); - return 0; - } - } - - usb_osal_leave_critical_section(flags); - return -EMFILE; -} - -/**************************************************************************** - * Name: usbh_msc_devno_free - * - * Description: - * Free a /dev/sd[n] minor number so that it can be used. - * - ****************************************************************************/ - -static void usbh_msc_devno_free(struct usbh_msc *priv) -{ - int devno = priv->sdchar - 'a'; - - if (devno >= 0 && devno < 26) { - uint32_t flags = usb_osal_enter_critical_section(); - g_devinuse &= ~(1 << devno); - usb_osal_leave_critical_section(flags); - } -} - -/**************************************************************************** - * Name: usbh_msc_mkdevname - * - * Description: - * Format a /dev/sd[n] device name given a minor number. - * - ****************************************************************************/ - -static inline void usbh_msc_mkdevname(struct usbh_msc *priv, char *devname) -{ - snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->sdchar); -} - -static int usbh_msc_get_maxlun(struct usbh_hubport *hport, uint8_t intf, uint8_t *buffer) -{ - struct usb_setup_packet *setup; - struct usbh_msc *msc_class = (struct usbh_msc *)hport->config.intf[intf].priv; - - setup = msc_class->setup; - - if (msc_class->intf != intf) { - return -1; - } - - setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; - setup->bRequest = MSC_REQUEST_GET_MAX_LUN; - setup->wValue = 0; - setup->wIndex = intf; - setup->wLength = 1; - - return usbh_control_transfer(hport->ep0, setup, buffer); -} - -static void usbh_msc_cbw_dump(struct CBW *cbw) -{ - int i; - - USB_LOG_INFO("CBW:\r\n"); - USB_LOG_INFO(" signature: 0x%08x\r\n", (unsigned int)cbw->dSignature); - USB_LOG_INFO(" tag: 0x%08x\r\n", (unsigned int)cbw->dTag); - USB_LOG_INFO(" datlen: 0x%08x\r\n", (unsigned int)cbw->dDataLength); - USB_LOG_INFO(" flags: 0x%02x\r\n", cbw->bmFlags); - USB_LOG_INFO(" lun: 0x%02x\r\n", cbw->bLUN); - USB_LOG_INFO(" cblen: 0x%02x\r\n", cbw->bCBLength); - - USB_LOG_INFO("CB:\r\n"); - for (i = 0; i < cbw->bCBLength; i += 8) { - USB_LOG_INFO(" 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\r\n", - cbw->CB[i], cbw->CB[i + 1], cbw->CB[i + 2], - cbw->CB[i + 3], cbw->CB[i + 4], cbw->CB[i + 5], - cbw->CB[i + 6], cbw->CB[i + 7]); - } -} - -static void usbh_msc_csw_dump(struct CSW *csw) -{ - USB_LOG_INFO("CSW:\r\n"); - USB_LOG_INFO(" signature: 0x%08x\r\n", (unsigned int)csw->dSignature); - USB_LOG_INFO(" tag: 0x%08x\r\n", (unsigned int)csw->dTag); - USB_LOG_INFO(" residue: 0x%08x\r\n", (unsigned int)csw->dDataResidue); - USB_LOG_INFO(" status: 0x%02x\r\n", csw->bStatus); -} - -static inline int usbh_msc_scsi_testunitready(struct usbh_msc *msc_class) -{ - int nbytes; - struct CBW *cbw; - - /* Construct the CBW */ - cbw = (struct CBW *)msc_class->tx_buffer; - memset(cbw, 0, USB_SIZEOF_MSC_CBW); - cbw->dSignature = MSC_CBW_Signature; - - cbw->bCBLength = SCSICMD_TESTUNITREADY_SIZEOF; - cbw->CB[0] = SCSI_CMD_TESTUNITREADY; - - /* Send the CBW */ - nbytes = usbh_ep_bulk_transfer(msc_class->bulkout, (uint8_t *)cbw, USB_SIZEOF_MSC_CBW); - if (nbytes >= 0) { - /* Receive the CSW */ - nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, USB_SIZEOF_MSC_CSW); - if (nbytes >= 0) { - usbh_msc_csw_dump((struct CSW *)msc_class->tx_buffer); - } - } - return nbytes < 0 ? (int)nbytes : 0; -} - -static inline int usbh_msc_scsi_requestsense(struct usbh_msc *msc_class) -{ - int nbytes; - struct CBW *cbw; - - /* Construct the CBW */ - cbw = (struct CBW *)msc_class->tx_buffer; - memset(cbw, 0, USB_SIZEOF_MSC_CBW); - cbw->dSignature = MSC_CBW_Signature; - - cbw->bmFlags = 0x80; - cbw->bCBLength = SCSIRESP_FIXEDSENSEDATA_SIZEOF; - cbw->dDataLength = SCSICMD_REQUESTSENSE_SIZEOF; - cbw->CB[0] = SCSI_REQUEST_SENSE; - cbw->CB[4] = SCSIRESP_FIXEDSENSEDATA_SIZEOF; - /* Send the CBW */ - nbytes = usbh_ep_bulk_transfer(msc_class->bulkout, (uint8_t *)cbw, USB_SIZEOF_MSC_CBW); - if (nbytes >= 0) { - /* Receive the sense data response */ - nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, SCSIRESP_FIXEDSENSEDATA_SIZEOF); - if (nbytes >= 0) { - /* Receive the CSW */ - nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, USB_SIZEOF_MSC_CSW); - if (nbytes >= 0) { - usbh_msc_csw_dump((struct CSW *)msc_class->tx_buffer); - } - } - } - return nbytes < 0 ? (int)nbytes : 0; -} - -static inline int usbh_msc_scsi_inquiry(struct usbh_msc *msc_class) -{ - int nbytes; - struct CBW *cbw; - - /* Construct the CBW */ - cbw = (struct CBW *)msc_class->tx_buffer; - memset(cbw, 0, USB_SIZEOF_MSC_CBW); - cbw->dSignature = MSC_CBW_Signature; - - cbw->dDataLength = SCSIRESP_INQUIRY_SIZEOF; - cbw->bmFlags = 0x80; - cbw->bCBLength = SCSICMD_INQUIRY_SIZEOF; - cbw->CB[0] = SCSI_INQUIRY; - cbw->CB[4] = SCSIRESP_INQUIRY_SIZEOF; - - /* Send the CBW */ - nbytes = usbh_ep_bulk_transfer(msc_class->bulkout, (uint8_t *)cbw, USB_SIZEOF_MSC_CBW); - if (nbytes >= 0) { - /* Receive the sense data response */ - nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, SCSIRESP_INQUIRY_SIZEOF); - if (nbytes >= 0) { - /* Receive the CSW */ - nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, USB_SIZEOF_MSC_CSW); - if (nbytes >= 0) { - usbh_msc_csw_dump((struct CSW *)msc_class->tx_buffer); - } - } - } - return nbytes < 0 ? (int)nbytes : 0; -} - -static inline int usbh_msc_scsi_readcapacity10(struct usbh_msc *msc_class) -{ - int nbytes; - struct CBW *cbw; - - /* Construct the CBW */ - cbw = (struct CBW *)msc_class->tx_buffer; - memset(cbw, 0, USB_SIZEOF_MSC_CBW); - cbw->dSignature = MSC_CBW_Signature; - - cbw->dDataLength = SCSIRESP_READCAPACITY10_SIZEOF; - cbw->bmFlags = 0x80; - cbw->bCBLength = SCSICMD_READCAPACITY10_SIZEOF; - cbw->CB[0] = SCSI_READ_CAPACITY10; - - /* Send the CBW */ - nbytes = usbh_ep_bulk_transfer(msc_class->bulkout, (uint8_t *)cbw, USB_SIZEOF_MSC_CBW); - if (nbytes >= 0) { - /* Receive the sense data response */ - nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, SCSIRESP_READCAPACITY10_SIZEOF); - if (nbytes >= 0) { - /* Save the capacity information */ - msc_class->blocknum = GET_BE32(&msc_class->tx_buffer[0]) + 1; - msc_class->blocksize = GET_BE32(&msc_class->tx_buffer[4]); - USB_LOG_INFO("capacity info:\r\n"); - USB_LOG_INFO("block num:%d,block size:%d\r\n", (unsigned int)msc_class->blocknum, (unsigned int)msc_class->blocksize); - /* Receive the CSW */ - nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, USB_SIZEOF_MSC_CSW); - if (nbytes >= 0) { - usbh_msc_csw_dump((struct CSW *)msc_class->tx_buffer); - } - } - } - return nbytes < 0 ? (int)nbytes : 0; -} - -int usbh_msc_mem_write(struct usbh_msc *msc_class, uint32_t sector, const uint8_t *buffer, uint32_t nsectors) -{ - int nbytes; - struct CBW *cbw; - - /* Construct the CBW */ - cbw = (struct CBW *)msc_class->tx_buffer; - memset(cbw, 0, USB_SIZEOF_MSC_CBW); - cbw->dSignature = MSC_CBW_Signature; - - cbw->dDataLength = (msc_class->blocksize * nsectors); - cbw->bCBLength = SCSICMD_WRITE10_SIZEOF; - cbw->CB[0] = SCSI_WRITE10; - - SET_BE24(&cbw->CB[2], sector); - SET_BE24(&cbw->CB[7], nsectors); - - /* Send the CBW */ - nbytes = usbh_ep_bulk_transfer(msc_class->bulkout, (uint8_t *)cbw, USB_SIZEOF_MSC_CBW); - if (nbytes >= 0) { - /* Send the user data */ - nbytes = usbh_ep_bulk_transfer(msc_class->bulkout, (uint8_t *)buffer, msc_class->blocksize * nsectors); - if (nbytes >= 0) { - /* Receive the CSW */ - nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, USB_SIZEOF_MSC_CSW); - if (nbytes >= 0) { - usbh_msc_csw_dump((struct CSW *)msc_class->tx_buffer); - } - } - } - return nbytes < 0 ? (int)nbytes : 0; -} - -int usbh_msc_mem_read(struct usbh_msc *msc_class, uint32_t sector, const uint8_t *buffer, uint32_t nsectors) -{ - int nbytes; - struct CBW *cbw; - - /* Construct the CBW */ - cbw = (struct CBW *)msc_class->tx_buffer; - memset(cbw, 0, USB_SIZEOF_MSC_CBW); - cbw->dSignature = MSC_CBW_Signature; - - cbw->dDataLength = (msc_class->blocksize * nsectors); - cbw->bmFlags = 0x80; - cbw->bCBLength = SCSICMD_READ10_SIZEOF; - cbw->CB[0] = SCSI_READ10; - - SET_BE24(&cbw->CB[2], sector); - SET_BE24(&cbw->CB[7], nsectors); - - /* Send the CBW */ - nbytes = usbh_ep_bulk_transfer(msc_class->bulkout, (uint8_t *)cbw, USB_SIZEOF_MSC_CBW); - if (nbytes >= 0) { - /* Receive the user data */ - nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, (uint8_t *)buffer, msc_class->blocksize * nsectors); - if (nbytes >= 0) { - /* Receive the CSW */ - nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, USB_SIZEOF_MSC_CSW); - if (nbytes >= 0) { - usbh_msc_csw_dump((struct CSW *)msc_class->tx_buffer); - } - } - } - return nbytes < 0 ? (int)nbytes : 0; -} - -int usbh_msc_connect(struct usbh_hubport *hport, uint8_t intf) -{ - struct usbh_endpoint_cfg ep_cfg = { 0 }; - struct usb_endpoint_descriptor *ep_desc; - - char devname[DEV_NAMELEN]; - int ret; - - struct usbh_msc *msc_class = usb_malloc(sizeof(struct usbh_msc)); - if (msc_class == NULL) { - USB_LOG_ERR("Fail to alloc msc_class\r\n"); - return -ENOMEM; - } - - memset(msc_class, 0, sizeof(struct usbh_msc)); - - usbh_msc_devno_alloc(msc_class); - usbh_msc_mkdevname(msc_class, devname); - - hport->config.intf[intf].priv = msc_class; - - msc_class->setup = usb_iomalloc(sizeof(struct usb_setup_packet)); - if (msc_class->setup == NULL) { - USB_LOG_ERR("Fail to alloc setup\r\n"); - return -ENOMEM; - } - msc_class->tx_buffer = usb_iomalloc(128); - if (msc_class->tx_buffer == NULL) { - USB_LOG_ERR("Fail to alloc tx_buffer\r\n"); - return -ENOMEM; - } - ret = usbh_msc_get_maxlun(hport, intf, msc_class->tx_buffer); - if (ret < 0) { - return ret; - } - - USB_LOG_INFO("Get max LUN:%u\r\n", msc_class->tx_buffer[0]); - - for (uint8_t i = 0; i < hport->config.intf[intf].intf_desc.bNumEndpoints; i++) { - ep_desc = &hport->config.intf[intf].ep[i].ep_desc; - - ep_cfg.ep_addr = ep_desc->bEndpointAddress; - ep_cfg.ep_type = ep_desc->bmAttributes & USB_ENDPOINT_TYPE_MASK; - ep_cfg.ep_mps = ep_desc->wMaxPacketSize; - ep_cfg.ep_interval = ep_desc->bInterval; - ep_cfg.hport = hport; - if (ep_desc->bEndpointAddress & 0x80) { - usbh_ep_alloc(&msc_class->bulkin, &ep_cfg); - } else { - usbh_ep_alloc(&msc_class->bulkout, &ep_cfg); - } - } - - USB_LOG_INFO("Register MSC Class:%s\r\n", devname); - - ret = usbh_msc_scsi_testunitready(msc_class); - ret = usbh_msc_scsi_inquiry(msc_class); - ret = usbh_msc_scsi_readcapacity10(msc_class); - - return ret; -} - -int usbh_msc_disconnect(struct usbh_hubport *hport, uint8_t intf) -{ - char devname[DEV_NAMELEN]; - int ret = 0; - - struct usbh_msc *msc_class = (struct usbh_msc *)hport->config.intf[intf].priv; - - if (msc_class) { - usbh_msc_devno_free(msc_class); - usbh_msc_mkdevname(msc_class, devname); - - if (msc_class->bulkin) { - ret = usb_ep_cancel(msc_class->bulkin); - if (ret < 0) { - } - usbh_ep_free(msc_class->bulkin); - } - - if (msc_class->bulkout) { - ret = usb_ep_cancel(msc_class->bulkout); - if (ret < 0) { - } - usbh_ep_free(msc_class->bulkout); - } - - if (msc_class->setup) - usb_iofree(msc_class->setup); - if (msc_class->tx_buffer) - usb_iofree(msc_class->tx_buffer); - - usb_free(msc_class); - - hport->config.intf[intf].priv = NULL; - - USB_LOG_INFO("Unregister MSC Class:%s\r\n", devname); - } - - return ret; -} - -const struct usbh_class_driver msc_class_driver = { - .driver_name = "msc", - .connect = usbh_msc_connect, - .disconnect = usbh_msc_disconnect +/** + * @file usbh_msc.c + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#include "usbh_core.h" +#include "usbh_msc.h" +#include "scsi.h" + +#define DEV_FORMAT "/dev/sd%c" +#define DEV_NAMELEN 16 + +static uint32_t g_devinuse = 0; + +/**************************************************************************** + * Name: usbh_msc_devno_alloc + * + * Description: + * Allocate a unique /dev/ttyACM[n] minor number in the range 0-31. + * + ****************************************************************************/ + +static int usbh_msc_devno_alloc(struct usbh_msc *priv) +{ + uint32_t flags; + int devno; + + flags = usb_osal_enter_critical_section(); + for (devno = 0; devno < 26; devno++) { + uint32_t bitno = 1 << devno; + if ((g_devinuse & bitno) == 0) { + g_devinuse |= bitno; + priv->sdchar = 'a' + devno; + usb_osal_leave_critical_section(flags); + return 0; + } + } + + usb_osal_leave_critical_section(flags); + return -EMFILE; +} + +/**************************************************************************** + * Name: usbh_msc_devno_free + * + * Description: + * Free a /dev/sd[n] minor number so that it can be used. + * + ****************************************************************************/ + +static void usbh_msc_devno_free(struct usbh_msc *priv) +{ + int devno = priv->sdchar - 'a'; + + if (devno >= 0 && devno < 26) { + uint32_t flags = usb_osal_enter_critical_section(); + g_devinuse &= ~(1 << devno); + usb_osal_leave_critical_section(flags); + } +} + +/**************************************************************************** + * Name: usbh_msc_mkdevname + * + * Description: + * Format a /dev/sd[n] device name given a minor number. + * + ****************************************************************************/ + +static inline void usbh_msc_mkdevname(struct usbh_msc *priv, char *devname) +{ + snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->sdchar); +} + +static int usbh_msc_get_maxlun(struct usbh_hubport *hport, uint8_t intf, uint8_t *buffer) +{ + struct usb_setup_packet *setup; + struct usbh_msc *msc_class = (struct usbh_msc *)hport->config.intf[intf].priv; + + setup = msc_class->setup; + + if (msc_class->intf != intf) { + return -1; + } + + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; + setup->bRequest = MSC_REQUEST_GET_MAX_LUN; + setup->wValue = 0; + setup->wIndex = intf; + setup->wLength = 1; + + return usbh_control_transfer(hport->ep0, setup, buffer); +} + +static void usbh_msc_cbw_dump(struct CBW *cbw) +{ + int i; + + USB_LOG_INFO("CBW:\r\n"); + USB_LOG_INFO(" signature: 0x%08x\r\n", (unsigned int)cbw->dSignature); + USB_LOG_INFO(" tag: 0x%08x\r\n", (unsigned int)cbw->dTag); + USB_LOG_INFO(" datlen: 0x%08x\r\n", (unsigned int)cbw->dDataLength); + USB_LOG_INFO(" flags: 0x%02x\r\n", cbw->bmFlags); + USB_LOG_INFO(" lun: 0x%02x\r\n", cbw->bLUN); + USB_LOG_INFO(" cblen: 0x%02x\r\n", cbw->bCBLength); + + USB_LOG_INFO("CB:\r\n"); + for (i = 0; i < cbw->bCBLength; i += 8) { + USB_LOG_INFO(" 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\r\n", + cbw->CB[i], cbw->CB[i + 1], cbw->CB[i + 2], + cbw->CB[i + 3], cbw->CB[i + 4], cbw->CB[i + 5], + cbw->CB[i + 6], cbw->CB[i + 7]); + } +} + +static void usbh_msc_csw_dump(struct CSW *csw) +{ + USB_LOG_INFO("CSW:\r\n"); + USB_LOG_INFO(" signature: 0x%08x\r\n", (unsigned int)csw->dSignature); + USB_LOG_INFO(" tag: 0x%08x\r\n", (unsigned int)csw->dTag); + USB_LOG_INFO(" residue: 0x%08x\r\n", (unsigned int)csw->dDataResidue); + USB_LOG_INFO(" status: 0x%02x\r\n", csw->bStatus); +} + +static inline int usbh_msc_scsi_testunitready(struct usbh_msc *msc_class) +{ + int nbytes; + struct CBW *cbw; + + /* Construct the CBW */ + cbw = (struct CBW *)msc_class->tx_buffer; + memset(cbw, 0, USB_SIZEOF_MSC_CBW); + cbw->dSignature = MSC_CBW_Signature; + + cbw->bCBLength = SCSICMD_TESTUNITREADY_SIZEOF; + cbw->CB[0] = SCSI_CMD_TESTUNITREADY; + + /* Send the CBW */ + nbytes = usbh_ep_bulk_transfer(msc_class->bulkout, (uint8_t *)cbw, USB_SIZEOF_MSC_CBW); + if (nbytes >= 0) { + /* Receive the CSW */ + nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, USB_SIZEOF_MSC_CSW); + if (nbytes >= 0) { + usbh_msc_csw_dump((struct CSW *)msc_class->tx_buffer); + } + } + return nbytes < 0 ? (int)nbytes : 0; +} + +static inline int usbh_msc_scsi_requestsense(struct usbh_msc *msc_class) +{ + int nbytes; + struct CBW *cbw; + + /* Construct the CBW */ + cbw = (struct CBW *)msc_class->tx_buffer; + memset(cbw, 0, USB_SIZEOF_MSC_CBW); + cbw->dSignature = MSC_CBW_Signature; + + cbw->bmFlags = 0x80; + cbw->bCBLength = SCSIRESP_FIXEDSENSEDATA_SIZEOF; + cbw->dDataLength = SCSICMD_REQUESTSENSE_SIZEOF; + cbw->CB[0] = SCSI_REQUEST_SENSE; + cbw->CB[4] = SCSIRESP_FIXEDSENSEDATA_SIZEOF; + /* Send the CBW */ + nbytes = usbh_ep_bulk_transfer(msc_class->bulkout, (uint8_t *)cbw, USB_SIZEOF_MSC_CBW); + if (nbytes >= 0) { + /* Receive the sense data response */ + nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, SCSIRESP_FIXEDSENSEDATA_SIZEOF); + if (nbytes >= 0) { + /* Receive the CSW */ + nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, USB_SIZEOF_MSC_CSW); + if (nbytes >= 0) { + usbh_msc_csw_dump((struct CSW *)msc_class->tx_buffer); + } + } + } + return nbytes < 0 ? (int)nbytes : 0; +} + +static inline int usbh_msc_scsi_inquiry(struct usbh_msc *msc_class) +{ + int nbytes; + struct CBW *cbw; + + /* Construct the CBW */ + cbw = (struct CBW *)msc_class->tx_buffer; + memset(cbw, 0, USB_SIZEOF_MSC_CBW); + cbw->dSignature = MSC_CBW_Signature; + + cbw->dDataLength = SCSIRESP_INQUIRY_SIZEOF; + cbw->bmFlags = 0x80; + cbw->bCBLength = SCSICMD_INQUIRY_SIZEOF; + cbw->CB[0] = SCSI_INQUIRY; + cbw->CB[4] = SCSIRESP_INQUIRY_SIZEOF; + + /* Send the CBW */ + nbytes = usbh_ep_bulk_transfer(msc_class->bulkout, (uint8_t *)cbw, USB_SIZEOF_MSC_CBW); + if (nbytes >= 0) { + /* Receive the sense data response */ + nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, SCSIRESP_INQUIRY_SIZEOF); + if (nbytes >= 0) { + /* Receive the CSW */ + nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, USB_SIZEOF_MSC_CSW); + if (nbytes >= 0) { + usbh_msc_csw_dump((struct CSW *)msc_class->tx_buffer); + } + } + } + return nbytes < 0 ? (int)nbytes : 0; +} + +static inline int usbh_msc_scsi_readcapacity10(struct usbh_msc *msc_class) +{ + int nbytes; + struct CBW *cbw; + + /* Construct the CBW */ + cbw = (struct CBW *)msc_class->tx_buffer; + memset(cbw, 0, USB_SIZEOF_MSC_CBW); + cbw->dSignature = MSC_CBW_Signature; + + cbw->dDataLength = SCSIRESP_READCAPACITY10_SIZEOF; + cbw->bmFlags = 0x80; + cbw->bCBLength = SCSICMD_READCAPACITY10_SIZEOF; + cbw->CB[0] = SCSI_READ_CAPACITY10; + + /* Send the CBW */ + nbytes = usbh_ep_bulk_transfer(msc_class->bulkout, (uint8_t *)cbw, USB_SIZEOF_MSC_CBW); + if (nbytes >= 0) { + /* Receive the sense data response */ + nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, SCSIRESP_READCAPACITY10_SIZEOF); + if (nbytes >= 0) { + /* Save the capacity information */ + msc_class->blocknum = GET_BE32(&msc_class->tx_buffer[0]) + 1; + msc_class->blocksize = GET_BE32(&msc_class->tx_buffer[4]); + USB_LOG_INFO("capacity info:\r\n"); + USB_LOG_INFO("block num:%d,block size:%d\r\n", (unsigned int)msc_class->blocknum, (unsigned int)msc_class->blocksize); + /* Receive the CSW */ + nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, USB_SIZEOF_MSC_CSW); + if (nbytes >= 0) { + usbh_msc_csw_dump((struct CSW *)msc_class->tx_buffer); + } + } + } + return nbytes < 0 ? (int)nbytes : 0; +} + +int usbh_msc_mem_write(struct usbh_msc *msc_class, uint32_t sector, const uint8_t *buffer, uint32_t nsectors) +{ + int nbytes; + struct CBW *cbw; + + /* Construct the CBW */ + cbw = (struct CBW *)msc_class->tx_buffer; + memset(cbw, 0, USB_SIZEOF_MSC_CBW); + cbw->dSignature = MSC_CBW_Signature; + + cbw->dDataLength = (msc_class->blocksize * nsectors); + cbw->bCBLength = SCSICMD_WRITE10_SIZEOF; + cbw->CB[0] = SCSI_WRITE10; + + SET_BE24(&cbw->CB[2], sector); + SET_BE24(&cbw->CB[7], nsectors); + + /* Send the CBW */ + nbytes = usbh_ep_bulk_transfer(msc_class->bulkout, (uint8_t *)cbw, USB_SIZEOF_MSC_CBW); + if (nbytes >= 0) { + /* Send the user data */ + nbytes = usbh_ep_bulk_transfer(msc_class->bulkout, (uint8_t *)buffer, msc_class->blocksize * nsectors); + if (nbytes >= 0) { + /* Receive the CSW */ + nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, USB_SIZEOF_MSC_CSW); + if (nbytes >= 0) { + usbh_msc_csw_dump((struct CSW *)msc_class->tx_buffer); + } + } + } + return nbytes < 0 ? (int)nbytes : 0; +} + +int usbh_msc_mem_read(struct usbh_msc *msc_class, uint32_t sector, const uint8_t *buffer, uint32_t nsectors) +{ + int nbytes; + struct CBW *cbw; + + /* Construct the CBW */ + cbw = (struct CBW *)msc_class->tx_buffer; + memset(cbw, 0, USB_SIZEOF_MSC_CBW); + cbw->dSignature = MSC_CBW_Signature; + + cbw->dDataLength = (msc_class->blocksize * nsectors); + cbw->bmFlags = 0x80; + cbw->bCBLength = SCSICMD_READ10_SIZEOF; + cbw->CB[0] = SCSI_READ10; + + SET_BE24(&cbw->CB[2], sector); + SET_BE24(&cbw->CB[7], nsectors); + + /* Send the CBW */ + nbytes = usbh_ep_bulk_transfer(msc_class->bulkout, (uint8_t *)cbw, USB_SIZEOF_MSC_CBW); + if (nbytes >= 0) { + /* Receive the user data */ + nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, (uint8_t *)buffer, msc_class->blocksize * nsectors); + if (nbytes >= 0) { + /* Receive the CSW */ + nbytes = usbh_ep_bulk_transfer(msc_class->bulkin, msc_class->tx_buffer, USB_SIZEOF_MSC_CSW); + if (nbytes >= 0) { + usbh_msc_csw_dump((struct CSW *)msc_class->tx_buffer); + } + } + } + return nbytes < 0 ? (int)nbytes : 0; +} + +int usbh_msc_connect(struct usbh_hubport *hport, uint8_t intf) +{ + struct usbh_endpoint_cfg ep_cfg = { 0 }; + struct usb_endpoint_descriptor *ep_desc; + + char devname[DEV_NAMELEN]; + int ret; + + struct usbh_msc *msc_class = usb_malloc(sizeof(struct usbh_msc)); + if (msc_class == NULL) { + USB_LOG_ERR("Fail to alloc msc_class\r\n"); + return -ENOMEM; + } + + memset(msc_class, 0, sizeof(struct usbh_msc)); + + usbh_msc_devno_alloc(msc_class); + usbh_msc_mkdevname(msc_class, devname); + + hport->config.intf[intf].priv = msc_class; + + msc_class->setup = usb_iomalloc(sizeof(struct usb_setup_packet)); + if (msc_class->setup == NULL) { + USB_LOG_ERR("Fail to alloc setup\r\n"); + return -ENOMEM; + } + msc_class->tx_buffer = usb_iomalloc(128); + if (msc_class->tx_buffer == NULL) { + USB_LOG_ERR("Fail to alloc tx_buffer\r\n"); + return -ENOMEM; + } + ret = usbh_msc_get_maxlun(hport, intf, msc_class->tx_buffer); + if (ret < 0) { + return ret; + } + + USB_LOG_INFO("Get max LUN:%u\r\n", msc_class->tx_buffer[0]); + + for (uint8_t i = 0; i < hport->config.intf[intf].intf_desc.bNumEndpoints; i++) { + ep_desc = &hport->config.intf[intf].ep[i].ep_desc; + + ep_cfg.ep_addr = ep_desc->bEndpointAddress; + ep_cfg.ep_type = ep_desc->bmAttributes & USB_ENDPOINT_TYPE_MASK; + ep_cfg.ep_mps = ep_desc->wMaxPacketSize; + ep_cfg.ep_interval = ep_desc->bInterval; + ep_cfg.hport = hport; + if (ep_desc->bEndpointAddress & 0x80) { + usbh_ep_alloc(&msc_class->bulkin, &ep_cfg); + } else { + usbh_ep_alloc(&msc_class->bulkout, &ep_cfg); + } + } + + USB_LOG_INFO("Register MSC Class:%s\r\n", devname); + + ret = usbh_msc_scsi_testunitready(msc_class); + ret = usbh_msc_scsi_inquiry(msc_class); + ret = usbh_msc_scsi_readcapacity10(msc_class); + + return ret; +} + +int usbh_msc_disconnect(struct usbh_hubport *hport, uint8_t intf) +{ + char devname[DEV_NAMELEN]; + int ret = 0; + + struct usbh_msc *msc_class = (struct usbh_msc *)hport->config.intf[intf].priv; + + if (msc_class) { + usbh_msc_devno_free(msc_class); + usbh_msc_mkdevname(msc_class, devname); + + if (msc_class->bulkin) { + ret = usb_ep_cancel(msc_class->bulkin); + if (ret < 0) { + } + usbh_ep_free(msc_class->bulkin); + } + + if (msc_class->bulkout) { + ret = usb_ep_cancel(msc_class->bulkout); + if (ret < 0) { + } + usbh_ep_free(msc_class->bulkout); + } + + if (msc_class->setup) + usb_iofree(msc_class->setup); + if (msc_class->tx_buffer) + usb_iofree(msc_class->tx_buffer); + + usb_free(msc_class); + + hport->config.intf[intf].priv = NULL; + + USB_LOG_INFO("Unregister MSC Class:%s\r\n", devname); + } + + return ret; +} + +const struct usbh_class_driver msc_class_driver = { + .driver_name = "msc", + .connect = usbh_msc_connect, + .disconnect = usbh_msc_disconnect }; \ No newline at end of file diff --git a/class/msc/usbh_msc.h b/class/msc/usbh_msc.h index ccc7806e..7318028a 100644 --- a/class/msc/usbh_msc.h +++ b/class/msc/usbh_msc.h @@ -1,49 +1,49 @@ -/** - * @file usbh_msc.h - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#ifndef _USBH_MSC_H -#define _USBH_MSC_H - -#include "usb_msc.h" -#include "usb_scsi.h" - -struct usbh_msc { - struct usb_setup_packet *setup; - uint8_t intf; /* Data interface number */ - uint8_t sdchar; - usbh_epinfo_t bulkin; /* Bulk IN endpoint */ - usbh_epinfo_t bulkout; /* Bulk OUT endpoint */ - uint8_t *tx_buffer; - uint32_t blocknum; /* Number of blocks on the USB mass storage device */ - uint16_t blocksize; /* Block size of USB mass storage device */ -}; - -extern const struct usbh_class_driver msc_class_driver; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif - +/** + * @file usbh_msc.h + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _USBH_MSC_H +#define _USBH_MSC_H + +#include "usb_msc.h" +#include "usb_scsi.h" + +struct usbh_msc { + struct usb_setup_packet *setup; + uint8_t intf; /* Data interface number */ + uint8_t sdchar; + usbh_epinfo_t bulkin; /* Bulk IN endpoint */ + usbh_epinfo_t bulkout; /* Bulk OUT endpoint */ + uint8_t *tx_buffer; + uint32_t blocknum; /* Number of blocks on the USB mass storage device */ + uint16_t blocksize; /* Block size of USB mass storage device */ +}; + +extern const struct usbh_class_driver msc_class_driver; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + #endif \ No newline at end of file diff --git a/class/video/usbd_video.c b/class/video/usbd_video.c index ae71171d..ccf30da9 100644 --- a/class/video/usbd_video.c +++ b/class/video/usbd_video.c @@ -1,134 +1,134 @@ -/** - * @file usbd_video.c - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#include "usbd_core.h" -#include "usbd_video.h" - -extern struct video_probe_and_commit_controls probe; -extern struct video_probe_and_commit_controls commit; - -int video_class_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) -{ - USB_LOG_DBG("Video Class request: " - "bRequest 0x%02x\r\n", - setup->bRequest); - - switch (setup->bRequest) { - case VIDEO_REQUEST_SET_CUR: - if (setup->wValue == 256) { - memcpy((uint8_t *)&probe, *data, setup->wLength); - } else if (setup->wValue == 512) { - memcpy((uint8_t *)&commit, *data, setup->wLength); - } - - break; - - case VIDEO_REQUEST_GET_CUR: - if (setup->wValue == 256) { - *data = (uint8_t *)&probe; - } else if (setup->wValue == 512) { - *data = (uint8_t *)&commit; - } - - break; - - case VIDEO_REQUEST_GET_MIN: - if (setup->wValue == 256) { - *data = (uint8_t *)&probe; - } else if (setup->wValue == 512) { - *data = (uint8_t *)&commit; - } - - break; - - case VIDEO_REQUEST_GET_MAX: - if (setup->wValue == 256) { - *data = (uint8_t *)&probe; - } else if (setup->wValue == 512) { - *data = (uint8_t *)&commit; - } - - break; - - case VIDEO_REQUEST_GET_RES: - - break; - - case VIDEO_REQUEST_GET_LEN: - - break; - - case VIDEO_REQUEST_GET_INFO: - - break; - - case VIDEO_REQUEST_GET_DEF: - if (setup->wLength == 256) { - *data = (uint8_t *)&probe; - } else if (setup->wLength == 512) { - *data = (uint8_t *)&commit; - } - - break; - - default: - USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); - return -1; - } - - return 0; -} - -void video_notify_handler(uint8_t event, void *arg) -{ - switch (event) { - case USBD_EVENT_RESET: - - break; - - case USBD_EVENT_SOF: - usbd_video_sof_callback(); - break; - - case USBD_EVENT_SET_INTERFACE: - usbd_video_set_interface_callback(((uint8_t *)arg)[3]); - break; - - default: - break; - } -} - -void usbd_video_add_interface(usbd_class_t *devclass, usbd_interface_t *intf) -{ - static usbd_class_t *last_class = NULL; - - if (last_class != devclass) { - last_class = devclass; - usbd_class_register(devclass); - } - - intf->class_handler = video_class_request_handler; - intf->custom_handler = NULL; - intf->vendor_handler = NULL; - intf->notify_handler = video_notify_handler; - usbd_class_add_interface(devclass, intf); +/** + * @file usbd_video.c + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#include "usbd_core.h" +#include "usbd_video.h" + +extern struct video_probe_and_commit_controls probe; +extern struct video_probe_and_commit_controls commit; + +int video_class_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) +{ + USB_LOG_DBG("Video Class request: " + "bRequest 0x%02x\r\n", + setup->bRequest); + + switch (setup->bRequest) { + case VIDEO_REQUEST_SET_CUR: + if (setup->wValue == 256) { + memcpy((uint8_t *)&probe, *data, setup->wLength); + } else if (setup->wValue == 512) { + memcpy((uint8_t *)&commit, *data, setup->wLength); + } + + break; + + case VIDEO_REQUEST_GET_CUR: + if (setup->wValue == 256) { + *data = (uint8_t *)&probe; + } else if (setup->wValue == 512) { + *data = (uint8_t *)&commit; + } + + break; + + case VIDEO_REQUEST_GET_MIN: + if (setup->wValue == 256) { + *data = (uint8_t *)&probe; + } else if (setup->wValue == 512) { + *data = (uint8_t *)&commit; + } + + break; + + case VIDEO_REQUEST_GET_MAX: + if (setup->wValue == 256) { + *data = (uint8_t *)&probe; + } else if (setup->wValue == 512) { + *data = (uint8_t *)&commit; + } + + break; + + case VIDEO_REQUEST_GET_RES: + + break; + + case VIDEO_REQUEST_GET_LEN: + + break; + + case VIDEO_REQUEST_GET_INFO: + + break; + + case VIDEO_REQUEST_GET_DEF: + if (setup->wLength == 256) { + *data = (uint8_t *)&probe; + } else if (setup->wLength == 512) { + *data = (uint8_t *)&commit; + } + + break; + + default: + USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); + return -1; + } + + return 0; +} + +void video_notify_handler(uint8_t event, void *arg) +{ + switch (event) { + case USBD_EVENT_RESET: + + break; + + case USBD_EVENT_SOF: + usbd_video_sof_callback(); + break; + + case USBD_EVENT_SET_INTERFACE: + usbd_video_set_interface_callback(((uint8_t *)arg)[3]); + break; + + default: + break; + } +} + +void usbd_video_add_interface(usbd_class_t *devclass, usbd_interface_t *intf) +{ + static usbd_class_t *last_class = NULL; + + if (last_class != devclass) { + last_class = devclass; + usbd_class_register(devclass); + } + + intf->class_handler = video_class_request_handler; + intf->custom_handler = NULL; + intf->vendor_handler = NULL; + intf->notify_handler = video_notify_handler; + usbd_class_add_interface(devclass, intf); } \ No newline at end of file diff --git a/common/usb_dc.h b/common/usb_dc.h index 139b0ae7..0b8ae7e7 100644 --- a/common/usb_dc.h +++ b/common/usb_dc.h @@ -1,200 +1,200 @@ -/** - * @file usb_dc.h - * @brief - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#ifndef _USB_DC_H -#define _USB_DC_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * USB endpoint direction and number. - */ -#define USB_EP_DIR_MASK 0x80U -#define USB_EP_DIR_IN 0x80U -#define USB_EP_DIR_OUT 0x00U - -/** Get endpoint index (number) from endpoint address */ -#define USB_EP_GET_IDX(ep) ((ep) & ~USB_EP_DIR_MASK) -/** Get direction from endpoint address */ -#define USB_EP_GET_DIR(ep) ((ep)&USB_EP_DIR_MASK) -/** Get endpoint address from endpoint index and direction */ -#define USB_EP_GET_ADDR(idx, dir) ((idx) | ((dir)&USB_EP_DIR_MASK)) -/** True if the endpoint is an IN endpoint */ -#define USB_EP_DIR_IS_IN(ep) (USB_EP_GET_DIR(ep) == USB_EP_DIR_IN) -/** True if the endpoint is an OUT endpoint */ -#define USB_EP_DIR_IS_OUT(ep) (USB_EP_GET_DIR(ep) == USB_EP_DIR_OUT) - -/* Default USB control EP, always 0 and 0x80 */ -#define USB_CONTROL_OUT_EP0 0 -#define USB_CONTROL_IN_EP0 0x80 - -/**< maximum packet size (MPS) for EP 0 */ -#define USB_CTRL_EP_MPS 64 - -/** - * USB endpoint Transfer Type mask. - */ -#define USBD_EP_TYPE_CTRL 0 -#define USBD_EP_TYPE_ISOC 1 -#define USBD_EP_TYPE_BULK 2 -#define USBD_EP_TYPE_INTR 3 -#define USBD_EP_TYPE_MASK 3 - -/** - * @brief USB Endpoint Configuration. - * - * Structure containing the USB endpoint configuration. - */ -struct usbd_endpoint_cfg { - /** The number associated with the EP in the device - * configuration structure - * IN EP = 0x80 | \ - * OUT EP = 0x00 | \ - */ - uint8_t ep_addr; - /** Endpoint Transfer Type. - * May be Bulk, Interrupt, Control or Isochronous - */ - uint8_t ep_type; - /** Endpoint max packet size */ - uint16_t ep_mps; -}; - -/** - * @brief USB Device Core Layer API - * @defgroup _usb_device_core_api USB Device Core API - * @{ - */ - -/** - * @brief Set USB device address - * - * @param[in] addr Device address - * - * @return 0 on success, negative errno code on fail. - */ -int usbd_set_address(const uint8_t addr); - -/* - * @brief configure and enable endpoint - * - * This function sets endpoint configuration according to one specified in USB - * endpoint descriptor and then enables it for data transfers. - * - * @param [in] ep_desc Endpoint descriptor byte array - * - * @return true if successfully configured and enabled - */ -int usbd_ep_open(const struct usbd_endpoint_cfg *ep_cfg); -/** - * @brief Disable the selected endpoint - * - * Function to disable the selected endpoint. Upon success interrupts are - * disabled for the corresponding endpoint and the endpoint is no longer able - * for transmitting/receiving data. - * - * @param[in] ep Endpoint address corresponding to the one - * listed in the device configuration table - * - * @return 0 on success, negative errno code on fail. - */ -int usbd_ep_close(const uint8_t ep); -/** - * @brief Set stall condition for the selected endpoint - * - * @param[in] ep Endpoint address corresponding to the one - * listed in the device configuration table - * - * @return 0 on success, negative errno code on fail. - */ -int usbd_ep_set_stall(const uint8_t ep); -/** - * @brief Clear stall condition for the selected endpoint - * - * @param[in] ep Endpoint address corresponding to the one - * listed in the device configuration table - * - * @return 0 on success, negative errno code on fail. - */ -int usbd_ep_clear_stall(const uint8_t ep); -/** - * @brief Check if the selected endpoint is stalled - * - * @param[in] ep Endpoint address corresponding to the one - * listed in the device configuration table - * @param[out] stalled Endpoint stall status - * - * @return 0 on success, negative errno code on fail. - */ -int usbd_ep_is_stalled(const uint8_t ep, uint8_t *stalled); -/** - * @brief Write data to the specified endpoint - * - * This function is called to write data to the specified endpoint. The - * supplied usbd_endpoint_callback function will be called when data is transmitted - * out. - * - * @param[in] ep Endpoint address corresponding to the one - * listed in the device configuration table - * @param[in] data Pointer to data to write - * @param[in] data_len Length of the data requested to write. This may - * be zero for a zero length status packet. - * @param[out] ret_bytes Bytes scheduled for transmission. This value - * may be NULL if the application expects all - * bytes to be written - * - * @return 0 on success, negative errno code on fail. - */ -int usbd_ep_write(const uint8_t ep, const uint8_t *data, uint32_t data_len, uint32_t *ret_bytes); -/** - * @brief Read data from the specified endpoint - * - * This is similar to usb_dc_ep_read, the difference being that, it doesn't - * clear the endpoint NAKs so that the consumer is not bogged down by further - * upcalls till he is done with the processing of the data. The caller should - * reactivate ep by setting max_data_len 0 do so. - * - * @param[in] ep Endpoint address corresponding to the one - * listed in the device configuration table - * @param[in] data Pointer to data buffer to write to - * @param[in] max_data_len Max length of data to read - * @param[out] read_bytes Number of bytes read. If data is NULL and - * max_data_len is 0 the number of bytes - * available for read should be returned. - * - * @return 0 on success, negative errno code on fail. - */ -int usbd_ep_read(const uint8_t ep, uint8_t *data, uint32_t max_data_len, uint32_t *read_bytes); - -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif +/** + * @file usb_dc.h + * @brief + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _USB_DC_H +#define _USB_DC_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * USB endpoint direction and number. + */ +#define USB_EP_DIR_MASK 0x80U +#define USB_EP_DIR_IN 0x80U +#define USB_EP_DIR_OUT 0x00U + +/** Get endpoint index (number) from endpoint address */ +#define USB_EP_GET_IDX(ep) ((ep) & ~USB_EP_DIR_MASK) +/** Get direction from endpoint address */ +#define USB_EP_GET_DIR(ep) ((ep)&USB_EP_DIR_MASK) +/** Get endpoint address from endpoint index and direction */ +#define USB_EP_GET_ADDR(idx, dir) ((idx) | ((dir)&USB_EP_DIR_MASK)) +/** True if the endpoint is an IN endpoint */ +#define USB_EP_DIR_IS_IN(ep) (USB_EP_GET_DIR(ep) == USB_EP_DIR_IN) +/** True if the endpoint is an OUT endpoint */ +#define USB_EP_DIR_IS_OUT(ep) (USB_EP_GET_DIR(ep) == USB_EP_DIR_OUT) + +/* Default USB control EP, always 0 and 0x80 */ +#define USB_CONTROL_OUT_EP0 0 +#define USB_CONTROL_IN_EP0 0x80 + +/**< maximum packet size (MPS) for EP 0 */ +#define USB_CTRL_EP_MPS 64 + +/** + * USB endpoint Transfer Type mask. + */ +#define USBD_EP_TYPE_CTRL 0 +#define USBD_EP_TYPE_ISOC 1 +#define USBD_EP_TYPE_BULK 2 +#define USBD_EP_TYPE_INTR 3 +#define USBD_EP_TYPE_MASK 3 + +/** + * @brief USB Endpoint Configuration. + * + * Structure containing the USB endpoint configuration. + */ +struct usbd_endpoint_cfg { + /** The number associated with the EP in the device + * configuration structure + * IN EP = 0x80 | \ + * OUT EP = 0x00 | \ + */ + uint8_t ep_addr; + /** Endpoint Transfer Type. + * May be Bulk, Interrupt, Control or Isochronous + */ + uint8_t ep_type; + /** Endpoint max packet size */ + uint16_t ep_mps; +}; + +/** + * @brief USB Device Core Layer API + * @defgroup _usb_device_core_api USB Device Core API + * @{ + */ + +/** + * @brief Set USB device address + * + * @param[in] addr Device address + * + * @return 0 on success, negative errno code on fail. + */ +int usbd_set_address(const uint8_t addr); + +/* + * @brief configure and enable endpoint + * + * This function sets endpoint configuration according to one specified in USB + * endpoint descriptor and then enables it for data transfers. + * + * @param [in] ep_desc Endpoint descriptor byte array + * + * @return true if successfully configured and enabled + */ +int usbd_ep_open(const struct usbd_endpoint_cfg *ep_cfg); +/** + * @brief Disable the selected endpoint + * + * Function to disable the selected endpoint. Upon success interrupts are + * disabled for the corresponding endpoint and the endpoint is no longer able + * for transmitting/receiving data. + * + * @param[in] ep Endpoint address corresponding to the one + * listed in the device configuration table + * + * @return 0 on success, negative errno code on fail. + */ +int usbd_ep_close(const uint8_t ep); +/** + * @brief Set stall condition for the selected endpoint + * + * @param[in] ep Endpoint address corresponding to the one + * listed in the device configuration table + * + * @return 0 on success, negative errno code on fail. + */ +int usbd_ep_set_stall(const uint8_t ep); +/** + * @brief Clear stall condition for the selected endpoint + * + * @param[in] ep Endpoint address corresponding to the one + * listed in the device configuration table + * + * @return 0 on success, negative errno code on fail. + */ +int usbd_ep_clear_stall(const uint8_t ep); +/** + * @brief Check if the selected endpoint is stalled + * + * @param[in] ep Endpoint address corresponding to the one + * listed in the device configuration table + * @param[out] stalled Endpoint stall status + * + * @return 0 on success, negative errno code on fail. + */ +int usbd_ep_is_stalled(const uint8_t ep, uint8_t *stalled); +/** + * @brief Write data to the specified endpoint + * + * This function is called to write data to the specified endpoint. The + * supplied usbd_endpoint_callback function will be called when data is transmitted + * out. + * + * @param[in] ep Endpoint address corresponding to the one + * listed in the device configuration table + * @param[in] data Pointer to data to write + * @param[in] data_len Length of the data requested to write. This may + * be zero for a zero length status packet. + * @param[out] ret_bytes Bytes scheduled for transmission. This value + * may be NULL if the application expects all + * bytes to be written + * + * @return 0 on success, negative errno code on fail. + */ +int usbd_ep_write(const uint8_t ep, const uint8_t *data, uint32_t data_len, uint32_t *ret_bytes); +/** + * @brief Read data from the specified endpoint + * + * This is similar to usb_dc_ep_read, the difference being that, it doesn't + * clear the endpoint NAKs so that the consumer is not bogged down by further + * upcalls till he is done with the processing of the data. The caller should + * reactivate ep by setting max_data_len 0 do so. + * + * @param[in] ep Endpoint address corresponding to the one + * listed in the device configuration table + * @param[in] data Pointer to data buffer to write to + * @param[in] max_data_len Max length of data to read + * @param[out] read_bytes Number of bytes read. If data is NULL and + * max_data_len is 0 the number of bytes + * available for read should be returned. + * + * @return 0 on success, negative errno code on fail. + */ +int usbd_ep_read(const uint8_t ep, uint8_t *data, uint32_t max_data_len, uint32_t *read_bytes); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/common/usb_def.h b/common/usb_def.h index f6b9cbda..ae5a468f 100644 --- a/common/usb_def.h +++ b/common/usb_def.h @@ -1,671 +1,671 @@ -/** - * @file usb_def.h - * @brief - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#ifndef USB_DEF_H -#define USB_DEF_H - -/* Useful define */ -#define USB_1_1 0x0110 -#define USB_2_0 0x0200 -/* Set USB version to 2.1 so that the host will request the BOS descriptor */ -#define USB_2_1 0x0210 - -/* Device speeds */ -#define USB_SPEED_UNKNOWN 0 /* Transfer rate not yet set */ -#define USB_SPEED_LOW 1 /* USB 1.1 */ -#define USB_SPEED_FULL 2 /* USB 1.1 */ -#define USB_SPEED_HIGH 3 /* USB 2.0 */ -#define USB_SPEED_VARIABLE 4 /* Wireless USB 2.5 */ - -/* Maximum number of devices per controller */ -#define USB_MAX_DEVICES (127) - -// USB PID Types -#define USB_PID_OUT (0x01) /* Tokens */ -#define USB_PID_IN (0x09) -#define USB_PID_SOF (0x05) -#define USB_PID_SETUP (0x0d) - -#define USB_PID_DATA0 (0x03) /* Data */ -#define USB_PID_DATA1 (0x0b) -#define USB_PID_DATA2 (0x07) -#define USB_PID_MDATA (0x0f) - -#define USB_PID_ACK (0x02) /* Handshake */ -#define USB_PID_NAK (0x0a) -#define USB_PID_STALL (0x0e) -#define USB_PID_NYET (0x06) - -#define USB_PID_PRE (0x0c) /* Special */ -#define USB_PID_ERR (0x0c) -#define USB_PID_SPLIT (0x08) -#define USB_PID_PING (0x04) -#define USB_PID_RESERVED (0x00) - -#define USB_REQUEST_DIR_SHIFT 7U /* Bits 7: Request dir */ -#define USB_REQUEST_DIR_OUT (0U << USB_REQUEST_DIR_SHIFT) /* Bit 7=0: Host-to-device */ -#define USB_REQUEST_DIR_IN (1U << USB_REQUEST_DIR_SHIFT) /* Bit 7=1: Device-to-host */ -#define USB_REQUEST_DIR_MASK (1U << USB_REQUEST_DIR_SHIFT) /* Bit 7=1: Direction bit */ - -#define USB_REQUEST_TYPE_SHIFT 5U /* Bits 5:6: Request type */ -#define USB_REQUEST_STANDARD (0U << USB_REQUEST_TYPE_SHIFT) -#define USB_REQUEST_CLASS (1U << USB_REQUEST_TYPE_SHIFT) -#define USB_REQUEST_VENDOR (2U << USB_REQUEST_TYPE_SHIFT) -#define USB_REQUEST_RESERVED (3U << USB_REQUEST_TYPE_SHIFT) -#define USB_REQUEST_TYPE_MASK (3U << USB_REQUEST_TYPE_SHIFT) - -#define USB_REQUEST_RECIPIENT_SHIFT 0U /* Bits 0:4: Recipient */ -#define USB_REQUEST_RECIPIENT_DEVICE (0U << USB_REQUEST_RECIPIENT_SHIFT) -#define USB_REQUEST_RECIPIENT_INTERFACE (1U << USB_REQUEST_RECIPIENT_SHIFT) -#define USB_REQUEST_RECIPIENT_ENDPOINT (2U << USB_REQUEST_RECIPIENT_SHIFT) -#define USB_REQUEST_RECIPIENT_OTHER (3U << USB_REQUEST_RECIPIENT_SHIFT) -#define USB_REQUEST_RECIPIENT_MASK (3U << USB_REQUEST_RECIPIENT_SHIFT) - -/* USB Standard Request Codes */ -#define USB_REQUEST_GET_STATUS 0x00 -#define USB_REQUEST_CLEAR_FEATURE 0x01 -#define USB_REQUEST_SET_FEATURE 0x03 -#define USB_REQUEST_SET_ADDRESS 0x05 -#define USB_REQUEST_GET_DESCRIPTOR 0x06 -#define USB_REQUEST_SET_DESCRIPTOR 0x07 -#define USB_REQUEST_GET_CONFIGURATION 0x08 -#define USB_REQUEST_SET_CONFIGURATION 0x09 -#define USB_REQUEST_GET_INTERFACE 0x0A -#define USB_REQUEST_SET_INTERFACE 0x0B -#define USB_REQUEST_SYNCH_FRAME 0x0C -#define USB_REQUEST_SET_ENCRYPTION 0x0D -#define USB_REQUEST_GET_ENCRYPTION 0x0E -#define USB_REQUEST_RPIPE_ABORT 0x0E -#define USB_REQUEST_SET_HANDSHAKE 0x0F -#define USB_REQUEST_RPIPE_RESET 0x0F -#define USB_REQUEST_GET_HANDSHAKE 0x10 -#define USB_REQUEST_SET_CONNECTION 0x11 -#define USB_REQUEST_SET_SECURITY_DATA 0x12 -#define USB_REQUEST_GET_SECURITY_DATA 0x13 -#define USB_REQUEST_SET_WUSB_DATA 0x14 -#define USB_REQUEST_LOOPBACK_DATA_WRITE 0x15 -#define USB_REQUEST_LOOPBACK_DATA_READ 0x16 -#define USB_REQUEST_SET_INTERFACE_DS 0x17 - -/* USB Standard Feature selectors */ -#define USB_FEATURE_ENDPOINT_HALT 0 -#define USB_FEATURE_SELF_POWERED 0 -#define USB_FEATURE_REMOTE_WAKEUP 1 -#define USB_FEATURE_TEST_MODE 2 -#define USB_FEATURE_BATTERY 2 -#define USB_FEATURE_BHNPENABLE 3 -#define USB_FEATURE_WUSBDEVICE 3 -#define USB_FEATURE_AHNPSUPPORT 4 -#define USB_FEATURE_AALTHNPSUPPORT 5 -#define USB_FEATURE_DEBUGMODE 6 - -/* USB GET_STATUS Bit Values */ -#define USB_GETSTATUS_ENDPOINT_HALT 0x01 -#define USB_GETSTATUS_SELF_POWERED 0x01 -#define USB_GETSTATUS_REMOTE_WAKEUP 0x02 - -/* USB Descriptor Types */ -#define USB_DESCRIPTOR_TYPE_DEVICE 0x01U -#define USB_DESCRIPTOR_TYPE_CONFIGURATION 0x02U -#define USB_DESCRIPTOR_TYPE_STRING 0x03U -#define USB_DESCRIPTOR_TYPE_INTERFACE 0x04U -#define USB_DESCRIPTOR_TYPE_ENDPOINT 0x05U -#define USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER 0x06U -#define USB_DESCRIPTOR_TYPE_OTHER_SPEED 0x07U -#define USB_DESCRIPTOR_TYPE_INTERFACE_POWER 0x08U -#define USB_DESCRIPTOR_TYPE_OTG 0x09U -#define USB_DESCRIPTOR_TYPE_DEBUG 0x0AU -#define USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION 0x0BU -#define USB_DESCRIPTOR_TYPE_BINARY_OBJECT_STORE 0x0FU -#define USB_DESCRIPTOR_TYPE_DEVICE_CAPABILITY 0x10U -#define USB_DESCRIPTOR_TYPE_WIRELESS_ENDPOINTCOMP 0x11U - -/* Class Specific Descriptor */ -#define USB_CS_DESCRIPTOR_TYPE_DEVICE 0x21U -#define USB_CS_DESCRIPTOR_TYPE_CONFIGURATION 0x22U -#define USB_CS_DESCRIPTOR_TYPE_STRING 0x23U -#define USB_CS_DESCRIPTOR_TYPE_INTERFACE 0x24U -#define USB_CS_DESCRIPTOR_TYPE_ENDPOINT 0x25U - -#define USB_DESCRIPTOR_TYPE_SUPERSPEED_ENDPOINT_COMPANION 0x30U -#define USB_DESCRIPTOR_TYPE_SUPERSPEED_ISO_ENDPOINT_COMPANION 0x31U - -/* USB Device Classes */ -#define USB_DEVICE_CLASS_RESERVED 0x00 -#define USB_DEVICE_CLASS_AUDIO 0x01 -#define USB_DEVICE_CLASS_CDC 0x02 -#define USB_DEVICE_CLASS_HID 0x03 -#define USB_DEVICE_CLASS_MONITOR 0x04 -#define USB_DEVICE_CLASS_PHYSICAL 0x05 -#define USB_DEVICE_CLASS_IMAGE 0x06 -#define USB_DEVICE_CLASS_PRINTER 0x07 -#define USB_DEVICE_CLASS_MASS_STORAGE 0x08 -#define USB_DEVICE_CLASS_HUB 0x09 -#define USB_DEVICE_CLASS_CDC_DATA 0x0a -#define USB_DEVICE_CLASS_SMART_CARD 0x0b -#define USB_DEVICE_CLASS_SECURITY 0x0d -#define USB_DEVICE_CLASS_VIDEO 0x0e -#define USB_DEVICE_CLASS_HEALTHCARE 0x0f -#define USB_DEVICE_CLASS_DIAG_DEVICE 0xdc -#define USB_DEVICE_CLASS_WIRELESS 0xe0 -#define USB_DEVICE_CLASS_MISC 0xef -#define USB_DEVICE_CLASS_APP_SPECIFIC 0xfe -#define USB_DEVICE_CLASS_VEND_SPECIFIC 0xff - -/* usb string index define */ -#define USB_STRING_LANGID_INDEX 0x00 -#define USB_STRING_MFC_INDEX 0x01 -#define USB_STRING_PRODUCT_INDEX 0x02 -#define USB_STRING_SERIAL_INDEX 0x03 -#define USB_STRING_CONFIG_INDEX 0x04 -#define USB_STRING_INTERFACE_INDEX 0x05 -#define USB_STRING_OS_INDEX 0x06 -#define USB_STRING_MAX USB_STRING_OS_INDEX -/* - * Devices supporting Microsoft OS Descriptors store special string - * descriptor at fixed index (0xEE). It is read when a new device is - * attached to a computer for the first time. - */ -#define USB_OSDESC_STRING_DESC_INDEX 0xEE - -/* bmAttributes in Configuration Descriptor */ -#define USB_CONFIG_REMOTE_WAKEUP 0x20 -#define USB_CONFIG_POWERED_MASK 0x40 -#define USB_CONFIG_BUS_POWERED 0x80 -#define USB_CONFIG_SELF_POWERED 0xC0 - -/* bMaxPower in Configuration Descriptor */ -#define USB_CONFIG_POWER_MA(mA) ((mA) / 2) - -/* bEndpointAddress in Endpoint Descriptor */ -#define USB_ENDPOINT_DIRECTION_MASK 0x80 -#define USB_ENDPOINT_OUT(addr) ((addr) | 0x00) -#define USB_ENDPOINT_IN(addr) ((addr) | 0x80) - -/* bmAttributes in Endpoint Descriptor */ -#define USB_ENDPOINT_TYPE_SHIFT 0 -#define USB_ENDPOINT_TYPE_CONTROL (0 << USB_ENDPOINT_TYPE_SHIFT) -#define USB_ENDPOINT_TYPE_ISOCHRONOUS (1 << USB_ENDPOINT_TYPE_SHIFT) -#define USB_ENDPOINT_TYPE_BULK (2 << USB_ENDPOINT_TYPE_SHIFT) -#define USB_ENDPOINT_TYPE_INTERRUPT (3 << USB_ENDPOINT_TYPE_SHIFT) -#define USB_ENDPOINT_TYPE_MASK (3 << USB_ENDPOINT_TYPE_SHIFT) - -#define USB_ENDPOINT_SYNC_SHIFT 2 -#define USB_ENDPOINT_SYNC_NO_SYNCHRONIZATION (0 << USB_ENDPOINT_SYNC_SHIFT) -#define USB_ENDPOINT_SYNC_ASYNCHRONOUS (1 << USB_ENDPOINT_SYNC_SHIFT) -#define USB_ENDPOINT_SYNC_ADAPTIVE (2 << USB_ENDPOINT_SYNC_SHIFT) -#define USB_ENDPOINT_SYNC_SYNCHRONOUS (3 << USB_ENDPOINT_SYNC_SHIFT) -#define USB_ENDPOINT_SYNC_MASK (3 << USB_ENDPOINT_SYNC_SHIFT) - -#define USB_ENDPOINT_USAGE_SHIFT 4 -#define USB_ENDPOINT_USAGE_DATA (0 << USB_ENDPOINT_USAGE_SHIFT) -#define USB_ENDPOINT_USAGE_FEEDBACK (1 << USB_ENDPOINT_USAGE_SHIFT) -#define USB_ENDPOINT_USAGE_IMPLICIT_FEEDBACK (2 << USB_ENDPOINT_USAGE_SHIFT) -#define USB_ENDPOINT_USAGE_MASK (3 << USB_ENDPOINT_USAGE_SHIFT) - -#define USB_ENDPOINT_MAX_ADJUSTABLE (1 << 7) - -/* bDevCapabilityType in Device Capability Descriptor */ -#define USB_DEVICE_CAPABILITY_WIRELESS_USB 1 -#define USB_DEVICE_CAPABILITY_USB_2_0_EXTENSION 2 -#define USB_DEVICE_CAPABILITY_SUPERSPEED_USB 3 -#define USB_DEVICE_CAPABILITY_CONTAINER_ID 4 -#define USB_DEVICE_CAPABILITY_PLATFORM 5 -#define USB_DEVICE_CAPABILITY_POWER_DELIVERY_CAPABILITY 6 -#define USB_DEVICE_CAPABILITY_BATTERY_INFO_CAPABILITY 7 -#define USB_DEVICE_CAPABILITY_PD_CONSUMER_PORT_CAPABILITY 8 -#define USB_DEVICE_CAPABILITY_PD_PROVIDER_PORT_CAPABILITY 9 -#define USB_DEVICE_CAPABILITY_SUPERSPEED_PLUS 10 -#define USB_DEVICE_CAPABILITY_PRECISION_TIME_MEASUREMENT 11 -#define USB_DEVICE_CAPABILITY_WIRELESS_USB_EXT 12 - -#define USB_BOS_CAPABILITY_EXTENSION 0x02 -#define USB_BOS_CAPABILITY_PLATFORM 0x05 - -/* OTG SET FEATURE Constants */ -#define USB_OTG_FEATURE_B_HNP_ENABLE 3 /* Enable B device to perform HNP */ -#define USB_OTG_FEATURE_A_HNP_SUPPORT 4 /* A device supports HNP */ -#define USB_OTG_FEATURE_A_ALT_HNP_SUPPORT 5 /* Another port on the A device supports HNP */ - -/* WinUSB Microsoft OS 2.0 descriptor request codes */ -#define WINUSB_REQUEST_GET_DESCRIPTOR_SET 0x07 -#define WINUSB_REQUEST_SET_ALT_ENUM 0x08 - -/* WinUSB Microsoft OS 2.0 descriptor sizes */ -#define WINUSB_DESCRIPTOR_SET_HEADER_SIZE 10 -#define WINUSB_FUNCTION_SUBSET_HEADER_SIZE 8 -#define WINUSB_FEATURE_COMPATIBLE_ID_SIZE 20 - -/* WinUSB Microsoft OS 2.0 Descriptor Types */ -#define WINUSB_SET_HEADER_DESCRIPTOR_TYPE 0x00 -#define WINUSB_SUBSET_HEADER_CONFIGURATION_TYPE 0x01 -#define WINUSB_SUBSET_HEADER_FUNCTION_TYPE 0x02 -#define WINUSB_FEATURE_COMPATIBLE_ID_TYPE 0x03 -#define WINUSB_FEATURE_REG_PROPERTY_TYPE 0x04 -#define WINUSB_FEATURE_MIN_RESUME_TIME_TYPE 0x05 -#define WINUSB_FEATURE_MODEL_ID_TYPE 0x06 -#define WINUSB_FEATURE_CCGP_DEVICE_TYPE 0x07 - -#define WINUSB_PROP_DATA_TYPE_REG_SZ 0x01 -#define WINUSB_PROP_DATA_TYPE_REG_MULTI_SZ 0x07 - -/* WebUSB Descriptor Types */ -#define WEBUSB_DESCRIPTOR_SET_HEADER_TYPE 0x00 -#define WEBUSB_CONFIGURATION_SUBSET_HEADER_TYPE 0x01 -#define WEBUSB_FUNCTION_SUBSET_HEADER_TYPE 0x02 -#define WEBUSB_URL_TYPE 0x03 - -/* WebUSB Request Codes */ -#define WEBUSB_REQUEST_GET_URL 0x02 - -/* bScheme in URL descriptor */ -#define WEBUSB_URL_SCHEME_HTTP 0x00 -#define WEBUSB_URL_SCHEME_HTTPS 0x01 - -/* WebUSB Descriptor sizes */ -#define WEBUSB_DESCRIPTOR_SET_HEADER_SIZE 5 -#define WEBUSB_CONFIGURATION_SUBSET_HEADER_SIZE 4 -#define WEBUSB_FUNCTION_SUBSET_HEADER_SIZE 3 - -/* Setup packet definition used to read raw data from USB line */ -struct usb_setup_packet { - /** Request type. Bits 0:4 determine recipient, see - * \ref usb_request_recipient. Bits 5:6 determine type, see - * \ref usb_request_type. Bit 7 determines data transfer direction, see - * \ref usb_endpoint_direction. - */ - uint8_t bmRequestType; - - /** Request. If the type bits of bmRequestType are equal to - * \ref usb_request_type::LIBUSB_REQUEST_TYPE_STANDARD - * "USB_REQUEST_TYPE_STANDARD" then this field refers to - * \ref usb_standard_request. For other cases, use of this field is - * application-specific. */ - uint8_t bRequest; - - /** Value. Varies according to request */ - uint16_t wValue; - - /** Index. Varies according to request, typically used to pass an index - * or offset */ - uint16_t wIndex; - - /** Number of bytes to transfer */ - uint16_t wLength; -} __PACKED; - -#define USB_SIZEOF_SETUP_PACKET 8 - -/** Standard Device Descriptor */ -struct usb_device_descriptor { - uint8_t bLength; /* Descriptor size in bytes = 18 */ - uint8_t bDescriptorType; /* DEVICE descriptor type = 1 */ - uint16_t bcdUSB; /* USB spec in BCD, e.g. 0x0200 */ - uint8_t bDeviceClass; /* Class code, if 0 see interface */ - uint8_t bDeviceSubClass; /* Sub-Class code, 0 if class = 0 */ - uint8_t bDeviceProtocol; /* Protocol, if 0 see interface */ - uint8_t bMaxPacketSize0; /* Endpoint 0 max. size */ - uint16_t idVendor; /* Vendor ID per USB-IF */ - uint16_t idProduct; /* Product ID per manufacturer */ - uint16_t bcdDevice; /* Device release # in BCD */ - uint8_t iManufacturer; /* Index to manufacturer string */ - uint8_t iProduct; /* Index to product string */ - uint8_t iSerialNumber; /* Index to serial number string */ - uint8_t bNumConfigurations; /* Number of possible configurations */ -} __PACKED; - -#define USB_SIZEOF_DEVICE_DESC 18 - -/** Standard Configuration Descriptor */ -struct usb_configuration_descriptor { - uint8_t bLength; /* Descriptor size in bytes = 9 */ - uint8_t bDescriptorType; /* CONFIGURATION type = 2 or 7 */ - uint16_t wTotalLength; /* Length of concatenated descriptors */ - uint8_t bNumInterfaces; /* Number of interfaces, this config. */ - uint8_t bConfigurationValue; /* Value to set this config. */ - uint8_t iConfiguration; /* Index to configuration string */ - uint8_t bmAttributes; /* Config. characteristics */ - uint8_t bMaxPower; /* Max.power from bus, 2mA units */ -} __PACKED; - -#define USB_SIZEOF_CONFIG_DESC 9 - -/** Standard Interface Descriptor */ -struct usb_interface_descriptor { - uint8_t bLength; /* Descriptor size in bytes = 9 */ - uint8_t bDescriptorType; /* INTERFACE descriptor type = 4 */ - uint8_t bInterfaceNumber; /* Interface no.*/ - uint8_t bAlternateSetting; /* Value to select this IF */ - uint8_t bNumEndpoints; /* Number of endpoints excluding 0 */ - uint8_t bInterfaceClass; /* Class code, 0xFF = vendor */ - uint8_t bInterfaceSubClass; /* Sub-Class code, 0 if class = 0 */ - uint8_t bInterfaceProtocol; /* Protocol, 0xFF = vendor */ - uint8_t iInterface; /* Index to interface string */ -} __PACKED; - -#define USB_SIZEOF_INTERFACE_DESC 9 - -/** Standard Endpoint Descriptor */ -struct usb_endpoint_descriptor { - uint8_t bLength; /* Descriptor size in bytes = 7 */ - uint8_t bDescriptorType; /* ENDPOINT descriptor type = 5 */ - uint8_t bEndpointAddress; /* Endpoint # 0 - 15 | IN/OUT */ - uint8_t bmAttributes; /* Transfer type */ - uint16_t wMaxPacketSize; /* Bits 10:0 = max. packet size */ - uint8_t bInterval; /* Polling interval in (micro) frames */ -} __PACKED; - -#define USB_SIZEOF_ENDPOINT_DESC 7 - -/** Unicode (UTF16LE) String Descriptor */ -struct usb_string_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; - uint16_t bString; -} __PACKED; - -#define USB_SIZEOF_STRING_LANGID_DESC 4 - -/* USB Interface Association Descriptor */ -struct usb_interface_association_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bFirstInterface; - uint8_t bInterfaceCount; - uint8_t bFunctionClass; - uint8_t bFunctionSubClass; - uint8_t bFunctionProtocol; - uint8_t iFunction; -} __PACKED; - -#define USB_SIZEOF_IAD_DESC 8 - -/** USB device_qualifier descriptor */ -struct usb_device_qualifier_descriptor { - uint8_t bLength; /* Descriptor size in bytes = 10 */ - uint8_t bDescriptorType; /* DEVICE QUALIFIER type = 6 */ - uint16_t bcdUSB; /* USB spec in BCD, e.g. 0x0200 */ - uint8_t bDeviceClass; /* Class code, if 0 see interface */ - uint8_t bDeviceSubClass; /* Sub-Class code, 0 if class = 0 */ - uint8_t bDeviceProtocol; /* Protocol, if 0 see interface */ - uint8_t bMaxPacketSize; /* Endpoint 0 max. size */ - uint8_t bNumConfigurations; /* Number of possible configurations */ - uint8_t bReserved; /* Reserved = 0 */ -} __PACKED; - -#define USB_SIZEOF_DEVICE_QUALIFIER_DESC 10 - -/* Microsoft OS function descriptor. - * This can be used to request a specific driver (such as WINUSB) to be - * loaded on Windows. Unlike other descriptors, it is requested by a special - * request USB_REQ_GETMSFTOSDESCRIPTOR. - * More details: - * https://msdn.microsoft.com/en-us/windows/hardware/gg463179 - * And excellent explanation: - * https://github.com/pbatard/libwdi/wiki/WCID-Devices - * - * The device will have exactly one "Extended Compat ID Feature Descriptor", - * which may contain multiple "Function Descriptors" associated with - * different interfaces. - */ - -/* MS OS 1.0 string descriptor */ -struct usb_msosv1_string_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bString[14]; - uint8_t bMS_VendorCode; /* Vendor Code, used for a control request */ - uint8_t bPad; /* Padding byte for VendorCode look as UTF16 */ -} __PACKED; - -/* MS OS 1.0 Header descriptor */ -struct usb_msosv1_compat_id_header_descriptor { - uint32_t dwLength; - uint16_t bcdVersion; - uint16_t wIndex; - uint8_t bCount; - uint8_t reserved[7]; -} __PACKED; - -/* MS OS 1.0 Function descriptor */ -struct usb_msosv1_comp_id_function_descriptor { - uint8_t bFirstInterfaceNumber; - uint8_t reserved1; - uint8_t compatibleID[8]; - uint8_t subCompatibleID[8]; - uint8_t reserved2[6]; -} __PACKED; - -#define usb_msosv1_comp_id_create(x) \ - struct usb_msosv1_comp_id { \ - struct usb_msosv1_compat_id_header_descriptor compat_id_header; \ - struct usb_msosv1_comp_id_function_descriptor compat_id_function[x]; \ - }; - -struct usb_msosv1_descriptor { - uint8_t *string; - uint8_t string_len; - uint8_t vendor_code; - uint8_t *compat_id; - uint16_t compat_id_len; - uint8_t *comp_id_property; - uint16_t comp_id_property_len; -}; - -/* MS OS 2.0 Header descriptor */ -struct usb_msosv2_header_descriptor { - uint32_t dwLength; - uint16_t bcdVersion; - uint16_t wIndex; - uint8_t bCount; -} __PACKED; - -/*Microsoft OS 2.0 set header descriptor*/ -struct usb_msosv2_set_header_descriptor { - uint16_t wLength; - uint16_t wDescriptorType; - uint32_t dwWindowsVersion; - uint16_t wDescriptorSetTotalLength; -} __PACKED; - -/* Microsoft OS 2.0 compatibleID descriptor*/ -struct usb_msosv2_comp_id_descriptor { - uint16_t wLength; - uint16_t wDescriptorType; - uint8_t compatibleID[8]; - uint8_t subCompatibleID[8]; -} __PACKED; - -/* MS OS 2.0 property descriptor */ -struct usb_msosv2_property_descriptor { - uint16_t wLength; - uint16_t wDescriptorType; - uint32_t dwPropertyDataType; - uint16_t wPropertyNameLength; - const char *bPropertyName; - uint32_t dwPropertyDataLength; - const char *bPropertyData; -}; - -/* Microsoft OS 2.0 subset function descriptor */ -struct usb_msosv2_subset_function_descriptor { - uint16_t wLength; - uint16_t wDescriptorType; - uint8_t bFirstInterface; - uint8_t bReserved; - uint16_t wSubsetLength; -} __PACKED; - -struct usb_msosv2_descriptor { - uint8_t *compat_id; - uint16_t compat_id_len; - uint8_t vendor_code; -}; - -/* BOS header Descriptor */ -struct usb_bos_header_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; - uint16_t wTotalLength; - uint8_t bNumDeviceCaps; -} __PACKED; - -/* BOS Capability platform Descriptor */ -struct usb_bos_capability_platform_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bDevCapabilityType; - uint8_t bReserved; - uint8_t PlatformCapabilityUUID[16]; -} __PACKED; - -/* BOS Capability MS OS Descriptors version 2 */ -struct usb_bos_capability_msosv2_descriptor { - uint32_t dwWindowsVersion; - uint16_t wMSOSDescriptorSetTotalLength; - uint8_t bVendorCode; - uint8_t bAltEnumCode; -} __PACKED; - -/* BOS Capability webusb */ -struct usb_bos_capability_webusb_descriptor { - uint16_t bcdVersion; - uint8_t bVendorCode; - uint8_t iLandingPage; -} __PACKED; - -/* BOS Capability extension Descriptor*/ -struct usb_bos_capability_extension_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bDevCapabilityType; - uint32_t bmAttributes; -} __PACKED; - -/* Microsoft OS 2.0 Platform Capability Descriptor -* See https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/ -* microsoft-defined-usb-descriptors -* Adapted from the source: -* https://github.com/sowbug/weblight/blob/master/firmware/webusb.c -* (BSD-2) Thanks http://janaxelson.com/files/ms_os_20_descriptors.c -*/ -struct usb_bos_capability_platform_msosv2_descriptor { - struct usb_bos_capability_platform_descriptor platform_msos; - struct usb_bos_capability_msosv2_descriptor data_msosv2; -} __PACKED; - -/* WebUSB Platform Capability Descriptor: -* https://wicg.github.io/webusb/#webusb-platform-capability-descriptor -*/ -struct usb_bos_capability_platform_webusb_descriptor { - struct usb_bos_capability_platform_descriptor platform_webusb; - struct usb_bos_capability_webusb_descriptor data_webusb; -} __PACKED; - -struct usb_webusb_url_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bScheme; - char URL[]; -} __PACKED; - -struct usb_bos_descriptor { - uint8_t *string; - uint32_t string_len; -}; - -/* USB Device Capability Descriptor */ -struct usb_device_capability_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bDevCapabilityType; -} __PACKED; - -/** USB descriptor header */ -struct usb_desc_header { - uint8_t bLength; /**< descriptor length */ - uint8_t bDescriptorType; /**< descriptor type */ -}; -// clang-format off -#define USB_DEVICE_DESCRIPTOR_INIT(bcdUSB, bDeviceClass, bDeviceSubClass, bDeviceProtocol, idVendor, idProduct, bcdDevice, bNumConfigurations) \ - 0x12, /* bLength */ \ - USB_DESCRIPTOR_TYPE_DEVICE, /* bDescriptorType */ \ - WBVAL(bcdUSB), /* bcdUSB */ \ - bDeviceClass, /* bDeviceClass */ \ - bDeviceSubClass, /* bDeviceSubClass */ \ - bDeviceProtocol, /* bDeviceProtocol */ \ - 0x40, /* bMaxPacketSize */ \ - WBVAL(idVendor), /* idVendor */ \ - WBVAL(idProduct), /* idProduct */ \ - WBVAL(bcdDevice), /* bcdDevice */ \ - USB_STRING_MFC_INDEX, /* iManufacturer */ \ - USB_STRING_PRODUCT_INDEX, /* iProduct */ \ - USB_STRING_SERIAL_INDEX, /* iSerial */ \ - bNumConfigurations /* bNumConfigurations */ - -#define USB_CONFIG_DESCRIPTOR_INIT(wTotalLength, bNumInterfaces, bConfigurationValue, bmAttributes, bMaxPower) \ - 0x09, /* bLength */ \ - USB_DESCRIPTOR_TYPE_CONFIGURATION, /* bDescriptorType */ \ - WBVAL(wTotalLength), /* wTotalLength */ \ - bNumInterfaces, /* bNumInterfaces */ \ - bConfigurationValue, /* bConfigurationValue */ \ - 0x00, /* iConfiguration */ \ - bmAttributes, /* bmAttributes */ \ - USB_CONFIG_POWER_MA(bMaxPower) /* bMaxPower */ - -#define USB_INTERFACE_DESCRIPTOR_INIT(bInterfaceNumber, bAlternateSetting, bNumEndpoints, \ - bInterfaceClass, bInterfaceSubClass, bInterfaceProtocol, iInterface) \ - 0x09, /* bLength */ \ - USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \ - bInterfaceNumber, /* bInterfaceNumber */ \ - bAlternateSetting, /* bAlternateSetting */ \ - bNumEndpoints, /* bNumEndpoints */ \ - bInterfaceClass, /* bInterfaceClass */ \ - bInterfaceSubClass, /* bInterfaceSubClass */ \ - bInterfaceProtocol, /* bInterfaceProtocol */ \ - iInterface /* iInterface */ - -#define USB_ENDPOINT_DESCRIPTOR_INIT(bEndpointAddress, bmAttributes, wMaxPacketSize, bInterval) \ - 0x07, /* bLength */ \ - USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \ - bEndpointAddress, /* bEndpointAddress */ \ - bmAttributes, /* bmAttributes */ \ - WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \ - bInterval /* bInterval */ - -#define USB_IAD_INIT(bFirstInterface, bInterfaceCount, bFunctionClass, bFunctionSubClass, bFunctionProtocol) \ - 0x08, /* bLength */ \ - USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, /* bDescriptorType */ \ - bFirstInterface, /* bFirstInterface */ \ - bInterfaceCount, /* bInterfaceCount */ \ - bFunctionClass, /* bFunctionClass */ \ - bFunctionSubClass, /* bFunctionSubClass */ \ - bFunctionProtocol, /* bFunctionProtocol */ \ - 0x00 /* iFunction */ - -#define USB_LANGID_INIT(id) \ - 0x04, /* bLength */ \ - USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ \ - WBVAL(id) /* wLangID0 */ -// clang-format on - -#endif +/** + * @file usb_def.h + * @brief + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#ifndef USB_DEF_H +#define USB_DEF_H + +/* Useful define */ +#define USB_1_1 0x0110 +#define USB_2_0 0x0200 +/* Set USB version to 2.1 so that the host will request the BOS descriptor */ +#define USB_2_1 0x0210 + +/* Device speeds */ +#define USB_SPEED_UNKNOWN 0 /* Transfer rate not yet set */ +#define USB_SPEED_LOW 1 /* USB 1.1 */ +#define USB_SPEED_FULL 2 /* USB 1.1 */ +#define USB_SPEED_HIGH 3 /* USB 2.0 */ +#define USB_SPEED_VARIABLE 4 /* Wireless USB 2.5 */ + +/* Maximum number of devices per controller */ +#define USB_MAX_DEVICES (127) + +// USB PID Types +#define USB_PID_OUT (0x01) /* Tokens */ +#define USB_PID_IN (0x09) +#define USB_PID_SOF (0x05) +#define USB_PID_SETUP (0x0d) + +#define USB_PID_DATA0 (0x03) /* Data */ +#define USB_PID_DATA1 (0x0b) +#define USB_PID_DATA2 (0x07) +#define USB_PID_MDATA (0x0f) + +#define USB_PID_ACK (0x02) /* Handshake */ +#define USB_PID_NAK (0x0a) +#define USB_PID_STALL (0x0e) +#define USB_PID_NYET (0x06) + +#define USB_PID_PRE (0x0c) /* Special */ +#define USB_PID_ERR (0x0c) +#define USB_PID_SPLIT (0x08) +#define USB_PID_PING (0x04) +#define USB_PID_RESERVED (0x00) + +#define USB_REQUEST_DIR_SHIFT 7U /* Bits 7: Request dir */ +#define USB_REQUEST_DIR_OUT (0U << USB_REQUEST_DIR_SHIFT) /* Bit 7=0: Host-to-device */ +#define USB_REQUEST_DIR_IN (1U << USB_REQUEST_DIR_SHIFT) /* Bit 7=1: Device-to-host */ +#define USB_REQUEST_DIR_MASK (1U << USB_REQUEST_DIR_SHIFT) /* Bit 7=1: Direction bit */ + +#define USB_REQUEST_TYPE_SHIFT 5U /* Bits 5:6: Request type */ +#define USB_REQUEST_STANDARD (0U << USB_REQUEST_TYPE_SHIFT) +#define USB_REQUEST_CLASS (1U << USB_REQUEST_TYPE_SHIFT) +#define USB_REQUEST_VENDOR (2U << USB_REQUEST_TYPE_SHIFT) +#define USB_REQUEST_RESERVED (3U << USB_REQUEST_TYPE_SHIFT) +#define USB_REQUEST_TYPE_MASK (3U << USB_REQUEST_TYPE_SHIFT) + +#define USB_REQUEST_RECIPIENT_SHIFT 0U /* Bits 0:4: Recipient */ +#define USB_REQUEST_RECIPIENT_DEVICE (0U << USB_REQUEST_RECIPIENT_SHIFT) +#define USB_REQUEST_RECIPIENT_INTERFACE (1U << USB_REQUEST_RECIPIENT_SHIFT) +#define USB_REQUEST_RECIPIENT_ENDPOINT (2U << USB_REQUEST_RECIPIENT_SHIFT) +#define USB_REQUEST_RECIPIENT_OTHER (3U << USB_REQUEST_RECIPIENT_SHIFT) +#define USB_REQUEST_RECIPIENT_MASK (3U << USB_REQUEST_RECIPIENT_SHIFT) + +/* USB Standard Request Codes */ +#define USB_REQUEST_GET_STATUS 0x00 +#define USB_REQUEST_CLEAR_FEATURE 0x01 +#define USB_REQUEST_SET_FEATURE 0x03 +#define USB_REQUEST_SET_ADDRESS 0x05 +#define USB_REQUEST_GET_DESCRIPTOR 0x06 +#define USB_REQUEST_SET_DESCRIPTOR 0x07 +#define USB_REQUEST_GET_CONFIGURATION 0x08 +#define USB_REQUEST_SET_CONFIGURATION 0x09 +#define USB_REQUEST_GET_INTERFACE 0x0A +#define USB_REQUEST_SET_INTERFACE 0x0B +#define USB_REQUEST_SYNCH_FRAME 0x0C +#define USB_REQUEST_SET_ENCRYPTION 0x0D +#define USB_REQUEST_GET_ENCRYPTION 0x0E +#define USB_REQUEST_RPIPE_ABORT 0x0E +#define USB_REQUEST_SET_HANDSHAKE 0x0F +#define USB_REQUEST_RPIPE_RESET 0x0F +#define USB_REQUEST_GET_HANDSHAKE 0x10 +#define USB_REQUEST_SET_CONNECTION 0x11 +#define USB_REQUEST_SET_SECURITY_DATA 0x12 +#define USB_REQUEST_GET_SECURITY_DATA 0x13 +#define USB_REQUEST_SET_WUSB_DATA 0x14 +#define USB_REQUEST_LOOPBACK_DATA_WRITE 0x15 +#define USB_REQUEST_LOOPBACK_DATA_READ 0x16 +#define USB_REQUEST_SET_INTERFACE_DS 0x17 + +/* USB Standard Feature selectors */ +#define USB_FEATURE_ENDPOINT_HALT 0 +#define USB_FEATURE_SELF_POWERED 0 +#define USB_FEATURE_REMOTE_WAKEUP 1 +#define USB_FEATURE_TEST_MODE 2 +#define USB_FEATURE_BATTERY 2 +#define USB_FEATURE_BHNPENABLE 3 +#define USB_FEATURE_WUSBDEVICE 3 +#define USB_FEATURE_AHNPSUPPORT 4 +#define USB_FEATURE_AALTHNPSUPPORT 5 +#define USB_FEATURE_DEBUGMODE 6 + +/* USB GET_STATUS Bit Values */ +#define USB_GETSTATUS_ENDPOINT_HALT 0x01 +#define USB_GETSTATUS_SELF_POWERED 0x01 +#define USB_GETSTATUS_REMOTE_WAKEUP 0x02 + +/* USB Descriptor Types */ +#define USB_DESCRIPTOR_TYPE_DEVICE 0x01U +#define USB_DESCRIPTOR_TYPE_CONFIGURATION 0x02U +#define USB_DESCRIPTOR_TYPE_STRING 0x03U +#define USB_DESCRIPTOR_TYPE_INTERFACE 0x04U +#define USB_DESCRIPTOR_TYPE_ENDPOINT 0x05U +#define USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER 0x06U +#define USB_DESCRIPTOR_TYPE_OTHER_SPEED 0x07U +#define USB_DESCRIPTOR_TYPE_INTERFACE_POWER 0x08U +#define USB_DESCRIPTOR_TYPE_OTG 0x09U +#define USB_DESCRIPTOR_TYPE_DEBUG 0x0AU +#define USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION 0x0BU +#define USB_DESCRIPTOR_TYPE_BINARY_OBJECT_STORE 0x0FU +#define USB_DESCRIPTOR_TYPE_DEVICE_CAPABILITY 0x10U +#define USB_DESCRIPTOR_TYPE_WIRELESS_ENDPOINTCOMP 0x11U + +/* Class Specific Descriptor */ +#define USB_CS_DESCRIPTOR_TYPE_DEVICE 0x21U +#define USB_CS_DESCRIPTOR_TYPE_CONFIGURATION 0x22U +#define USB_CS_DESCRIPTOR_TYPE_STRING 0x23U +#define USB_CS_DESCRIPTOR_TYPE_INTERFACE 0x24U +#define USB_CS_DESCRIPTOR_TYPE_ENDPOINT 0x25U + +#define USB_DESCRIPTOR_TYPE_SUPERSPEED_ENDPOINT_COMPANION 0x30U +#define USB_DESCRIPTOR_TYPE_SUPERSPEED_ISO_ENDPOINT_COMPANION 0x31U + +/* USB Device Classes */ +#define USB_DEVICE_CLASS_RESERVED 0x00 +#define USB_DEVICE_CLASS_AUDIO 0x01 +#define USB_DEVICE_CLASS_CDC 0x02 +#define USB_DEVICE_CLASS_HID 0x03 +#define USB_DEVICE_CLASS_MONITOR 0x04 +#define USB_DEVICE_CLASS_PHYSICAL 0x05 +#define USB_DEVICE_CLASS_IMAGE 0x06 +#define USB_DEVICE_CLASS_PRINTER 0x07 +#define USB_DEVICE_CLASS_MASS_STORAGE 0x08 +#define USB_DEVICE_CLASS_HUB 0x09 +#define USB_DEVICE_CLASS_CDC_DATA 0x0a +#define USB_DEVICE_CLASS_SMART_CARD 0x0b +#define USB_DEVICE_CLASS_SECURITY 0x0d +#define USB_DEVICE_CLASS_VIDEO 0x0e +#define USB_DEVICE_CLASS_HEALTHCARE 0x0f +#define USB_DEVICE_CLASS_DIAG_DEVICE 0xdc +#define USB_DEVICE_CLASS_WIRELESS 0xe0 +#define USB_DEVICE_CLASS_MISC 0xef +#define USB_DEVICE_CLASS_APP_SPECIFIC 0xfe +#define USB_DEVICE_CLASS_VEND_SPECIFIC 0xff + +/* usb string index define */ +#define USB_STRING_LANGID_INDEX 0x00 +#define USB_STRING_MFC_INDEX 0x01 +#define USB_STRING_PRODUCT_INDEX 0x02 +#define USB_STRING_SERIAL_INDEX 0x03 +#define USB_STRING_CONFIG_INDEX 0x04 +#define USB_STRING_INTERFACE_INDEX 0x05 +#define USB_STRING_OS_INDEX 0x06 +#define USB_STRING_MAX USB_STRING_OS_INDEX +/* + * Devices supporting Microsoft OS Descriptors store special string + * descriptor at fixed index (0xEE). It is read when a new device is + * attached to a computer for the first time. + */ +#define USB_OSDESC_STRING_DESC_INDEX 0xEE + +/* bmAttributes in Configuration Descriptor */ +#define USB_CONFIG_REMOTE_WAKEUP 0x20 +#define USB_CONFIG_POWERED_MASK 0x40 +#define USB_CONFIG_BUS_POWERED 0x80 +#define USB_CONFIG_SELF_POWERED 0xC0 + +/* bMaxPower in Configuration Descriptor */ +#define USB_CONFIG_POWER_MA(mA) ((mA) / 2) + +/* bEndpointAddress in Endpoint Descriptor */ +#define USB_ENDPOINT_DIRECTION_MASK 0x80 +#define USB_ENDPOINT_OUT(addr) ((addr) | 0x00) +#define USB_ENDPOINT_IN(addr) ((addr) | 0x80) + +/* bmAttributes in Endpoint Descriptor */ +#define USB_ENDPOINT_TYPE_SHIFT 0 +#define USB_ENDPOINT_TYPE_CONTROL (0 << USB_ENDPOINT_TYPE_SHIFT) +#define USB_ENDPOINT_TYPE_ISOCHRONOUS (1 << USB_ENDPOINT_TYPE_SHIFT) +#define USB_ENDPOINT_TYPE_BULK (2 << USB_ENDPOINT_TYPE_SHIFT) +#define USB_ENDPOINT_TYPE_INTERRUPT (3 << USB_ENDPOINT_TYPE_SHIFT) +#define USB_ENDPOINT_TYPE_MASK (3 << USB_ENDPOINT_TYPE_SHIFT) + +#define USB_ENDPOINT_SYNC_SHIFT 2 +#define USB_ENDPOINT_SYNC_NO_SYNCHRONIZATION (0 << USB_ENDPOINT_SYNC_SHIFT) +#define USB_ENDPOINT_SYNC_ASYNCHRONOUS (1 << USB_ENDPOINT_SYNC_SHIFT) +#define USB_ENDPOINT_SYNC_ADAPTIVE (2 << USB_ENDPOINT_SYNC_SHIFT) +#define USB_ENDPOINT_SYNC_SYNCHRONOUS (3 << USB_ENDPOINT_SYNC_SHIFT) +#define USB_ENDPOINT_SYNC_MASK (3 << USB_ENDPOINT_SYNC_SHIFT) + +#define USB_ENDPOINT_USAGE_SHIFT 4 +#define USB_ENDPOINT_USAGE_DATA (0 << USB_ENDPOINT_USAGE_SHIFT) +#define USB_ENDPOINT_USAGE_FEEDBACK (1 << USB_ENDPOINT_USAGE_SHIFT) +#define USB_ENDPOINT_USAGE_IMPLICIT_FEEDBACK (2 << USB_ENDPOINT_USAGE_SHIFT) +#define USB_ENDPOINT_USAGE_MASK (3 << USB_ENDPOINT_USAGE_SHIFT) + +#define USB_ENDPOINT_MAX_ADJUSTABLE (1 << 7) + +/* bDevCapabilityType in Device Capability Descriptor */ +#define USB_DEVICE_CAPABILITY_WIRELESS_USB 1 +#define USB_DEVICE_CAPABILITY_USB_2_0_EXTENSION 2 +#define USB_DEVICE_CAPABILITY_SUPERSPEED_USB 3 +#define USB_DEVICE_CAPABILITY_CONTAINER_ID 4 +#define USB_DEVICE_CAPABILITY_PLATFORM 5 +#define USB_DEVICE_CAPABILITY_POWER_DELIVERY_CAPABILITY 6 +#define USB_DEVICE_CAPABILITY_BATTERY_INFO_CAPABILITY 7 +#define USB_DEVICE_CAPABILITY_PD_CONSUMER_PORT_CAPABILITY 8 +#define USB_DEVICE_CAPABILITY_PD_PROVIDER_PORT_CAPABILITY 9 +#define USB_DEVICE_CAPABILITY_SUPERSPEED_PLUS 10 +#define USB_DEVICE_CAPABILITY_PRECISION_TIME_MEASUREMENT 11 +#define USB_DEVICE_CAPABILITY_WIRELESS_USB_EXT 12 + +#define USB_BOS_CAPABILITY_EXTENSION 0x02 +#define USB_BOS_CAPABILITY_PLATFORM 0x05 + +/* OTG SET FEATURE Constants */ +#define USB_OTG_FEATURE_B_HNP_ENABLE 3 /* Enable B device to perform HNP */ +#define USB_OTG_FEATURE_A_HNP_SUPPORT 4 /* A device supports HNP */ +#define USB_OTG_FEATURE_A_ALT_HNP_SUPPORT 5 /* Another port on the A device supports HNP */ + +/* WinUSB Microsoft OS 2.0 descriptor request codes */ +#define WINUSB_REQUEST_GET_DESCRIPTOR_SET 0x07 +#define WINUSB_REQUEST_SET_ALT_ENUM 0x08 + +/* WinUSB Microsoft OS 2.0 descriptor sizes */ +#define WINUSB_DESCRIPTOR_SET_HEADER_SIZE 10 +#define WINUSB_FUNCTION_SUBSET_HEADER_SIZE 8 +#define WINUSB_FEATURE_COMPATIBLE_ID_SIZE 20 + +/* WinUSB Microsoft OS 2.0 Descriptor Types */ +#define WINUSB_SET_HEADER_DESCRIPTOR_TYPE 0x00 +#define WINUSB_SUBSET_HEADER_CONFIGURATION_TYPE 0x01 +#define WINUSB_SUBSET_HEADER_FUNCTION_TYPE 0x02 +#define WINUSB_FEATURE_COMPATIBLE_ID_TYPE 0x03 +#define WINUSB_FEATURE_REG_PROPERTY_TYPE 0x04 +#define WINUSB_FEATURE_MIN_RESUME_TIME_TYPE 0x05 +#define WINUSB_FEATURE_MODEL_ID_TYPE 0x06 +#define WINUSB_FEATURE_CCGP_DEVICE_TYPE 0x07 + +#define WINUSB_PROP_DATA_TYPE_REG_SZ 0x01 +#define WINUSB_PROP_DATA_TYPE_REG_MULTI_SZ 0x07 + +/* WebUSB Descriptor Types */ +#define WEBUSB_DESCRIPTOR_SET_HEADER_TYPE 0x00 +#define WEBUSB_CONFIGURATION_SUBSET_HEADER_TYPE 0x01 +#define WEBUSB_FUNCTION_SUBSET_HEADER_TYPE 0x02 +#define WEBUSB_URL_TYPE 0x03 + +/* WebUSB Request Codes */ +#define WEBUSB_REQUEST_GET_URL 0x02 + +/* bScheme in URL descriptor */ +#define WEBUSB_URL_SCHEME_HTTP 0x00 +#define WEBUSB_URL_SCHEME_HTTPS 0x01 + +/* WebUSB Descriptor sizes */ +#define WEBUSB_DESCRIPTOR_SET_HEADER_SIZE 5 +#define WEBUSB_CONFIGURATION_SUBSET_HEADER_SIZE 4 +#define WEBUSB_FUNCTION_SUBSET_HEADER_SIZE 3 + +/* Setup packet definition used to read raw data from USB line */ +struct usb_setup_packet { + /** Request type. Bits 0:4 determine recipient, see + * \ref usb_request_recipient. Bits 5:6 determine type, see + * \ref usb_request_type. Bit 7 determines data transfer direction, see + * \ref usb_endpoint_direction. + */ + uint8_t bmRequestType; + + /** Request. If the type bits of bmRequestType are equal to + * \ref usb_request_type::LIBUSB_REQUEST_TYPE_STANDARD + * "USB_REQUEST_TYPE_STANDARD" then this field refers to + * \ref usb_standard_request. For other cases, use of this field is + * application-specific. */ + uint8_t bRequest; + + /** Value. Varies according to request */ + uint16_t wValue; + + /** Index. Varies according to request, typically used to pass an index + * or offset */ + uint16_t wIndex; + + /** Number of bytes to transfer */ + uint16_t wLength; +} __PACKED; + +#define USB_SIZEOF_SETUP_PACKET 8 + +/** Standard Device Descriptor */ +struct usb_device_descriptor { + uint8_t bLength; /* Descriptor size in bytes = 18 */ + uint8_t bDescriptorType; /* DEVICE descriptor type = 1 */ + uint16_t bcdUSB; /* USB spec in BCD, e.g. 0x0200 */ + uint8_t bDeviceClass; /* Class code, if 0 see interface */ + uint8_t bDeviceSubClass; /* Sub-Class code, 0 if class = 0 */ + uint8_t bDeviceProtocol; /* Protocol, if 0 see interface */ + uint8_t bMaxPacketSize0; /* Endpoint 0 max. size */ + uint16_t idVendor; /* Vendor ID per USB-IF */ + uint16_t idProduct; /* Product ID per manufacturer */ + uint16_t bcdDevice; /* Device release # in BCD */ + uint8_t iManufacturer; /* Index to manufacturer string */ + uint8_t iProduct; /* Index to product string */ + uint8_t iSerialNumber; /* Index to serial number string */ + uint8_t bNumConfigurations; /* Number of possible configurations */ +} __PACKED; + +#define USB_SIZEOF_DEVICE_DESC 18 + +/** Standard Configuration Descriptor */ +struct usb_configuration_descriptor { + uint8_t bLength; /* Descriptor size in bytes = 9 */ + uint8_t bDescriptorType; /* CONFIGURATION type = 2 or 7 */ + uint16_t wTotalLength; /* Length of concatenated descriptors */ + uint8_t bNumInterfaces; /* Number of interfaces, this config. */ + uint8_t bConfigurationValue; /* Value to set this config. */ + uint8_t iConfiguration; /* Index to configuration string */ + uint8_t bmAttributes; /* Config. characteristics */ + uint8_t bMaxPower; /* Max.power from bus, 2mA units */ +} __PACKED; + +#define USB_SIZEOF_CONFIG_DESC 9 + +/** Standard Interface Descriptor */ +struct usb_interface_descriptor { + uint8_t bLength; /* Descriptor size in bytes = 9 */ + uint8_t bDescriptorType; /* INTERFACE descriptor type = 4 */ + uint8_t bInterfaceNumber; /* Interface no.*/ + uint8_t bAlternateSetting; /* Value to select this IF */ + uint8_t bNumEndpoints; /* Number of endpoints excluding 0 */ + uint8_t bInterfaceClass; /* Class code, 0xFF = vendor */ + uint8_t bInterfaceSubClass; /* Sub-Class code, 0 if class = 0 */ + uint8_t bInterfaceProtocol; /* Protocol, 0xFF = vendor */ + uint8_t iInterface; /* Index to interface string */ +} __PACKED; + +#define USB_SIZEOF_INTERFACE_DESC 9 + +/** Standard Endpoint Descriptor */ +struct usb_endpoint_descriptor { + uint8_t bLength; /* Descriptor size in bytes = 7 */ + uint8_t bDescriptorType; /* ENDPOINT descriptor type = 5 */ + uint8_t bEndpointAddress; /* Endpoint # 0 - 15 | IN/OUT */ + uint8_t bmAttributes; /* Transfer type */ + uint16_t wMaxPacketSize; /* Bits 10:0 = max. packet size */ + uint8_t bInterval; /* Polling interval in (micro) frames */ +} __PACKED; + +#define USB_SIZEOF_ENDPOINT_DESC 7 + +/** Unicode (UTF16LE) String Descriptor */ +struct usb_string_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bString; +} __PACKED; + +#define USB_SIZEOF_STRING_LANGID_DESC 4 + +/* USB Interface Association Descriptor */ +struct usb_interface_association_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bFirstInterface; + uint8_t bInterfaceCount; + uint8_t bFunctionClass; + uint8_t bFunctionSubClass; + uint8_t bFunctionProtocol; + uint8_t iFunction; +} __PACKED; + +#define USB_SIZEOF_IAD_DESC 8 + +/** USB device_qualifier descriptor */ +struct usb_device_qualifier_descriptor { + uint8_t bLength; /* Descriptor size in bytes = 10 */ + uint8_t bDescriptorType; /* DEVICE QUALIFIER type = 6 */ + uint16_t bcdUSB; /* USB spec in BCD, e.g. 0x0200 */ + uint8_t bDeviceClass; /* Class code, if 0 see interface */ + uint8_t bDeviceSubClass; /* Sub-Class code, 0 if class = 0 */ + uint8_t bDeviceProtocol; /* Protocol, if 0 see interface */ + uint8_t bMaxPacketSize; /* Endpoint 0 max. size */ + uint8_t bNumConfigurations; /* Number of possible configurations */ + uint8_t bReserved; /* Reserved = 0 */ +} __PACKED; + +#define USB_SIZEOF_DEVICE_QUALIFIER_DESC 10 + +/* Microsoft OS function descriptor. + * This can be used to request a specific driver (such as WINUSB) to be + * loaded on Windows. Unlike other descriptors, it is requested by a special + * request USB_REQ_GETMSFTOSDESCRIPTOR. + * More details: + * https://msdn.microsoft.com/en-us/windows/hardware/gg463179 + * And excellent explanation: + * https://github.com/pbatard/libwdi/wiki/WCID-Devices + * + * The device will have exactly one "Extended Compat ID Feature Descriptor", + * which may contain multiple "Function Descriptors" associated with + * different interfaces. + */ + +/* MS OS 1.0 string descriptor */ +struct usb_msosv1_string_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bString[14]; + uint8_t bMS_VendorCode; /* Vendor Code, used for a control request */ + uint8_t bPad; /* Padding byte for VendorCode look as UTF16 */ +} __PACKED; + +/* MS OS 1.0 Header descriptor */ +struct usb_msosv1_compat_id_header_descriptor { + uint32_t dwLength; + uint16_t bcdVersion; + uint16_t wIndex; + uint8_t bCount; + uint8_t reserved[7]; +} __PACKED; + +/* MS OS 1.0 Function descriptor */ +struct usb_msosv1_comp_id_function_descriptor { + uint8_t bFirstInterfaceNumber; + uint8_t reserved1; + uint8_t compatibleID[8]; + uint8_t subCompatibleID[8]; + uint8_t reserved2[6]; +} __PACKED; + +#define usb_msosv1_comp_id_create(x) \ + struct usb_msosv1_comp_id { \ + struct usb_msosv1_compat_id_header_descriptor compat_id_header; \ + struct usb_msosv1_comp_id_function_descriptor compat_id_function[x]; \ + }; + +struct usb_msosv1_descriptor { + uint8_t *string; + uint8_t string_len; + uint8_t vendor_code; + uint8_t *compat_id; + uint16_t compat_id_len; + uint8_t *comp_id_property; + uint16_t comp_id_property_len; +}; + +/* MS OS 2.0 Header descriptor */ +struct usb_msosv2_header_descriptor { + uint32_t dwLength; + uint16_t bcdVersion; + uint16_t wIndex; + uint8_t bCount; +} __PACKED; + +/*Microsoft OS 2.0 set header descriptor*/ +struct usb_msosv2_set_header_descriptor { + uint16_t wLength; + uint16_t wDescriptorType; + uint32_t dwWindowsVersion; + uint16_t wDescriptorSetTotalLength; +} __PACKED; + +/* Microsoft OS 2.0 compatibleID descriptor*/ +struct usb_msosv2_comp_id_descriptor { + uint16_t wLength; + uint16_t wDescriptorType; + uint8_t compatibleID[8]; + uint8_t subCompatibleID[8]; +} __PACKED; + +/* MS OS 2.0 property descriptor */ +struct usb_msosv2_property_descriptor { + uint16_t wLength; + uint16_t wDescriptorType; + uint32_t dwPropertyDataType; + uint16_t wPropertyNameLength; + const char *bPropertyName; + uint32_t dwPropertyDataLength; + const char *bPropertyData; +}; + +/* Microsoft OS 2.0 subset function descriptor */ +struct usb_msosv2_subset_function_descriptor { + uint16_t wLength; + uint16_t wDescriptorType; + uint8_t bFirstInterface; + uint8_t bReserved; + uint16_t wSubsetLength; +} __PACKED; + +struct usb_msosv2_descriptor { + uint8_t *compat_id; + uint16_t compat_id_len; + uint8_t vendor_code; +}; + +/* BOS header Descriptor */ +struct usb_bos_header_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumDeviceCaps; +} __PACKED; + +/* BOS Capability platform Descriptor */ +struct usb_bos_capability_platform_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDevCapabilityType; + uint8_t bReserved; + uint8_t PlatformCapabilityUUID[16]; +} __PACKED; + +/* BOS Capability MS OS Descriptors version 2 */ +struct usb_bos_capability_msosv2_descriptor { + uint32_t dwWindowsVersion; + uint16_t wMSOSDescriptorSetTotalLength; + uint8_t bVendorCode; + uint8_t bAltEnumCode; +} __PACKED; + +/* BOS Capability webusb */ +struct usb_bos_capability_webusb_descriptor { + uint16_t bcdVersion; + uint8_t bVendorCode; + uint8_t iLandingPage; +} __PACKED; + +/* BOS Capability extension Descriptor*/ +struct usb_bos_capability_extension_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDevCapabilityType; + uint32_t bmAttributes; +} __PACKED; + +/* Microsoft OS 2.0 Platform Capability Descriptor +* See https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/ +* microsoft-defined-usb-descriptors +* Adapted from the source: +* https://github.com/sowbug/weblight/blob/master/firmware/webusb.c +* (BSD-2) Thanks http://janaxelson.com/files/ms_os_20_descriptors.c +*/ +struct usb_bos_capability_platform_msosv2_descriptor { + struct usb_bos_capability_platform_descriptor platform_msos; + struct usb_bos_capability_msosv2_descriptor data_msosv2; +} __PACKED; + +/* WebUSB Platform Capability Descriptor: +* https://wicg.github.io/webusb/#webusb-platform-capability-descriptor +*/ +struct usb_bos_capability_platform_webusb_descriptor { + struct usb_bos_capability_platform_descriptor platform_webusb; + struct usb_bos_capability_webusb_descriptor data_webusb; +} __PACKED; + +struct usb_webusb_url_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bScheme; + char URL[]; +} __PACKED; + +struct usb_bos_descriptor { + uint8_t *string; + uint32_t string_len; +}; + +/* USB Device Capability Descriptor */ +struct usb_device_capability_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDevCapabilityType; +} __PACKED; + +/** USB descriptor header */ +struct usb_desc_header { + uint8_t bLength; /**< descriptor length */ + uint8_t bDescriptorType; /**< descriptor type */ +}; +// clang-format off +#define USB_DEVICE_DESCRIPTOR_INIT(bcdUSB, bDeviceClass, bDeviceSubClass, bDeviceProtocol, idVendor, idProduct, bcdDevice, bNumConfigurations) \ + 0x12, /* bLength */ \ + USB_DESCRIPTOR_TYPE_DEVICE, /* bDescriptorType */ \ + WBVAL(bcdUSB), /* bcdUSB */ \ + bDeviceClass, /* bDeviceClass */ \ + bDeviceSubClass, /* bDeviceSubClass */ \ + bDeviceProtocol, /* bDeviceProtocol */ \ + 0x40, /* bMaxPacketSize */ \ + WBVAL(idVendor), /* idVendor */ \ + WBVAL(idProduct), /* idProduct */ \ + WBVAL(bcdDevice), /* bcdDevice */ \ + USB_STRING_MFC_INDEX, /* iManufacturer */ \ + USB_STRING_PRODUCT_INDEX, /* iProduct */ \ + USB_STRING_SERIAL_INDEX, /* iSerial */ \ + bNumConfigurations /* bNumConfigurations */ + +#define USB_CONFIG_DESCRIPTOR_INIT(wTotalLength, bNumInterfaces, bConfigurationValue, bmAttributes, bMaxPower) \ + 0x09, /* bLength */ \ + USB_DESCRIPTOR_TYPE_CONFIGURATION, /* bDescriptorType */ \ + WBVAL(wTotalLength), /* wTotalLength */ \ + bNumInterfaces, /* bNumInterfaces */ \ + bConfigurationValue, /* bConfigurationValue */ \ + 0x00, /* iConfiguration */ \ + bmAttributes, /* bmAttributes */ \ + USB_CONFIG_POWER_MA(bMaxPower) /* bMaxPower */ + +#define USB_INTERFACE_DESCRIPTOR_INIT(bInterfaceNumber, bAlternateSetting, bNumEndpoints, \ + bInterfaceClass, bInterfaceSubClass, bInterfaceProtocol, iInterface) \ + 0x09, /* bLength */ \ + USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \ + bInterfaceNumber, /* bInterfaceNumber */ \ + bAlternateSetting, /* bAlternateSetting */ \ + bNumEndpoints, /* bNumEndpoints */ \ + bInterfaceClass, /* bInterfaceClass */ \ + bInterfaceSubClass, /* bInterfaceSubClass */ \ + bInterfaceProtocol, /* bInterfaceProtocol */ \ + iInterface /* iInterface */ + +#define USB_ENDPOINT_DESCRIPTOR_INIT(bEndpointAddress, bmAttributes, wMaxPacketSize, bInterval) \ + 0x07, /* bLength */ \ + USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \ + bEndpointAddress, /* bEndpointAddress */ \ + bmAttributes, /* bmAttributes */ \ + WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \ + bInterval /* bInterval */ + +#define USB_IAD_INIT(bFirstInterface, bInterfaceCount, bFunctionClass, bFunctionSubClass, bFunctionProtocol) \ + 0x08, /* bLength */ \ + USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, /* bDescriptorType */ \ + bFirstInterface, /* bFirstInterface */ \ + bInterfaceCount, /* bInterfaceCount */ \ + bFunctionClass, /* bFunctionClass */ \ + bFunctionSubClass, /* bFunctionSubClass */ \ + bFunctionProtocol, /* bFunctionProtocol */ \ + 0x00 /* iFunction */ + +#define USB_LANGID_INIT(id) \ + 0x04, /* bLength */ \ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ \ + WBVAL(id) /* wLangID0 */ +// clang-format on + +#endif diff --git a/common/usb_util.h b/common/usb_util.h index ab4eb693..d21a5f61 100644 --- a/common/usb_util.h +++ b/common/usb_util.h @@ -1,300 +1,300 @@ -/** - * @file usb_util.h - * @brief - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#ifndef _USB_UTIL_H -#define _USB_UTIL_H - -#include -#include -#include -#include -#include -#include "usb_errno.h" -#include "usb_list.h" -#include "usb_mem.h" - -#if defined(__CC_ARM) -#ifndef __USED -#define __USED __attribute__((used)) -#endif -#ifndef __WEAK -#define __WEAK __attribute__((weak)) -#endif -#ifndef __PACKED -#define __PACKED __attribute__((packed)) -#endif -#ifndef __PACKED_STRUCT -#define __PACKED_STRUCT __packed struct -#endif -#ifndef __PACKED_UNION -#define __PACKED_UNION __packed union -#endif -#ifndef __ALIGNED -#define __ALIGNED(x) __attribute__((aligned(x))) -#endif -#elif defined(__GNUC__) -#ifndef __USED -#define __USED __attribute__((used)) -#endif -#ifndef __WEAK -#define __WEAK __attribute__((weak)) -#endif -#ifndef __PACKED -#define __PACKED __attribute__((packed, aligned(1))) -#endif -#ifndef __PACKED_STRUCT -#define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) -#endif -#ifndef __PACKED_UNION -#define __PACKED_UNION union __attribute__((packed, aligned(1))) -#endif -#ifndef __ALIGNED -#define __ALIGNED(x) __attribute__((aligned(x))) -#endif -#elif defined(__ICCARM__) -#ifndef __USED -#if __ICCARM_V8 -#define __USED __attribute__((used)) -#else -#define __USED _Pragma("__root") -#endif -#endif - -#ifndef __WEAK -#if __ICCARM_V8 -#define __WEAK __attribute__((weak)) -#else -#define __WEAK _Pragma("__weak") -#endif -#endif - -#ifndef __PACKED -#if __ICCARM_V8 -#define __PACKED __attribute__((packed, aligned(1))) -#else -/* Needs IAR language extensions */ -#define __PACKED __packed -#endif -#endif - -#ifndef __PACKED_STRUCT -#if __ICCARM_V8 -#define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) -#else -/* Needs IAR language extensions */ -#define __PACKED_STRUCT __packed struct -#endif -#endif - -#ifndef __PACKED_UNION -#if __ICCARM_V8 -#define __PACKED_UNION union __attribute__((packed, aligned(1))) -#else -/* Needs IAR language extensions */ -#define __PACKED_UNION __packed union -#endif -#endif - -#ifndef __ALIGNED -#if __ICCARM_V8 -#define __ALIGNED(x) __attribute__((aligned(x))) -#elif (__VER__ >= 7080000) -/* Needs IAR language extensions */ -#define __ALIGNED(x) __attribute__((aligned(x))) -#else -#warning No compiler specific solution for __ALIGNED.__ALIGNED is ignored. -#define __ALIGNED(x) -#endif -#endif - -#endif - -#ifndef __ALIGN_BEGIN -#define __ALIGN_BEGIN -#endif -#ifndef __ALIGN_END -#define __ALIGN_END __attribute__((aligned(4))) -#endif - -#ifndef ARG_UNUSED -#define ARG_UNUSED(x) (void)(x) -#endif - -#ifndef LO_BYTE -#define LO_BYTE(x) ((uint8_t)(x & 0x00FF)) -#endif - -#ifndef HI_BYTE -#define HI_BYTE(x) ((uint8_t)((x & 0xFF00) >> 8)) -#endif - -/** - * @def MAX - * @brief The larger value between @p a and @p b. - * @note Arguments are evaluated twice. - */ -#ifndef MAX -/* Use Z_MAX for a GCC-only, single evaluation version */ -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif - -/** - * @def MIN - * @brief The smaller value between @p a and @p b. - * @note Arguments are evaluated twice. - */ -#ifndef MIN -/* Use Z_MIN for a GCC-only, single evaluation version */ -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif - -#ifndef BCD -#define BCD(x) ((((x) / 10) << 4) | ((x) % 10)) -#endif - -#ifdef BIT -#undef BIT -#define BIT(n) (1UL << (n)) -#else -#define BIT(n) (1UL << (n)) -#endif - -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(array) \ - ((int)((sizeof(array) / sizeof((array)[0])))) -#endif - -#ifndef BSWAP16 -#define BSWAP16(u16) (__builtin_bswap16(u16)) -#endif -#ifndef BSWAP32 -#define BSWAP32(u32) (__builtin_bswap32(u32)) -#endif - -#define GET_BE16(field) \ - (((uint16_t)(field)[0] << 8) | ((uint16_t)(field)[1])) - -#define GET_BE32(field) \ - (((uint32_t)(field)[0] << 24) | ((uint32_t)(field)[1] << 16) | ((uint32_t)(field)[2] << 8) | ((uint32_t)(field)[3] << 0)) - -#define SET_BE16(field, value) \ - do { \ - (field)[0] = (uint8_t)((value) >> 8); \ - (field)[1] = (uint8_t)((value) >> 0); \ - } while (0) - -#define SET_BE24(field, value) \ - do { \ - (field)[0] = (uint8_t)((value) >> 16); \ - (field)[1] = (uint8_t)((value) >> 8); \ - (field)[2] = (uint8_t)((value) >> 0); \ - } while (0) - -#define SET_BE32(field, value) \ - do { \ - (field)[0] = (uint8_t)((value) >> 24); \ - (field)[1] = (uint8_t)((value) >> 16); \ - (field)[2] = (uint8_t)((value) >> 8); \ - (field)[3] = (uint8_t)((value) >> 0); \ - } while (0) - -#define REQTYPE_GET_DIR(x) (((x) >> 7) & 0x01) -#define REQTYPE_GET_TYPE(x) (((x) >> 5) & 0x03U) -#define REQTYPE_GET_RECIP(x) ((x)&0x1F) - -#define GET_DESC_TYPE(x) (((x) >> 8) & 0xFFU) -#define GET_DESC_INDEX(x) ((x)&0xFFU) - -#define WBVAL(x) (x & 0xFF), ((x >> 8) & 0xFF) -#define DBVAL(x) (x & 0xFF), ((x >> 8) & 0xFF), ((x >> 16) & 0xFF), ((x >> 24) & 0xFF) - -#define USB_DESC_SECTION __attribute__((section("usb_desc"))) __USED __ALIGNED(1) - -/* DEBUG level */ -#define USB_DBG_ERROR 0 -#define USB_DBG_WARNING 1 -#define USB_DBG_INFO 2 -#define USB_DBG_LOG 3 - -#ifndef USB_DBG_LEVEL -#define USB_DBG_LEVEL USB_DBG_INFO -#endif - -#ifndef USB_DBG_TAG -#define USB_DBG_TAG "USB" -#endif -/* - * The color for terminal (foreground) - * BLACK 30 - * RED 31 - * GREEN 32 - * YELLOW 33 - * BLUE 34 - * PURPLE 35 - * CYAN 36 - * WHITE 37 - */ -#define _USB_DBG_COLOR(n) printf("\033[" #n "m") -#define _USB_DBG_LOG_HDR(lvl_name, color_n) \ - printf("\033[" #color_n "m[" lvl_name "/" USB_DBG_TAG "] ") -#define _USB_DBG_LOG_X_END \ - printf("\033[0m") - -#define usb_dbg_log_line(lvl, color_n, fmt, ...) \ - do { \ - _USB_DBG_LOG_HDR(lvl, color_n); \ - printf(fmt, ##__VA_ARGS__); \ - _USB_DBG_LOG_X_END; \ - } while (0) - -#if (USB_DBG_LEVEL >= USB_DBG_LOG) -#define USB_LOG_DBG(fmt, ...) usb_dbg_log_line("D", 0, fmt, ##__VA_ARGS__) -#else -#define USB_LOG_DBG(...) -#endif - -#if (USB_DBG_LEVEL >= USB_DBG_INFO) -#define USB_LOG_INFO(fmt, ...) usb_dbg_log_line("I", 32, fmt, ##__VA_ARGS__) -#else -#define USB_LOG_INFO(...) -#endif - -#if (USB_DBG_LEVEL >= USB_DBG_WARNING) -#define USB_LOG_WRN(fmt, ...) usb_dbg_log_line("W", 33, fmt, ##__VA_ARGS__) -#else -#define USB_LOG_WRN(...) -#endif - -#if (USB_DBG_LEVEL >= USB_DBG_ERROR) -#define USB_LOG_ERR(fmt, ...) usb_dbg_log_line("E", 31, fmt, ##__VA_ARGS__) -#else -#define USB_LOG_ERR(...) -#endif - -void usb_assert(const char *filename, int linenum); -#define USB_ASSERT(f) \ - do { \ - if (!(f)) \ - usb_assert(__FILE__, __LINE__); \ - } while (0) - -#endif +/** + * @file usb_util.h + * @brief + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _USB_UTIL_H +#define _USB_UTIL_H + +#include +#include +#include +#include +#include +#include "usb_errno.h" +#include "usb_list.h" +#include "usb_mem.h" + +#if defined(__CC_ARM) +#ifndef __USED +#define __USED __attribute__((used)) +#endif +#ifndef __WEAK +#define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED +#define __PACKED __attribute__((packed)) +#endif +#ifndef __PACKED_STRUCT +#define __PACKED_STRUCT __packed struct +#endif +#ifndef __PACKED_UNION +#define __PACKED_UNION __packed union +#endif +#ifndef __ALIGNED +#define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#elif defined(__GNUC__) +#ifndef __USED +#define __USED __attribute__((used)) +#endif +#ifndef __WEAK +#define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED +#define __PACKED __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_STRUCT +#define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_UNION +#define __PACKED_UNION union __attribute__((packed, aligned(1))) +#endif +#ifndef __ALIGNED +#define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#elif defined(__ICCARM__) +#ifndef __USED +#if __ICCARM_V8 +#define __USED __attribute__((used)) +#else +#define __USED _Pragma("__root") +#endif +#endif + +#ifndef __WEAK +#if __ICCARM_V8 +#define __WEAK __attribute__((weak)) +#else +#define __WEAK _Pragma("__weak") +#endif +#endif + +#ifndef __PACKED +#if __ICCARM_V8 +#define __PACKED __attribute__((packed, aligned(1))) +#else +/* Needs IAR language extensions */ +#define __PACKED __packed +#endif +#endif + +#ifndef __PACKED_STRUCT +#if __ICCARM_V8 +#define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) +#else +/* Needs IAR language extensions */ +#define __PACKED_STRUCT __packed struct +#endif +#endif + +#ifndef __PACKED_UNION +#if __ICCARM_V8 +#define __PACKED_UNION union __attribute__((packed, aligned(1))) +#else +/* Needs IAR language extensions */ +#define __PACKED_UNION __packed union +#endif +#endif + +#ifndef __ALIGNED +#if __ICCARM_V8 +#define __ALIGNED(x) __attribute__((aligned(x))) +#elif (__VER__ >= 7080000) +/* Needs IAR language extensions */ +#define __ALIGNED(x) __attribute__((aligned(x))) +#else +#warning No compiler specific solution for __ALIGNED.__ALIGNED is ignored. +#define __ALIGNED(x) +#endif +#endif + +#endif + +#ifndef __ALIGN_BEGIN +#define __ALIGN_BEGIN +#endif +#ifndef __ALIGN_END +#define __ALIGN_END __attribute__((aligned(4))) +#endif + +#ifndef ARG_UNUSED +#define ARG_UNUSED(x) (void)(x) +#endif + +#ifndef LO_BYTE +#define LO_BYTE(x) ((uint8_t)(x & 0x00FF)) +#endif + +#ifndef HI_BYTE +#define HI_BYTE(x) ((uint8_t)((x & 0xFF00) >> 8)) +#endif + +/** + * @def MAX + * @brief The larger value between @p a and @p b. + * @note Arguments are evaluated twice. + */ +#ifndef MAX +/* Use Z_MAX for a GCC-only, single evaluation version */ +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +/** + * @def MIN + * @brief The smaller value between @p a and @p b. + * @note Arguments are evaluated twice. + */ +#ifndef MIN +/* Use Z_MIN for a GCC-only, single evaluation version */ +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef BCD +#define BCD(x) ((((x) / 10) << 4) | ((x) % 10)) +#endif + +#ifdef BIT +#undef BIT +#define BIT(n) (1UL << (n)) +#else +#define BIT(n) (1UL << (n)) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(array) \ + ((int)((sizeof(array) / sizeof((array)[0])))) +#endif + +#ifndef BSWAP16 +#define BSWAP16(u16) (__builtin_bswap16(u16)) +#endif +#ifndef BSWAP32 +#define BSWAP32(u32) (__builtin_bswap32(u32)) +#endif + +#define GET_BE16(field) \ + (((uint16_t)(field)[0] << 8) | ((uint16_t)(field)[1])) + +#define GET_BE32(field) \ + (((uint32_t)(field)[0] << 24) | ((uint32_t)(field)[1] << 16) | ((uint32_t)(field)[2] << 8) | ((uint32_t)(field)[3] << 0)) + +#define SET_BE16(field, value) \ + do { \ + (field)[0] = (uint8_t)((value) >> 8); \ + (field)[1] = (uint8_t)((value) >> 0); \ + } while (0) + +#define SET_BE24(field, value) \ + do { \ + (field)[0] = (uint8_t)((value) >> 16); \ + (field)[1] = (uint8_t)((value) >> 8); \ + (field)[2] = (uint8_t)((value) >> 0); \ + } while (0) + +#define SET_BE32(field, value) \ + do { \ + (field)[0] = (uint8_t)((value) >> 24); \ + (field)[1] = (uint8_t)((value) >> 16); \ + (field)[2] = (uint8_t)((value) >> 8); \ + (field)[3] = (uint8_t)((value) >> 0); \ + } while (0) + +#define REQTYPE_GET_DIR(x) (((x) >> 7) & 0x01) +#define REQTYPE_GET_TYPE(x) (((x) >> 5) & 0x03U) +#define REQTYPE_GET_RECIP(x) ((x)&0x1F) + +#define GET_DESC_TYPE(x) (((x) >> 8) & 0xFFU) +#define GET_DESC_INDEX(x) ((x)&0xFFU) + +#define WBVAL(x) (x & 0xFF), ((x >> 8) & 0xFF) +#define DBVAL(x) (x & 0xFF), ((x >> 8) & 0xFF), ((x >> 16) & 0xFF), ((x >> 24) & 0xFF) + +#define USB_DESC_SECTION __attribute__((section("usb_desc"))) __USED __ALIGNED(1) + +/* DEBUG level */ +#define USB_DBG_ERROR 0 +#define USB_DBG_WARNING 1 +#define USB_DBG_INFO 2 +#define USB_DBG_LOG 3 + +#ifndef USB_DBG_LEVEL +#define USB_DBG_LEVEL USB_DBG_INFO +#endif + +#ifndef USB_DBG_TAG +#define USB_DBG_TAG "USB" +#endif +/* + * The color for terminal (foreground) + * BLACK 30 + * RED 31 + * GREEN 32 + * YELLOW 33 + * BLUE 34 + * PURPLE 35 + * CYAN 36 + * WHITE 37 + */ +#define _USB_DBG_COLOR(n) printf("\033[" #n "m") +#define _USB_DBG_LOG_HDR(lvl_name, color_n) \ + printf("\033[" #color_n "m[" lvl_name "/" USB_DBG_TAG "] ") +#define _USB_DBG_LOG_X_END \ + printf("\033[0m") + +#define usb_dbg_log_line(lvl, color_n, fmt, ...) \ + do { \ + _USB_DBG_LOG_HDR(lvl, color_n); \ + printf(fmt, ##__VA_ARGS__); \ + _USB_DBG_LOG_X_END; \ + } while (0) + +#if (USB_DBG_LEVEL >= USB_DBG_LOG) +#define USB_LOG_DBG(fmt, ...) usb_dbg_log_line("D", 0, fmt, ##__VA_ARGS__) +#else +#define USB_LOG_DBG(...) +#endif + +#if (USB_DBG_LEVEL >= USB_DBG_INFO) +#define USB_LOG_INFO(fmt, ...) usb_dbg_log_line("I", 32, fmt, ##__VA_ARGS__) +#else +#define USB_LOG_INFO(...) +#endif + +#if (USB_DBG_LEVEL >= USB_DBG_WARNING) +#define USB_LOG_WRN(fmt, ...) usb_dbg_log_line("W", 33, fmt, ##__VA_ARGS__) +#else +#define USB_LOG_WRN(...) +#endif + +#if (USB_DBG_LEVEL >= USB_DBG_ERROR) +#define USB_LOG_ERR(fmt, ...) usb_dbg_log_line("E", 31, fmt, ##__VA_ARGS__) +#else +#define USB_LOG_ERR(...) +#endif + +void usb_assert(const char *filename, int linenum); +#define USB_ASSERT(f) \ + do { \ + if (!(f)) \ + usb_assert(__FILE__, __LINE__); \ + } while (0) + +#endif diff --git a/core/usbd_core.c b/core/usbd_core.c index 34daa302..6e88ecdb 100644 --- a/core/usbd_core.c +++ b/core/usbd_core.c @@ -1,1299 +1,1299 @@ -/** - * @file usbd_core.c - * @brief - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#include "usbd_core.h" - -#define USBD_EP_CALLBACK_LIST_SEARCH 0 -#define USBD_EP_CALLBACK_ARR_SEARCH 1 -#define USBD_EP_CALLBACK_SEARCH_METHOD USBD_EP_CALLBACK_ARR_SEARCH - -/* general descriptor field offsets */ -#define DESC_bLength 0 /** Length offset */ -#define DESC_bDescriptorType 1 /** Descriptor type offset */ - -/* config descriptor field offsets */ -#define CONF_DESC_wTotalLength 2 /** Total length offset */ -#define CONF_DESC_bConfigurationValue 5 /** Configuration value offset */ -#define CONF_DESC_bmAttributes 7 /** configuration characteristics */ - -/* interface descriptor field offsets */ -#define INTF_DESC_bInterfaceNumber 2 /** Interface number offset */ -#define INTF_DESC_bAlternateSetting 3 /** Alternate setting offset */ - -#define USB_REQUEST_BUFFER_SIZE 256 -#define USB_EP_OUT_NUM 8 -#define USB_EP_IN_NUM 8 - -static struct usbd_core_cfg_priv { - /** Setup packet */ - struct usb_setup_packet setup; - /** Pointer to data buffer */ - uint8_t *ep0_data_buf; - /** Remaining bytes in buffer */ - uint32_t ep0_data_buf_residue; - /** Total length of control transfer */ - uint32_t ep0_data_buf_len; - /** Zero length packet flag of control transfer */ - bool zlp_flag; - /** Pointer to registered descriptors */ - const uint8_t *descriptors; - /* Buffer used for storing standard, class and vendor request data */ - uint8_t req_data[USB_REQUEST_BUFFER_SIZE]; - -#if USBD_EP_CALLBACK_SEARCH_METHOD == USBD_EP_CALLBACK_ARR_SEARCH - usbd_endpoint_callback in_ep_cb[USB_EP_IN_NUM]; - usbd_endpoint_callback out_ep_cb[USB_EP_OUT_NUM]; -#endif - /** Variable to check whether the usb has been configured */ - bool configured; - /** Currently selected configuration */ - uint8_t configuration; - /** Remote wakeup feature status */ - uint16_t remote_wakeup; - uint8_t reserved; -} usbd_core_cfg; - -static usb_slist_t usbd_class_head = USB_SLIST_OBJECT_INIT(usbd_class_head); -static struct usb_msosv1_descriptor *msosv1_desc; -static struct usb_msosv2_descriptor *msosv2_desc; -static struct usb_bos_descriptor *bos_desc; - -/** - * @brief print the contents of a setup packet - * - * @param [in] setup The setup packet - * - */ -static void usbd_print_setup(struct usb_setup_packet *setup) -{ - USB_LOG_INFO("Setup: " - "bmRequestType 0x%02x, bRequest 0x%02x, wValue 0x%04x, wIndex 0x%04x, wLength 0x%04x\r\n", - setup->bmRequestType, - setup->bRequest, - setup->wValue, - setup->wIndex, - setup->wLength); -} - -/** - * @brief Check if the device is in Configured state - * - * @return true if Configured, false otherwise. - */ -static bool is_device_configured(void) -{ - return (usbd_core_cfg.configuration != 0); -} -/** - * @brief Check if the interface of given number is valid - * - * @param [in] interface Number of the addressed interface - * - * This function searches through descriptor and checks - * is the Host has addressed valid interface. - * - * @return true if interface exists - valid - */ -static bool is_interface_valid(uint8_t interface) -{ - const uint8_t *p = (uint8_t *)usbd_core_cfg.descriptors; - const struct usb_configuration_descriptor *cfg_descr; - - /* Search through descriptor for matching interface */ - while (p[DESC_bLength] != 0U) { - if (p[DESC_bDescriptorType] == USB_DESCRIPTOR_TYPE_CONFIGURATION) { - cfg_descr = (const struct usb_configuration_descriptor *)p; - - if (interface < cfg_descr->bNumInterfaces) { - return true; - } - } - - p += p[DESC_bLength]; - } - - return false; -} -/** - * @brief Check if the endpoint of given address is valid - * - * @param [in] ep Address of the Endpoint - * - * This function checks if the Endpoint of given address - * is valid for the configured device. Valid Endpoint is - * either Control Endpoint or one used by the device. - * - * @return true if endpoint exists - valid - */ -static bool is_ep_valid(uint8_t ep) -{ - /* Check if its Endpoint 0 */ - if ((ep & 0x7f) == 0) { - return true; - } - - return true; -} -#if USBD_EP_CALLBACK_SEARCH_METHOD == USBD_EP_CALLBACK_ARR_SEARCH -static void usbd_ep_callback_register(void) -{ - usb_slist_t *i, *j, *k; - usb_slist_for_each(i, &usbd_class_head) - { - usbd_class_t *devclass = usb_slist_entry(i, struct usbd_class, list); - - usb_slist_for_each(j, &devclass->intf_list) - { - usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list); - - usb_slist_for_each(k, &intf->ep_list) - { - usbd_endpoint_t *ept = usb_slist_entry(k, struct usbd_endpoint, list); - - if (ept->ep_cb) { - if (ept->ep_addr & 0x80) { - usbd_core_cfg.in_ep_cb[ept->ep_addr & 0x7f] = ept->ep_cb; - } else { - usbd_core_cfg.out_ep_cb[ept->ep_addr & 0x7f] = ept->ep_cb; - } - } - } - } - } -} -#endif -/** - * @brief configure and enable endpoint - * - * This function sets endpoint configuration according to one specified in USB - * endpoint descriptor and then enables it for data transfers. - * - * @param [in] ep_desc Endpoint descriptor byte array - * - * @return true if successfully configured and enabled - */ -static bool usbd_set_endpoint(const struct usb_endpoint_descriptor *ep_desc) -{ - struct usbd_endpoint_cfg ep_cfg; - - ep_cfg.ep_addr = ep_desc->bEndpointAddress; - ep_cfg.ep_mps = ep_desc->wMaxPacketSize; - ep_cfg.ep_type = ep_desc->bmAttributes & USBD_EP_TYPE_MASK; - - USB_LOG_INFO("Open endpoint:0x%x type:%u mps:%u\r\n", - ep_cfg.ep_addr, ep_cfg.ep_type, ep_cfg.ep_mps); - - usbd_ep_open(&ep_cfg); - usbd_core_cfg.configured = true; - - return true; -} -/** - * @brief Disable endpoint for transferring data - * - * This function cancels transfers that are associated with endpoint and - * disabled endpoint itself. - * - * @param [in] ep_desc Endpoint descriptor byte array - * - * @return true if successfully deconfigured and disabled - */ -static bool usbd_reset_endpoint(const struct usb_endpoint_descriptor *ep_desc) -{ - struct usbd_endpoint_cfg ep_cfg; - - ep_cfg.ep_addr = ep_desc->bEndpointAddress; - ep_cfg.ep_mps = ep_desc->wMaxPacketSize; - ep_cfg.ep_type = ep_desc->bmAttributes & USBD_EP_TYPE_MASK; - - USB_LOG_INFO("Close endpoint:0x%x type:%u\r\n", - ep_cfg.ep_addr, ep_cfg.ep_type); - - usbd_ep_close(ep_cfg.ep_addr); - - return true; -} - -/** - * @brief get specified USB descriptor - * - * This function parses the list of installed USB descriptors and attempts - * to find the specified USB descriptor. - * - * @param [in] type_index Type and index of the descriptor - * @param [out] data Descriptor data - * @param [out] len Descriptor length - * - * @return true if the descriptor was found, false otherwise - */ -static bool usbd_get_descriptor(uint16_t type_index, uint8_t **data, uint32_t *len) -{ - uint8_t type = 0U; - uint8_t index = 0U; - uint8_t *p = NULL; - uint32_t cur_index = 0U; - bool found = false; - - type = GET_DESC_TYPE(type_index); - index = GET_DESC_INDEX(type_index); - - if ((type == USB_DESCRIPTOR_TYPE_STRING) && (index == USB_OSDESC_STRING_DESC_INDEX)) { - USB_LOG_INFO("read MS OS 2.0 descriptor string\r\n"); - - if (!msosv1_desc) { - return false; - } - - *data = (uint8_t *)msosv1_desc->string; - *len = msosv1_desc->string_len; - - return true; - } else if (type == USB_DESCRIPTOR_TYPE_BINARY_OBJECT_STORE) { - USB_LOG_INFO("read BOS descriptor string\r\n"); - - if (!bos_desc) { - return false; - } - - *data = bos_desc->string; - *len = bos_desc->string_len; - return true; - } - /* - * Invalid types of descriptors, - * see USB Spec. Revision 2.0, 9.4.3 Get Descriptor - */ - else if ((type == USB_DESCRIPTOR_TYPE_INTERFACE) || (type == USB_DESCRIPTOR_TYPE_ENDPOINT) || -#ifndef CONFIG_USB_HS - (type > USB_DESCRIPTOR_TYPE_ENDPOINT)) { -#else - (type > USB_DESCRIPTOR_TYPE_OTHER_SPEED)) { -#endif - return false; - } - - p = (uint8_t *)usbd_core_cfg.descriptors; - - cur_index = 0U; - - while (p[DESC_bLength] != 0U) { - if (p[DESC_bDescriptorType] == type) { - if (cur_index == index) { - found = true; - break; - } - - cur_index++; - } - - /* skip to next descriptor */ - p += p[DESC_bLength]; - } - - if (found) { - /* set data pointer */ - *data = p; - - /* get length from structure */ - if (type == USB_DESCRIPTOR_TYPE_CONFIGURATION) { - /* configuration descriptor is an - * exception, length is at offset - * 2 and 3 - */ - *len = (p[CONF_DESC_wTotalLength]) | - (p[CONF_DESC_wTotalLength + 1] << 8); - } else { - /* normally length is at offset 0 */ - *len = p[DESC_bLength]; - } - } else { - /* nothing found */ - USB_LOG_ERR("descriptor not found!\r\n", type, index); - } - - return found; -} - -/** - * @brief set USB configuration - * - * This function configures the device according to the specified configuration - * index and alternate setting by parsing the installed USB descriptor list. - * A configuration index of 0 unconfigures the device. - * - * @param [in] config_index Configuration index - * @param [in] alt_setting Alternate setting number - * - * @return true if successfully configured false if error or unconfigured - */ -static bool usbd_set_configuration(uint8_t config_index, uint8_t alt_setting) -{ - uint8_t *p = (uint8_t *)usbd_core_cfg.descriptors; - uint8_t cur_alt_setting = 0xFF; - uint8_t cur_config = 0xFF; - bool found = false; - - if (config_index == 0U) { - /* TODO: unconfigure device */ - USB_LOG_ERR("Device not configured - invalid configuration\r\n"); - return true; - } - - /* configure endpoints for this configuration/altsetting */ - while (p[DESC_bLength] != 0U) { - switch (p[DESC_bDescriptorType]) { - case USB_DESCRIPTOR_TYPE_CONFIGURATION: - /* remember current configuration index */ - cur_config = p[CONF_DESC_bConfigurationValue]; - - if (cur_config == config_index) { - found = true; - } - - break; - - case USB_DESCRIPTOR_TYPE_INTERFACE: - /* remember current alternate setting */ - cur_alt_setting = - p[INTF_DESC_bAlternateSetting]; - break; - - case USB_DESCRIPTOR_TYPE_ENDPOINT: - if ((cur_config != config_index) || - (cur_alt_setting != alt_setting)) { - break; - } - - found = usbd_set_endpoint((struct usb_endpoint_descriptor *)p); - break; - - default: - break; - } - - /* skip to next descriptor */ - p += p[DESC_bLength]; - } - - return found; -} - -/** - * @brief set USB interface - * - * @param [in] iface Interface index - * @param [in] alt_setting Alternate setting number - * - * @return true if successfully configured false if error or unconfigured - */ -static bool usbd_set_interface(uint8_t iface, uint8_t alt_setting) -{ - const uint8_t *p = usbd_core_cfg.descriptors; - const uint8_t *if_desc = NULL; - struct usb_endpoint_descriptor *ep_desc; - uint8_t cur_alt_setting = 0xFF; - uint8_t cur_iface = 0xFF; - bool ret = false; - - USB_LOG_DBG("iface %u alt_setting %u\r\n", iface, alt_setting); - - while (p[DESC_bLength] != 0U) { - switch (p[DESC_bDescriptorType]) { - case USB_DESCRIPTOR_TYPE_INTERFACE: - /* remember current alternate setting */ - cur_alt_setting = p[INTF_DESC_bAlternateSetting]; - cur_iface = p[INTF_DESC_bInterfaceNumber]; - - if (cur_iface == iface && - cur_alt_setting == alt_setting) { - if_desc = (void *)p; - } - - USB_LOG_DBG("Current iface %u alt setting %u", - cur_iface, cur_alt_setting); - break; - - case USB_DESCRIPTOR_TYPE_ENDPOINT: - if (cur_iface == iface) { - ep_desc = (struct usb_endpoint_descriptor *)p; - - if (cur_alt_setting != alt_setting) { - ret = usbd_reset_endpoint(ep_desc); - } else { - ret = usbd_set_endpoint(ep_desc); - } - } - - break; - - default: - break; - } - - /* skip to next descriptor */ - p += p[DESC_bLength]; - } - - usbd_event_notify_handler(USBD_EVENT_SET_INTERFACE, (void *)if_desc); - - return ret; -} - -/** - * @brief handle a standard device request - * - * @param [in] setup The setup packet - * @param [in,out] data Data buffer - * @param [in,out] len Pointer to data length - * - * @return true if the request was handled successfully - */ -static bool usbd_std_device_req_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) -{ - uint16_t value = setup->wValue; - // uint16_t index = setup->wIndex; - bool ret = true; - - switch (setup->bRequest) { - case USB_REQUEST_GET_STATUS: - USB_LOG_DBG("REQ_GET_STATUS\r\n"); - /* bit 0: self-powered */ - /* bit 1: remote wakeup */ - *data = (uint8_t *)&usbd_core_cfg.remote_wakeup; - - *len = 2; - break; - - case USB_REQUEST_CLEAR_FEATURE: - USB_LOG_DBG("REQ_CLEAR_FEATURE\r\n"); - ret = false; - - if (value == USB_FEATURE_REMOTE_WAKEUP) { - usbd_core_cfg.remote_wakeup = 0; - usbd_event_notify_handler(USBD_EVENT_CLEAR_REMOTE_WAKEUP, NULL); - ret = true; - } - - break; - - case USB_REQUEST_SET_FEATURE: - USB_LOG_DBG("REQ_SET_FEATURE\r\n"); - ret = false; - - if (value == USB_FEATURE_REMOTE_WAKEUP) { - usbd_core_cfg.remote_wakeup = 1; - usbd_event_notify_handler(USBD_EVENT_SET_REMOTE_WAKEUP, NULL); - ret = true; - } - - if (value == USB_FEATURE_TEST_MODE) { - /* put TEST_MODE code here */ - } - - break; - - case USB_REQUEST_SET_ADDRESS: - USB_LOG_DBG("REQ_SET_ADDRESS, addr 0x%x\r\n", value); - usbd_set_address(value); - break; - - case USB_REQUEST_GET_DESCRIPTOR: - USB_LOG_DBG("REQ_GET_DESCRIPTOR\r\n"); - ret = usbd_get_descriptor(value, data, len); - break; - - case USB_REQUEST_SET_DESCRIPTOR: - USB_LOG_DBG("Device req 0x%02x not implemented\r\n", setup->bRequest); - ret = false; - break; - - case USB_REQUEST_GET_CONFIGURATION: - USB_LOG_DBG("REQ_GET_CONFIGURATION\r\n"); - /* indicate if we are configured */ - *data = (uint8_t *)&usbd_core_cfg.configuration; - *len = 1; - break; - - case USB_REQUEST_SET_CONFIGURATION: - value &= 0xFF; - USB_LOG_DBG("REQ_SET_CONFIGURATION, conf 0x%x\r\n", value); - - if (!usbd_set_configuration(value, 0)) { - USB_LOG_DBG("USB Set Configuration failed\r\n"); - ret = false; - } else { - /* configuration successful, - * update current configuration - */ - usbd_core_cfg.configuration = value; - usbd_event_notify_handler(USBD_EVENT_CONFIGURED, NULL); - } - - break; - - case USB_REQUEST_GET_INTERFACE: - break; - - case USB_REQUEST_SET_INTERFACE: - break; - - default: - USB_LOG_ERR("Illegal device req 0x%02x\r\n", setup->bRequest); - ret = false; - break; - } - - return ret; -} - -/** - * @brief handle a standard interface request - * - * @param [in] setup The setup packet - * @param [in,out] data Data buffer - * @param [in,out] len Pointer to data length - * - * @return true if the request was handled successfully - */ -static bool usbd_std_interface_req_handler(struct usb_setup_packet *setup, - uint8_t **data, uint32_t *len) -{ - /** The device must be configured to accept standard interface - * requests and the addressed Interface must be valid. - */ - if (!is_device_configured() || - (!is_interface_valid((uint8_t)setup->wIndex))) { - return false; - } - - switch (setup->bRequest) { - case USB_REQUEST_GET_STATUS: - /* no bits specified */ - *data = (uint8_t *)&usbd_core_cfg.remote_wakeup; - - *len = 2; - break; - - case USB_REQUEST_CLEAR_FEATURE: - case USB_REQUEST_SET_FEATURE: - /* not defined for interface */ - return false; - - case USB_REQUEST_GET_INTERFACE: - /** This handler is called for classes that does not support - * alternate Interfaces so always return 0. Classes that - * support alternative interfaces handles GET_INTERFACE - * in custom_handler. - */ - *data = (uint8_t *)&usbd_core_cfg.reserved; - - *len = 1; - break; - - case USB_REQUEST_SET_INTERFACE: - USB_LOG_DBG("REQ_SET_INTERFACE\r\n"); - usbd_set_interface(setup->wIndex, setup->wValue); - break; - - default: - USB_LOG_ERR("Illegal interface req 0x%02x\r\n", setup->bRequest); - return false; - } - - return true; -} - -/** - * @brief handle a standard endpoint request - * - * @param [in] setup The setup packet - * @param [in,out] data Data buffer - * @param [in,out] len Pointer to data length - * - * @return true if the request was handled successfully - */ -static bool usbd_std_endpoint_req_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) -{ - uint8_t ep = (uint8_t)setup->wIndex; - - /* Check if request addresses valid Endpoint */ - if (!is_ep_valid(ep)) { - return false; - } - - switch (setup->bRequest) { - case USB_REQUEST_GET_STATUS: - - /** This request is valid for Control Endpoints when - * the device is not yet configured. For other - * Endpoints the device must be configured. - * Firstly check if addressed ep is Control Endpoint. - * If no then the device must be in Configured state - * to accept the request. - */ - if (((ep & 0x7f) == 0) || is_device_configured()) { - /* bit 0 - Endpoint halted or not */ - usbd_ep_is_stalled(ep, (uint8_t *)&usbd_core_cfg.remote_wakeup); - *data = (uint8_t *)&usbd_core_cfg.remote_wakeup; - - *len = 2; - break; - } - - return false; - - case USB_REQUEST_CLEAR_FEATURE: - if (setup->wValue == USB_FEATURE_ENDPOINT_HALT) { - /** This request is valid for Control Endpoints when - * the device is not yet configured. For other - * Endpoints the device must be configured. - * Firstly check if addressed ep is Control Endpoint. - * If no then the device must be in Configured state - * to accept the request. - */ - if (((ep & 0x7f) == 0) || is_device_configured()) { - USB_LOG_ERR("ep:%x clear halt\r\n", ep); - usbd_ep_clear_stall(ep); - usbd_event_notify_handler(USBD_EVENT_CLEAR_HALT, NULL); - break; - } - } - - /* only ENDPOINT_HALT defined for endpoints */ - return false; - - case USB_REQUEST_SET_FEATURE: - if (setup->wValue == USB_FEATURE_ENDPOINT_HALT) { - /** This request is valid for Control Endpoints when - * the device is not yet configured. For other - * Endpoints the device must be configured. - * Firstly check if addressed ep is Control Endpoint. - * If no then the device must be in Configured state - * to accept the request. - */ - if (((ep & 0x7f) == 0) || is_device_configured()) { - /* set HALT by stalling */ - USB_LOG_ERR("ep:%x set halt\r\n", ep); - usbd_ep_set_stall(ep); - usbd_event_notify_handler(USBD_EVENT_SET_HALT, NULL); - break; - } - } - - /* only ENDPOINT_HALT defined for endpoints */ - return false; - - case USB_REQUEST_SYNCH_FRAME: - - /* For Synch Frame request the device must be configured */ - if (is_device_configured()) { - /* Not supported, return false anyway */ - USB_LOG_DBG("ep req 0x%02x not implemented\r\n", setup->bRequest); - } - - return false; - - default: - USB_LOG_ERR("Illegal ep req 0x%02x\r\n", setup->bRequest); - return false; - } - - return true; -} - -/** - * @brief default handler for standard ('chapter 9') requests - * - * If a custom request handler was installed, this handler is called first. - * - * @param [in] setup The setup packet - * @param [in,out] data Data buffer - * @param [in,out] len Pointer to data length - * - * @return true if the request was handled successfully - */ -static int usbd_standard_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) -{ - int rc = 0; - - switch (setup->bmRequestType & USB_REQUEST_RECIPIENT_MASK) { - case USB_REQUEST_RECIPIENT_DEVICE: - if (usbd_std_device_req_handler(setup, data, len) == false) { - rc = -1; - } - - break; - - case USB_REQUEST_RECIPIENT_INTERFACE: - if (usbd_std_interface_req_handler(setup, data, len) == false) { - rc = -1; - } - - break; - - case USB_REQUEST_RECIPIENT_ENDPOINT: - if (usbd_std_endpoint_req_handler(setup, data, len) == false) { - rc = -1; - } - - break; - - default: - rc = -1; - } - - return rc; -} - -/** - * @brief handler for class requests - * - * If a custom request handler was installed, this handler is called first. - * - * @param [in] setup The setup packet - * @param [in,out] data Data buffer - * @param [in,out] len Pointer to data length - * - * @return true if the request was handled successfully - */ -static int usbd_class_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) -{ - USB_LOG_DBG("bRequest 0x%02x, wIndex 0x%04x\r\n", setup->bRequest, setup->wIndex); - - if ((setup->bmRequestType & USB_REQUEST_RECIPIENT_MASK) != USB_REQUEST_RECIPIENT_INTERFACE) { - return -1; - } - - usb_slist_t *i, *j; - usb_slist_for_each(i, &usbd_class_head) - { - usbd_class_t *devclass = usb_slist_entry(i, struct usbd_class, list); - - usb_slist_for_each(j, &devclass->intf_list) - { - usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list); - - if (intf->class_handler && (intf->intf_num == (setup->wIndex & 0xFF))) { - return intf->class_handler(setup, data, len); - } - } - } - return -1; -} - -/** - * @brief handler for vendor requests - * - * If a custom request handler was installed, this handler is called first. - * - * @param [in] setup The setup packet - * @param [in,out] data Data buffer - * @param [in,out] len Pointer to data length - * - * @return true if the request was handled successfully - */ -static int usbd_vendor_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) -{ - USB_LOG_DBG("bRequest 0x%02x, wValue0x%04x, wIndex 0x%04x\r\n", setup->bRequest, setup->wValue, setup->wIndex); - - // if((setup->bmRequestType & USB_REQUEST_RECIPIENT_MASK) != USB_REQUEST_RECIPIENT_DEVICE) - // { - // return -1; - // } - - if (msosv1_desc) { - if (setup->bRequest == msosv1_desc->vendor_code) { - switch (setup->wIndex) { - case 0x04: - USB_LOG_INFO("get Compat ID\r\n"); - *data = (uint8_t *)msosv1_desc->compat_id; - *len = msosv1_desc->compat_id_len; - - return 0; - case 0x05: - USB_LOG_INFO("get Compat id properties\r\n"); - *data = (uint8_t *)msosv1_desc->comp_id_property; - *len = msosv1_desc->comp_id_property_len; - - return 0; - default: - USB_LOG_ERR("unknown vendor code\r\n"); - return -1; - } - } - } else if (msosv2_desc) { - if (setup->bRequest == msosv2_desc->vendor_code) { - switch (setup->wIndex) { - case WINUSB_REQUEST_GET_DESCRIPTOR_SET: - USB_LOG_INFO("GET MS OS 2.0 Descriptor\r\n"); - *data = (uint8_t *)msosv2_desc->compat_id; - *len = msosv2_desc->compat_id_len; - return 0; - default: - USB_LOG_ERR("unknown vendor code\r\n"); - return -1; - } - } - } - - usb_slist_t *i, *j; - - usb_slist_for_each(i, &usbd_class_head) - { - usbd_class_t *devclass = usb_slist_entry(i, struct usbd_class, list); - - usb_slist_for_each(j, &devclass->intf_list) - { - usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list); - - if (intf->vendor_handler && !intf->vendor_handler(setup, data, len)) { - return 0; - } - } - } - - return -1; -} - -/** - * @brief handler for special requests - * - * If a custom request handler was installed, this handler is called first. - * - * @param [in] setup The setup packet - * @param [in,out] data Data buffer - * @param [in,out] len Pointer to data length - * - * @return true if the request was handled successfully - */ -static int usbd_custom_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) -{ - USB_LOG_DBG("bRequest 0x%02x, wIndex 0x%04x\r\n", setup->bRequest, setup->wIndex); - - if ((setup->bmRequestType & USB_REQUEST_RECIPIENT_MASK) != USB_REQUEST_RECIPIENT_INTERFACE) { - return -1; - } - - usb_slist_t *i, *j; - usb_slist_for_each(i, &usbd_class_head) - { - usbd_class_t *devclass = usb_slist_entry(i, struct usbd_class, list); - - usb_slist_for_each(j, &devclass->intf_list) - { - usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list); - - if (intf->custom_handler && (intf->intf_num == (setup->wIndex & 0xFF))) { - return intf->custom_handler(setup, data, len); - } - } - } - - return -1; -} - -/** - * @brief handle a request by calling one of the installed request handlers - * - * Local function to handle a request by calling one of the installed request - * handlers. In case of data going from host to device, the data is at *ppbData. - * In case of data going from device to host, the handler can either choose to - * write its data at *ppbData or update the data pointer. - * - * @param [in] setup The setup packet - * @param [in,out] data Data buffer - * @param [in,out] len Pointer to data length - * - * @return true if the request was handles successfully - */ -static bool usbd_setup_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) -{ - uint8_t type = setup->bmRequestType & USB_REQUEST_TYPE_MASK; - - if (!usbd_custom_request_handler(setup, data, len)) { - return true; - } - - if (type == USB_REQUEST_STANDARD) { - if (usbd_standard_request_handler(setup, data, len) < 0) { - USB_LOG_ERR("Handler Error %d\r\n", type); - usbd_print_setup(setup); - return false; - } - } else if (type == USB_REQUEST_CLASS) { - if (usbd_class_request_handler(setup, data, len) < 0) { - USB_LOG_ERR("Handler Error %d\r\n", type); - usbd_print_setup(setup); - return false; - } - } else if (type == USB_REQUEST_VENDOR) { - if (usbd_vendor_request_handler(setup, data, len) < 0) { - USB_LOG_ERR("Handler Error %d\r\n", type); - usbd_print_setup(setup); - return false; - } - } else { - return false; - } - - return true; -} -/** - * @brief send data or status to host - * - * @return N/A - */ -static void usbd_send_to_host(uint16_t len) -{ - uint32_t chunk = 0U; - - if (usbd_core_cfg.zlp_flag == false) { - chunk = usbd_core_cfg.ep0_data_buf_residue; - - if (usbd_ep_write(USB_CONTROL_IN_EP0, usbd_core_cfg.ep0_data_buf, usbd_core_cfg.ep0_data_buf_residue, &chunk) < 0) { - USB_LOG_ERR("USB write data failed\r\n"); - return; - } - - usbd_core_cfg.ep0_data_buf += chunk; - usbd_core_cfg.ep0_data_buf_residue -= chunk; - - /* - * Set ZLP flag when host asks for a bigger length and the - * last chunk is wMaxPacketSize long, to indicate the last - * packet. - */ - if ((!usbd_core_cfg.ep0_data_buf_residue) && (usbd_core_cfg.ep0_data_buf_len >= USB_CTRL_EP_MPS) && !(usbd_core_cfg.ep0_data_buf_len % USB_CTRL_EP_MPS)) { - /* Transfers a zero-length packet next*/ - usbd_core_cfg.zlp_flag = true; - } - } else { - usbd_core_cfg.zlp_flag = false; - USB_LOG_DBG("send zlp\r\n"); - if (usbd_ep_write(USB_CONTROL_IN_EP0, NULL, 0, NULL) < 0) { - USB_LOG_ERR("USB write zlp failed\r\n"); - return; - } - } -} - -static void usbd_ep0_setup_handler(void) -{ - struct usb_setup_packet *setup = &usbd_core_cfg.setup; - - /* - * OUT transfer, Setup packet, - * reset request message state machine - */ - if (usbd_ep_read(USB_CONTROL_OUT_EP0, (uint8_t *)setup, - sizeof(struct usb_setup_packet), NULL) < 0) { - USB_LOG_ERR("Read Setup Packet failed\r\n"); - usbd_ep_set_stall(USB_CONTROL_IN_EP0); - return; - } - - //usbd_print_setup(setup); - - if (setup->wLength > USB_REQUEST_BUFFER_SIZE) { - if ((setup->bmRequestType & USB_REQUEST_DIR_MASK) == USB_REQUEST_DIR_OUT) { - USB_LOG_ERR("Request buffer too small\r\n"); - usbd_ep_set_stall(USB_CONTROL_IN_EP0); - return; - } - } - - // usbd_core_cfg.ep0_data_buf = usbd_core_cfg.req_data; - usbd_core_cfg.ep0_data_buf_residue = setup->wLength; - usbd_core_cfg.ep0_data_buf_len = setup->wLength; - usbd_core_cfg.zlp_flag = false; - - /* this maybe set code in class request code */ - if (setup->wLength && - (setup->bmRequestType & USB_REQUEST_DIR_MASK) == USB_REQUEST_DIR_OUT) { - USB_LOG_DBG("prepare to out data\r\n"); - return; - } - - /* Ask installed handler to process request */ - if (!usbd_setup_request_handler(setup, &usbd_core_cfg.ep0_data_buf, &usbd_core_cfg.ep0_data_buf_len)) { - USB_LOG_ERR("usbd_setup_request_handler failed\r\n"); - usbd_ep_set_stall(USB_CONTROL_IN_EP0); - return; - } - - /* Send smallest of requested and offered length */ - usbd_core_cfg.ep0_data_buf_residue = MIN(usbd_core_cfg.ep0_data_buf_len, - setup->wLength); - /*Send data or status to host*/ - usbd_send_to_host(setup->wLength); -} - -static void usbd_ep0_out_handler(void) -{ - uint32_t chunk = 0U; - struct usb_setup_packet *setup = &usbd_core_cfg.setup; - - /* OUT transfer, status packets */ - if (usbd_core_cfg.ep0_data_buf_residue == 0) { - /* absorb zero-length status message */ - USB_LOG_DBG("recv status\r\n"); - - if (usbd_ep_read(USB_CONTROL_OUT_EP0, - NULL, - 0, NULL) < 0) { - USB_LOG_ERR("Read DATA Packet failed\r\n"); - usbd_ep_set_stall(USB_CONTROL_IN_EP0); - } - - return; - } - - usbd_core_cfg.ep0_data_buf = usbd_core_cfg.req_data; - - /* OUT transfer, data packets */ - if (usbd_ep_read(USB_CONTROL_OUT_EP0, - usbd_core_cfg.ep0_data_buf, - usbd_core_cfg.ep0_data_buf_residue, &chunk) < 0) { - USB_LOG_ERR("Read DATA Packet failed\r\n"); - usbd_ep_set_stall(USB_CONTROL_IN_EP0); - return; - } - - usbd_core_cfg.ep0_data_buf += chunk; - usbd_core_cfg.ep0_data_buf_residue -= chunk; - - if (usbd_core_cfg.ep0_data_buf_residue == 0) { - /* Received all, send data to handler */ - usbd_core_cfg.ep0_data_buf = usbd_core_cfg.req_data; - - if (!usbd_setup_request_handler(setup, &usbd_core_cfg.ep0_data_buf, &usbd_core_cfg.ep0_data_buf_len)) { - USB_LOG_ERR("usbd_setup_request_handler1 failed\r\n"); - usbd_ep_set_stall(USB_CONTROL_IN_EP0); - return; - } - - /*Send status to host*/ - usbd_send_to_host(setup->wLength); - } else { - USB_LOG_ERR("ep0_data_buf_residue is not zero\r\n"); - } -} - -static void usbd_ep0_in_handler(void) -{ - struct usb_setup_packet *setup = &usbd_core_cfg.setup; - - /* Send more data if available */ - if (usbd_core_cfg.ep0_data_buf_residue != 0 || usbd_core_cfg.zlp_flag == true) { - usbd_send_to_host(setup->wLength); - } else { - /*ep0 tx has completed,and no data to send,so do nothing*/ - } -} - -static void usbd_ep_out_handler(uint8_t ep) -{ -#if USBD_EP_CALLBACK_SEARCH_METHOD == USBD_EP_CALLBACK_LIST_SEARCH - usb_slist_t *i, *j, *k; - usb_slist_for_each(i, &usbd_class_head) - { - usbd_class_t *devclass = usb_slist_entry(i, struct usbd_class, list); - - usb_slist_for_each(j, &devclass->intf_list) - { - usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list); - - usb_slist_for_each(k, &intf->ep_list) - { - usbd_endpoint_t *ept = usb_slist_entry(k, struct usbd_endpoint, list); - - if ((ept->ep_addr == ep) && ept->ep_cb) { - ept->ep_cb(ep); - } - } - } - } -#else - - if (usbd_core_cfg.out_ep_cb[ep & 0x7f]) { - usbd_core_cfg.out_ep_cb[ep & 0x7f](ep); - } - -#endif -} - -static void usbd_ep_in_handler(uint8_t ep) -{ -#if USBD_EP_CALLBACK_SEARCH_METHOD == USBD_EP_CALLBACK_LIST_SEARCH - usb_slist_t *i, *j, *k; - usb_slist_for_each(i, &usbd_class_head) - { - usbd_class_t *devclass = usb_slist_entry(i, struct usbd_class, list); - - usb_slist_for_each(j, &devclass->intf_list) - { - usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list); - - usb_slist_for_each(k, &intf->ep_list) - { - usbd_endpoint_t *ept = usb_slist_entry(k, struct usbd_endpoint, list); - - if ((ept->ep_addr == ep) && ept->ep_cb) { - ept->ep_cb(ep); - } - } - } - } -#else - - if (usbd_core_cfg.in_ep_cb[ep & 0x7f]) { - usbd_core_cfg.in_ep_cb[ep & 0x7f](ep); - } - -#endif -} - -static void usbd_class_event_notify_handler(uint8_t event, void *arg) -{ - usb_slist_t *i, *j; - usb_slist_for_each(i, &usbd_class_head) - { - usbd_class_t *devclass = usb_slist_entry(i, struct usbd_class, list); - - usb_slist_for_each(j, &devclass->intf_list) - { - usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list); - - if (intf->notify_handler) { - intf->notify_handler(event, arg); - } - } - } -} - -void usbd_event_notify_handler(uint8_t event, void *arg) -{ - switch (event) { - case USBD_EVENT_RESET: - usbd_set_address(0); - usbd_core_cfg.configured = 0; - usbd_core_cfg.configuration = 0; - struct usbd_endpoint_cfg ep0_cfg; - ep0_cfg.ep_mps = USB_CTRL_EP_MPS; - ep0_cfg.ep_type = USBD_EP_TYPE_CTRL; - ep0_cfg.ep_addr = USB_CONTROL_IN_EP0; - /*set USB_CONTROL_IN_EP0 nak*/ - usbd_ep_open(&ep0_cfg); - - ep0_cfg.ep_addr = USB_CONTROL_OUT_EP0; - /*set USB_CONTROL_OUT_EP0 ack to prepare receiving setup data*/ - usbd_ep_open(&ep0_cfg); -#if USBD_EP_CALLBACK_SEARCH_METHOD == USBD_EP_CALLBACK_ARR_SEARCH - usbd_ep_callback_register(); -#endif - - case USBD_EVENT_ERROR: - case USBD_EVENT_SOF: - case USBD_EVENT_CONNECTED: - case USBD_EVENT_CONFIGURED: - case USBD_EVENT_SUSPEND: - case USBD_EVENT_DISCONNECTED: - case USBD_EVENT_RESUME: - case USBD_EVENT_SET_INTERFACE: - case USBD_EVENT_SET_REMOTE_WAKEUP: - case USBD_EVENT_CLEAR_REMOTE_WAKEUP: - case USBD_EVENT_SET_HALT: - case USBD_EVENT_CLEAR_HALT: - usbd_class_event_notify_handler(event, arg); - break; - - case USBD_EVENT_SETUP_NOTIFY: - usbd_ep0_setup_handler(); - break; - - case USBD_EVENT_EP0_IN_NOTIFY: - usbd_ep0_in_handler(); - break; - - case USBD_EVENT_EP0_OUT_NOTIFY: - usbd_ep0_out_handler(); - break; - - case USBD_EVENT_EP_IN_NOTIFY: - usbd_ep_in_handler((uint32_t)arg); - break; - - case USBD_EVENT_EP_OUT_NOTIFY: - usbd_ep_out_handler((uint32_t)arg); - break; - - default: - USB_LOG_ERR("USB unknown event: %d\r\n", event); - break; - } -} - -void usbd_desc_register(const uint8_t *desc) -{ - usbd_core_cfg.descriptors = desc; -} -/* Register MS OS Descriptors version 1 */ -void usbd_msosv1_desc_register(struct usb_msosv1_descriptor *desc) -{ - msosv1_desc = desc; -} - -/* Register MS OS Descriptors version 2 */ -void usbd_msosv2_desc_register(struct usb_msosv2_descriptor *desc) -{ - msosv2_desc = desc; -} - -void usbd_bos_desc_register(struct usb_bos_descriptor *desc) -{ - bos_desc = desc; -} - -void usbd_class_register(usbd_class_t *devclass) -{ - usb_slist_add_tail(&usbd_class_head, &devclass->list); - usb_slist_init(&devclass->intf_list); -} - -void usbd_class_add_interface(usbd_class_t *devclass, usbd_interface_t *intf) -{ - static uint8_t intf_offset = 0; - intf->intf_num = intf_offset; - usb_slist_add_tail(&devclass->intf_list, &intf->list); - usb_slist_init(&intf->ep_list); - intf_offset++; -} - -void usbd_interface_add_endpoint(usbd_interface_t *intf, usbd_endpoint_t *ep) -{ - usb_slist_add_tail(&intf->ep_list, &ep->list); -} - -bool usb_device_is_configured(void) -{ - return usbd_core_cfg.configured; -} +/** + * @file usbd_core.c + * @brief + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#include "usbd_core.h" + +#define USBD_EP_CALLBACK_LIST_SEARCH 0 +#define USBD_EP_CALLBACK_ARR_SEARCH 1 +#define USBD_EP_CALLBACK_SEARCH_METHOD USBD_EP_CALLBACK_ARR_SEARCH + +/* general descriptor field offsets */ +#define DESC_bLength 0 /** Length offset */ +#define DESC_bDescriptorType 1 /** Descriptor type offset */ + +/* config descriptor field offsets */ +#define CONF_DESC_wTotalLength 2 /** Total length offset */ +#define CONF_DESC_bConfigurationValue 5 /** Configuration value offset */ +#define CONF_DESC_bmAttributes 7 /** configuration characteristics */ + +/* interface descriptor field offsets */ +#define INTF_DESC_bInterfaceNumber 2 /** Interface number offset */ +#define INTF_DESC_bAlternateSetting 3 /** Alternate setting offset */ + +#define USB_REQUEST_BUFFER_SIZE 256 +#define USB_EP_OUT_NUM 8 +#define USB_EP_IN_NUM 8 + +static struct usbd_core_cfg_priv { + /** Setup packet */ + struct usb_setup_packet setup; + /** Pointer to data buffer */ + uint8_t *ep0_data_buf; + /** Remaining bytes in buffer */ + uint32_t ep0_data_buf_residue; + /** Total length of control transfer */ + uint32_t ep0_data_buf_len; + /** Zero length packet flag of control transfer */ + bool zlp_flag; + /** Pointer to registered descriptors */ + const uint8_t *descriptors; + /* Buffer used for storing standard, class and vendor request data */ + uint8_t req_data[USB_REQUEST_BUFFER_SIZE]; + +#if USBD_EP_CALLBACK_SEARCH_METHOD == USBD_EP_CALLBACK_ARR_SEARCH + usbd_endpoint_callback in_ep_cb[USB_EP_IN_NUM]; + usbd_endpoint_callback out_ep_cb[USB_EP_OUT_NUM]; +#endif + /** Variable to check whether the usb has been configured */ + bool configured; + /** Currently selected configuration */ + uint8_t configuration; + /** Remote wakeup feature status */ + uint16_t remote_wakeup; + uint8_t reserved; +} usbd_core_cfg; + +static usb_slist_t usbd_class_head = USB_SLIST_OBJECT_INIT(usbd_class_head); +static struct usb_msosv1_descriptor *msosv1_desc; +static struct usb_msosv2_descriptor *msosv2_desc; +static struct usb_bos_descriptor *bos_desc; + +/** + * @brief print the contents of a setup packet + * + * @param [in] setup The setup packet + * + */ +static void usbd_print_setup(struct usb_setup_packet *setup) +{ + USB_LOG_INFO("Setup: " + "bmRequestType 0x%02x, bRequest 0x%02x, wValue 0x%04x, wIndex 0x%04x, wLength 0x%04x\r\n", + setup->bmRequestType, + setup->bRequest, + setup->wValue, + setup->wIndex, + setup->wLength); +} + +/** + * @brief Check if the device is in Configured state + * + * @return true if Configured, false otherwise. + */ +static bool is_device_configured(void) +{ + return (usbd_core_cfg.configuration != 0); +} +/** + * @brief Check if the interface of given number is valid + * + * @param [in] interface Number of the addressed interface + * + * This function searches through descriptor and checks + * is the Host has addressed valid interface. + * + * @return true if interface exists - valid + */ +static bool is_interface_valid(uint8_t interface) +{ + const uint8_t *p = (uint8_t *)usbd_core_cfg.descriptors; + const struct usb_configuration_descriptor *cfg_descr; + + /* Search through descriptor for matching interface */ + while (p[DESC_bLength] != 0U) { + if (p[DESC_bDescriptorType] == USB_DESCRIPTOR_TYPE_CONFIGURATION) { + cfg_descr = (const struct usb_configuration_descriptor *)p; + + if (interface < cfg_descr->bNumInterfaces) { + return true; + } + } + + p += p[DESC_bLength]; + } + + return false; +} +/** + * @brief Check if the endpoint of given address is valid + * + * @param [in] ep Address of the Endpoint + * + * This function checks if the Endpoint of given address + * is valid for the configured device. Valid Endpoint is + * either Control Endpoint or one used by the device. + * + * @return true if endpoint exists - valid + */ +static bool is_ep_valid(uint8_t ep) +{ + /* Check if its Endpoint 0 */ + if ((ep & 0x7f) == 0) { + return true; + } + + return true; +} +#if USBD_EP_CALLBACK_SEARCH_METHOD == USBD_EP_CALLBACK_ARR_SEARCH +static void usbd_ep_callback_register(void) +{ + usb_slist_t *i, *j, *k; + usb_slist_for_each(i, &usbd_class_head) + { + usbd_class_t *devclass = usb_slist_entry(i, struct usbd_class, list); + + usb_slist_for_each(j, &devclass->intf_list) + { + usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list); + + usb_slist_for_each(k, &intf->ep_list) + { + usbd_endpoint_t *ept = usb_slist_entry(k, struct usbd_endpoint, list); + + if (ept->ep_cb) { + if (ept->ep_addr & 0x80) { + usbd_core_cfg.in_ep_cb[ept->ep_addr & 0x7f] = ept->ep_cb; + } else { + usbd_core_cfg.out_ep_cb[ept->ep_addr & 0x7f] = ept->ep_cb; + } + } + } + } + } +} +#endif +/** + * @brief configure and enable endpoint + * + * This function sets endpoint configuration according to one specified in USB + * endpoint descriptor and then enables it for data transfers. + * + * @param [in] ep_desc Endpoint descriptor byte array + * + * @return true if successfully configured and enabled + */ +static bool usbd_set_endpoint(const struct usb_endpoint_descriptor *ep_desc) +{ + struct usbd_endpoint_cfg ep_cfg; + + ep_cfg.ep_addr = ep_desc->bEndpointAddress; + ep_cfg.ep_mps = ep_desc->wMaxPacketSize; + ep_cfg.ep_type = ep_desc->bmAttributes & USBD_EP_TYPE_MASK; + + USB_LOG_INFO("Open endpoint:0x%x type:%u mps:%u\r\n", + ep_cfg.ep_addr, ep_cfg.ep_type, ep_cfg.ep_mps); + + usbd_ep_open(&ep_cfg); + usbd_core_cfg.configured = true; + + return true; +} +/** + * @brief Disable endpoint for transferring data + * + * This function cancels transfers that are associated with endpoint and + * disabled endpoint itself. + * + * @param [in] ep_desc Endpoint descriptor byte array + * + * @return true if successfully deconfigured and disabled + */ +static bool usbd_reset_endpoint(const struct usb_endpoint_descriptor *ep_desc) +{ + struct usbd_endpoint_cfg ep_cfg; + + ep_cfg.ep_addr = ep_desc->bEndpointAddress; + ep_cfg.ep_mps = ep_desc->wMaxPacketSize; + ep_cfg.ep_type = ep_desc->bmAttributes & USBD_EP_TYPE_MASK; + + USB_LOG_INFO("Close endpoint:0x%x type:%u\r\n", + ep_cfg.ep_addr, ep_cfg.ep_type); + + usbd_ep_close(ep_cfg.ep_addr); + + return true; +} + +/** + * @brief get specified USB descriptor + * + * This function parses the list of installed USB descriptors and attempts + * to find the specified USB descriptor. + * + * @param [in] type_index Type and index of the descriptor + * @param [out] data Descriptor data + * @param [out] len Descriptor length + * + * @return true if the descriptor was found, false otherwise + */ +static bool usbd_get_descriptor(uint16_t type_index, uint8_t **data, uint32_t *len) +{ + uint8_t type = 0U; + uint8_t index = 0U; + uint8_t *p = NULL; + uint32_t cur_index = 0U; + bool found = false; + + type = GET_DESC_TYPE(type_index); + index = GET_DESC_INDEX(type_index); + + if ((type == USB_DESCRIPTOR_TYPE_STRING) && (index == USB_OSDESC_STRING_DESC_INDEX)) { + USB_LOG_INFO("read MS OS 2.0 descriptor string\r\n"); + + if (!msosv1_desc) { + return false; + } + + *data = (uint8_t *)msosv1_desc->string; + *len = msosv1_desc->string_len; + + return true; + } else if (type == USB_DESCRIPTOR_TYPE_BINARY_OBJECT_STORE) { + USB_LOG_INFO("read BOS descriptor string\r\n"); + + if (!bos_desc) { + return false; + } + + *data = bos_desc->string; + *len = bos_desc->string_len; + return true; + } + /* + * Invalid types of descriptors, + * see USB Spec. Revision 2.0, 9.4.3 Get Descriptor + */ + else if ((type == USB_DESCRIPTOR_TYPE_INTERFACE) || (type == USB_DESCRIPTOR_TYPE_ENDPOINT) || +#ifndef CONFIG_USB_HS + (type > USB_DESCRIPTOR_TYPE_ENDPOINT)) { +#else + (type > USB_DESCRIPTOR_TYPE_OTHER_SPEED)) { +#endif + return false; + } + + p = (uint8_t *)usbd_core_cfg.descriptors; + + cur_index = 0U; + + while (p[DESC_bLength] != 0U) { + if (p[DESC_bDescriptorType] == type) { + if (cur_index == index) { + found = true; + break; + } + + cur_index++; + } + + /* skip to next descriptor */ + p += p[DESC_bLength]; + } + + if (found) { + /* set data pointer */ + *data = p; + + /* get length from structure */ + if (type == USB_DESCRIPTOR_TYPE_CONFIGURATION) { + /* configuration descriptor is an + * exception, length is at offset + * 2 and 3 + */ + *len = (p[CONF_DESC_wTotalLength]) | + (p[CONF_DESC_wTotalLength + 1] << 8); + } else { + /* normally length is at offset 0 */ + *len = p[DESC_bLength]; + } + } else { + /* nothing found */ + USB_LOG_ERR("descriptor not found!\r\n", type, index); + } + + return found; +} + +/** + * @brief set USB configuration + * + * This function configures the device according to the specified configuration + * index and alternate setting by parsing the installed USB descriptor list. + * A configuration index of 0 unconfigures the device. + * + * @param [in] config_index Configuration index + * @param [in] alt_setting Alternate setting number + * + * @return true if successfully configured false if error or unconfigured + */ +static bool usbd_set_configuration(uint8_t config_index, uint8_t alt_setting) +{ + uint8_t *p = (uint8_t *)usbd_core_cfg.descriptors; + uint8_t cur_alt_setting = 0xFF; + uint8_t cur_config = 0xFF; + bool found = false; + + if (config_index == 0U) { + /* TODO: unconfigure device */ + USB_LOG_ERR("Device not configured - invalid configuration\r\n"); + return true; + } + + /* configure endpoints for this configuration/altsetting */ + while (p[DESC_bLength] != 0U) { + switch (p[DESC_bDescriptorType]) { + case USB_DESCRIPTOR_TYPE_CONFIGURATION: + /* remember current configuration index */ + cur_config = p[CONF_DESC_bConfigurationValue]; + + if (cur_config == config_index) { + found = true; + } + + break; + + case USB_DESCRIPTOR_TYPE_INTERFACE: + /* remember current alternate setting */ + cur_alt_setting = + p[INTF_DESC_bAlternateSetting]; + break; + + case USB_DESCRIPTOR_TYPE_ENDPOINT: + if ((cur_config != config_index) || + (cur_alt_setting != alt_setting)) { + break; + } + + found = usbd_set_endpoint((struct usb_endpoint_descriptor *)p); + break; + + default: + break; + } + + /* skip to next descriptor */ + p += p[DESC_bLength]; + } + + return found; +} + +/** + * @brief set USB interface + * + * @param [in] iface Interface index + * @param [in] alt_setting Alternate setting number + * + * @return true if successfully configured false if error or unconfigured + */ +static bool usbd_set_interface(uint8_t iface, uint8_t alt_setting) +{ + const uint8_t *p = usbd_core_cfg.descriptors; + const uint8_t *if_desc = NULL; + struct usb_endpoint_descriptor *ep_desc; + uint8_t cur_alt_setting = 0xFF; + uint8_t cur_iface = 0xFF; + bool ret = false; + + USB_LOG_DBG("iface %u alt_setting %u\r\n", iface, alt_setting); + + while (p[DESC_bLength] != 0U) { + switch (p[DESC_bDescriptorType]) { + case USB_DESCRIPTOR_TYPE_INTERFACE: + /* remember current alternate setting */ + cur_alt_setting = p[INTF_DESC_bAlternateSetting]; + cur_iface = p[INTF_DESC_bInterfaceNumber]; + + if (cur_iface == iface && + cur_alt_setting == alt_setting) { + if_desc = (void *)p; + } + + USB_LOG_DBG("Current iface %u alt setting %u", + cur_iface, cur_alt_setting); + break; + + case USB_DESCRIPTOR_TYPE_ENDPOINT: + if (cur_iface == iface) { + ep_desc = (struct usb_endpoint_descriptor *)p; + + if (cur_alt_setting != alt_setting) { + ret = usbd_reset_endpoint(ep_desc); + } else { + ret = usbd_set_endpoint(ep_desc); + } + } + + break; + + default: + break; + } + + /* skip to next descriptor */ + p += p[DESC_bLength]; + } + + usbd_event_notify_handler(USBD_EVENT_SET_INTERFACE, (void *)if_desc); + + return ret; +} + +/** + * @brief handle a standard device request + * + * @param [in] setup The setup packet + * @param [in,out] data Data buffer + * @param [in,out] len Pointer to data length + * + * @return true if the request was handled successfully + */ +static bool usbd_std_device_req_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) +{ + uint16_t value = setup->wValue; + // uint16_t index = setup->wIndex; + bool ret = true; + + switch (setup->bRequest) { + case USB_REQUEST_GET_STATUS: + USB_LOG_DBG("REQ_GET_STATUS\r\n"); + /* bit 0: self-powered */ + /* bit 1: remote wakeup */ + *data = (uint8_t *)&usbd_core_cfg.remote_wakeup; + + *len = 2; + break; + + case USB_REQUEST_CLEAR_FEATURE: + USB_LOG_DBG("REQ_CLEAR_FEATURE\r\n"); + ret = false; + + if (value == USB_FEATURE_REMOTE_WAKEUP) { + usbd_core_cfg.remote_wakeup = 0; + usbd_event_notify_handler(USBD_EVENT_CLEAR_REMOTE_WAKEUP, NULL); + ret = true; + } + + break; + + case USB_REQUEST_SET_FEATURE: + USB_LOG_DBG("REQ_SET_FEATURE\r\n"); + ret = false; + + if (value == USB_FEATURE_REMOTE_WAKEUP) { + usbd_core_cfg.remote_wakeup = 1; + usbd_event_notify_handler(USBD_EVENT_SET_REMOTE_WAKEUP, NULL); + ret = true; + } + + if (value == USB_FEATURE_TEST_MODE) { + /* put TEST_MODE code here */ + } + + break; + + case USB_REQUEST_SET_ADDRESS: + USB_LOG_DBG("REQ_SET_ADDRESS, addr 0x%x\r\n", value); + usbd_set_address(value); + break; + + case USB_REQUEST_GET_DESCRIPTOR: + USB_LOG_DBG("REQ_GET_DESCRIPTOR\r\n"); + ret = usbd_get_descriptor(value, data, len); + break; + + case USB_REQUEST_SET_DESCRIPTOR: + USB_LOG_DBG("Device req 0x%02x not implemented\r\n", setup->bRequest); + ret = false; + break; + + case USB_REQUEST_GET_CONFIGURATION: + USB_LOG_DBG("REQ_GET_CONFIGURATION\r\n"); + /* indicate if we are configured */ + *data = (uint8_t *)&usbd_core_cfg.configuration; + *len = 1; + break; + + case USB_REQUEST_SET_CONFIGURATION: + value &= 0xFF; + USB_LOG_DBG("REQ_SET_CONFIGURATION, conf 0x%x\r\n", value); + + if (!usbd_set_configuration(value, 0)) { + USB_LOG_DBG("USB Set Configuration failed\r\n"); + ret = false; + } else { + /* configuration successful, + * update current configuration + */ + usbd_core_cfg.configuration = value; + usbd_event_notify_handler(USBD_EVENT_CONFIGURED, NULL); + } + + break; + + case USB_REQUEST_GET_INTERFACE: + break; + + case USB_REQUEST_SET_INTERFACE: + break; + + default: + USB_LOG_ERR("Illegal device req 0x%02x\r\n", setup->bRequest); + ret = false; + break; + } + + return ret; +} + +/** + * @brief handle a standard interface request + * + * @param [in] setup The setup packet + * @param [in,out] data Data buffer + * @param [in,out] len Pointer to data length + * + * @return true if the request was handled successfully + */ +static bool usbd_std_interface_req_handler(struct usb_setup_packet *setup, + uint8_t **data, uint32_t *len) +{ + /** The device must be configured to accept standard interface + * requests and the addressed Interface must be valid. + */ + if (!is_device_configured() || + (!is_interface_valid((uint8_t)setup->wIndex))) { + return false; + } + + switch (setup->bRequest) { + case USB_REQUEST_GET_STATUS: + /* no bits specified */ + *data = (uint8_t *)&usbd_core_cfg.remote_wakeup; + + *len = 2; + break; + + case USB_REQUEST_CLEAR_FEATURE: + case USB_REQUEST_SET_FEATURE: + /* not defined for interface */ + return false; + + case USB_REQUEST_GET_INTERFACE: + /** This handler is called for classes that does not support + * alternate Interfaces so always return 0. Classes that + * support alternative interfaces handles GET_INTERFACE + * in custom_handler. + */ + *data = (uint8_t *)&usbd_core_cfg.reserved; + + *len = 1; + break; + + case USB_REQUEST_SET_INTERFACE: + USB_LOG_DBG("REQ_SET_INTERFACE\r\n"); + usbd_set_interface(setup->wIndex, setup->wValue); + break; + + default: + USB_LOG_ERR("Illegal interface req 0x%02x\r\n", setup->bRequest); + return false; + } + + return true; +} + +/** + * @brief handle a standard endpoint request + * + * @param [in] setup The setup packet + * @param [in,out] data Data buffer + * @param [in,out] len Pointer to data length + * + * @return true if the request was handled successfully + */ +static bool usbd_std_endpoint_req_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) +{ + uint8_t ep = (uint8_t)setup->wIndex; + + /* Check if request addresses valid Endpoint */ + if (!is_ep_valid(ep)) { + return false; + } + + switch (setup->bRequest) { + case USB_REQUEST_GET_STATUS: + + /** This request is valid for Control Endpoints when + * the device is not yet configured. For other + * Endpoints the device must be configured. + * Firstly check if addressed ep is Control Endpoint. + * If no then the device must be in Configured state + * to accept the request. + */ + if (((ep & 0x7f) == 0) || is_device_configured()) { + /* bit 0 - Endpoint halted or not */ + usbd_ep_is_stalled(ep, (uint8_t *)&usbd_core_cfg.remote_wakeup); + *data = (uint8_t *)&usbd_core_cfg.remote_wakeup; + + *len = 2; + break; + } + + return false; + + case USB_REQUEST_CLEAR_FEATURE: + if (setup->wValue == USB_FEATURE_ENDPOINT_HALT) { + /** This request is valid for Control Endpoints when + * the device is not yet configured. For other + * Endpoints the device must be configured. + * Firstly check if addressed ep is Control Endpoint. + * If no then the device must be in Configured state + * to accept the request. + */ + if (((ep & 0x7f) == 0) || is_device_configured()) { + USB_LOG_ERR("ep:%x clear halt\r\n", ep); + usbd_ep_clear_stall(ep); + usbd_event_notify_handler(USBD_EVENT_CLEAR_HALT, NULL); + break; + } + } + + /* only ENDPOINT_HALT defined for endpoints */ + return false; + + case USB_REQUEST_SET_FEATURE: + if (setup->wValue == USB_FEATURE_ENDPOINT_HALT) { + /** This request is valid for Control Endpoints when + * the device is not yet configured. For other + * Endpoints the device must be configured. + * Firstly check if addressed ep is Control Endpoint. + * If no then the device must be in Configured state + * to accept the request. + */ + if (((ep & 0x7f) == 0) || is_device_configured()) { + /* set HALT by stalling */ + USB_LOG_ERR("ep:%x set halt\r\n", ep); + usbd_ep_set_stall(ep); + usbd_event_notify_handler(USBD_EVENT_SET_HALT, NULL); + break; + } + } + + /* only ENDPOINT_HALT defined for endpoints */ + return false; + + case USB_REQUEST_SYNCH_FRAME: + + /* For Synch Frame request the device must be configured */ + if (is_device_configured()) { + /* Not supported, return false anyway */ + USB_LOG_DBG("ep req 0x%02x not implemented\r\n", setup->bRequest); + } + + return false; + + default: + USB_LOG_ERR("Illegal ep req 0x%02x\r\n", setup->bRequest); + return false; + } + + return true; +} + +/** + * @brief default handler for standard ('chapter 9') requests + * + * If a custom request handler was installed, this handler is called first. + * + * @param [in] setup The setup packet + * @param [in,out] data Data buffer + * @param [in,out] len Pointer to data length + * + * @return true if the request was handled successfully + */ +static int usbd_standard_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) +{ + int rc = 0; + + switch (setup->bmRequestType & USB_REQUEST_RECIPIENT_MASK) { + case USB_REQUEST_RECIPIENT_DEVICE: + if (usbd_std_device_req_handler(setup, data, len) == false) { + rc = -1; + } + + break; + + case USB_REQUEST_RECIPIENT_INTERFACE: + if (usbd_std_interface_req_handler(setup, data, len) == false) { + rc = -1; + } + + break; + + case USB_REQUEST_RECIPIENT_ENDPOINT: + if (usbd_std_endpoint_req_handler(setup, data, len) == false) { + rc = -1; + } + + break; + + default: + rc = -1; + } + + return rc; +} + +/** + * @brief handler for class requests + * + * If a custom request handler was installed, this handler is called first. + * + * @param [in] setup The setup packet + * @param [in,out] data Data buffer + * @param [in,out] len Pointer to data length + * + * @return true if the request was handled successfully + */ +static int usbd_class_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) +{ + USB_LOG_DBG("bRequest 0x%02x, wIndex 0x%04x\r\n", setup->bRequest, setup->wIndex); + + if ((setup->bmRequestType & USB_REQUEST_RECIPIENT_MASK) != USB_REQUEST_RECIPIENT_INTERFACE) { + return -1; + } + + usb_slist_t *i, *j; + usb_slist_for_each(i, &usbd_class_head) + { + usbd_class_t *devclass = usb_slist_entry(i, struct usbd_class, list); + + usb_slist_for_each(j, &devclass->intf_list) + { + usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list); + + if (intf->class_handler && (intf->intf_num == (setup->wIndex & 0xFF))) { + return intf->class_handler(setup, data, len); + } + } + } + return -1; +} + +/** + * @brief handler for vendor requests + * + * If a custom request handler was installed, this handler is called first. + * + * @param [in] setup The setup packet + * @param [in,out] data Data buffer + * @param [in,out] len Pointer to data length + * + * @return true if the request was handled successfully + */ +static int usbd_vendor_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) +{ + USB_LOG_DBG("bRequest 0x%02x, wValue0x%04x, wIndex 0x%04x\r\n", setup->bRequest, setup->wValue, setup->wIndex); + + // if((setup->bmRequestType & USB_REQUEST_RECIPIENT_MASK) != USB_REQUEST_RECIPIENT_DEVICE) + // { + // return -1; + // } + + if (msosv1_desc) { + if (setup->bRequest == msosv1_desc->vendor_code) { + switch (setup->wIndex) { + case 0x04: + USB_LOG_INFO("get Compat ID\r\n"); + *data = (uint8_t *)msosv1_desc->compat_id; + *len = msosv1_desc->compat_id_len; + + return 0; + case 0x05: + USB_LOG_INFO("get Compat id properties\r\n"); + *data = (uint8_t *)msosv1_desc->comp_id_property; + *len = msosv1_desc->comp_id_property_len; + + return 0; + default: + USB_LOG_ERR("unknown vendor code\r\n"); + return -1; + } + } + } else if (msosv2_desc) { + if (setup->bRequest == msosv2_desc->vendor_code) { + switch (setup->wIndex) { + case WINUSB_REQUEST_GET_DESCRIPTOR_SET: + USB_LOG_INFO("GET MS OS 2.0 Descriptor\r\n"); + *data = (uint8_t *)msosv2_desc->compat_id; + *len = msosv2_desc->compat_id_len; + return 0; + default: + USB_LOG_ERR("unknown vendor code\r\n"); + return -1; + } + } + } + + usb_slist_t *i, *j; + + usb_slist_for_each(i, &usbd_class_head) + { + usbd_class_t *devclass = usb_slist_entry(i, struct usbd_class, list); + + usb_slist_for_each(j, &devclass->intf_list) + { + usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list); + + if (intf->vendor_handler && !intf->vendor_handler(setup, data, len)) { + return 0; + } + } + } + + return -1; +} + +/** + * @brief handler for special requests + * + * If a custom request handler was installed, this handler is called first. + * + * @param [in] setup The setup packet + * @param [in,out] data Data buffer + * @param [in,out] len Pointer to data length + * + * @return true if the request was handled successfully + */ +static int usbd_custom_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) +{ + USB_LOG_DBG("bRequest 0x%02x, wIndex 0x%04x\r\n", setup->bRequest, setup->wIndex); + + if ((setup->bmRequestType & USB_REQUEST_RECIPIENT_MASK) != USB_REQUEST_RECIPIENT_INTERFACE) { + return -1; + } + + usb_slist_t *i, *j; + usb_slist_for_each(i, &usbd_class_head) + { + usbd_class_t *devclass = usb_slist_entry(i, struct usbd_class, list); + + usb_slist_for_each(j, &devclass->intf_list) + { + usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list); + + if (intf->custom_handler && (intf->intf_num == (setup->wIndex & 0xFF))) { + return intf->custom_handler(setup, data, len); + } + } + } + + return -1; +} + +/** + * @brief handle a request by calling one of the installed request handlers + * + * Local function to handle a request by calling one of the installed request + * handlers. In case of data going from host to device, the data is at *ppbData. + * In case of data going from device to host, the handler can either choose to + * write its data at *ppbData or update the data pointer. + * + * @param [in] setup The setup packet + * @param [in,out] data Data buffer + * @param [in,out] len Pointer to data length + * + * @return true if the request was handles successfully + */ +static bool usbd_setup_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) +{ + uint8_t type = setup->bmRequestType & USB_REQUEST_TYPE_MASK; + + if (!usbd_custom_request_handler(setup, data, len)) { + return true; + } + + if (type == USB_REQUEST_STANDARD) { + if (usbd_standard_request_handler(setup, data, len) < 0) { + USB_LOG_ERR("Handler Error %d\r\n", type); + usbd_print_setup(setup); + return false; + } + } else if (type == USB_REQUEST_CLASS) { + if (usbd_class_request_handler(setup, data, len) < 0) { + USB_LOG_ERR("Handler Error %d\r\n", type); + usbd_print_setup(setup); + return false; + } + } else if (type == USB_REQUEST_VENDOR) { + if (usbd_vendor_request_handler(setup, data, len) < 0) { + USB_LOG_ERR("Handler Error %d\r\n", type); + usbd_print_setup(setup); + return false; + } + } else { + return false; + } + + return true; +} +/** + * @brief send data or status to host + * + * @return N/A + */ +static void usbd_send_to_host(uint16_t len) +{ + uint32_t chunk = 0U; + + if (usbd_core_cfg.zlp_flag == false) { + chunk = usbd_core_cfg.ep0_data_buf_residue; + + if (usbd_ep_write(USB_CONTROL_IN_EP0, usbd_core_cfg.ep0_data_buf, usbd_core_cfg.ep0_data_buf_residue, &chunk) < 0) { + USB_LOG_ERR("USB write data failed\r\n"); + return; + } + + usbd_core_cfg.ep0_data_buf += chunk; + usbd_core_cfg.ep0_data_buf_residue -= chunk; + + /* + * Set ZLP flag when host asks for a bigger length and the + * last chunk is wMaxPacketSize long, to indicate the last + * packet. + */ + if ((!usbd_core_cfg.ep0_data_buf_residue) && (usbd_core_cfg.ep0_data_buf_len >= USB_CTRL_EP_MPS) && !(usbd_core_cfg.ep0_data_buf_len % USB_CTRL_EP_MPS)) { + /* Transfers a zero-length packet next*/ + usbd_core_cfg.zlp_flag = true; + } + } else { + usbd_core_cfg.zlp_flag = false; + USB_LOG_DBG("send zlp\r\n"); + if (usbd_ep_write(USB_CONTROL_IN_EP0, NULL, 0, NULL) < 0) { + USB_LOG_ERR("USB write zlp failed\r\n"); + return; + } + } +} + +static void usbd_ep0_setup_handler(void) +{ + struct usb_setup_packet *setup = &usbd_core_cfg.setup; + + /* + * OUT transfer, Setup packet, + * reset request message state machine + */ + if (usbd_ep_read(USB_CONTROL_OUT_EP0, (uint8_t *)setup, + sizeof(struct usb_setup_packet), NULL) < 0) { + USB_LOG_ERR("Read Setup Packet failed\r\n"); + usbd_ep_set_stall(USB_CONTROL_IN_EP0); + return; + } + + //usbd_print_setup(setup); + + if (setup->wLength > USB_REQUEST_BUFFER_SIZE) { + if ((setup->bmRequestType & USB_REQUEST_DIR_MASK) == USB_REQUEST_DIR_OUT) { + USB_LOG_ERR("Request buffer too small\r\n"); + usbd_ep_set_stall(USB_CONTROL_IN_EP0); + return; + } + } + + // usbd_core_cfg.ep0_data_buf = usbd_core_cfg.req_data; + usbd_core_cfg.ep0_data_buf_residue = setup->wLength; + usbd_core_cfg.ep0_data_buf_len = setup->wLength; + usbd_core_cfg.zlp_flag = false; + + /* this maybe set code in class request code */ + if (setup->wLength && + (setup->bmRequestType & USB_REQUEST_DIR_MASK) == USB_REQUEST_DIR_OUT) { + USB_LOG_DBG("prepare to out data\r\n"); + return; + } + + /* Ask installed handler to process request */ + if (!usbd_setup_request_handler(setup, &usbd_core_cfg.ep0_data_buf, &usbd_core_cfg.ep0_data_buf_len)) { + USB_LOG_ERR("usbd_setup_request_handler failed\r\n"); + usbd_ep_set_stall(USB_CONTROL_IN_EP0); + return; + } + + /* Send smallest of requested and offered length */ + usbd_core_cfg.ep0_data_buf_residue = MIN(usbd_core_cfg.ep0_data_buf_len, + setup->wLength); + /*Send data or status to host*/ + usbd_send_to_host(setup->wLength); +} + +static void usbd_ep0_out_handler(void) +{ + uint32_t chunk = 0U; + struct usb_setup_packet *setup = &usbd_core_cfg.setup; + + /* OUT transfer, status packets */ + if (usbd_core_cfg.ep0_data_buf_residue == 0) { + /* absorb zero-length status message */ + USB_LOG_DBG("recv status\r\n"); + + if (usbd_ep_read(USB_CONTROL_OUT_EP0, + NULL, + 0, NULL) < 0) { + USB_LOG_ERR("Read DATA Packet failed\r\n"); + usbd_ep_set_stall(USB_CONTROL_IN_EP0); + } + + return; + } + + usbd_core_cfg.ep0_data_buf = usbd_core_cfg.req_data; + + /* OUT transfer, data packets */ + if (usbd_ep_read(USB_CONTROL_OUT_EP0, + usbd_core_cfg.ep0_data_buf, + usbd_core_cfg.ep0_data_buf_residue, &chunk) < 0) { + USB_LOG_ERR("Read DATA Packet failed\r\n"); + usbd_ep_set_stall(USB_CONTROL_IN_EP0); + return; + } + + usbd_core_cfg.ep0_data_buf += chunk; + usbd_core_cfg.ep0_data_buf_residue -= chunk; + + if (usbd_core_cfg.ep0_data_buf_residue == 0) { + /* Received all, send data to handler */ + usbd_core_cfg.ep0_data_buf = usbd_core_cfg.req_data; + + if (!usbd_setup_request_handler(setup, &usbd_core_cfg.ep0_data_buf, &usbd_core_cfg.ep0_data_buf_len)) { + USB_LOG_ERR("usbd_setup_request_handler1 failed\r\n"); + usbd_ep_set_stall(USB_CONTROL_IN_EP0); + return; + } + + /*Send status to host*/ + usbd_send_to_host(setup->wLength); + } else { + USB_LOG_ERR("ep0_data_buf_residue is not zero\r\n"); + } +} + +static void usbd_ep0_in_handler(void) +{ + struct usb_setup_packet *setup = &usbd_core_cfg.setup; + + /* Send more data if available */ + if (usbd_core_cfg.ep0_data_buf_residue != 0 || usbd_core_cfg.zlp_flag == true) { + usbd_send_to_host(setup->wLength); + } else { + /*ep0 tx has completed,and no data to send,so do nothing*/ + } +} + +static void usbd_ep_out_handler(uint8_t ep) +{ +#if USBD_EP_CALLBACK_SEARCH_METHOD == USBD_EP_CALLBACK_LIST_SEARCH + usb_slist_t *i, *j, *k; + usb_slist_for_each(i, &usbd_class_head) + { + usbd_class_t *devclass = usb_slist_entry(i, struct usbd_class, list); + + usb_slist_for_each(j, &devclass->intf_list) + { + usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list); + + usb_slist_for_each(k, &intf->ep_list) + { + usbd_endpoint_t *ept = usb_slist_entry(k, struct usbd_endpoint, list); + + if ((ept->ep_addr == ep) && ept->ep_cb) { + ept->ep_cb(ep); + } + } + } + } +#else + + if (usbd_core_cfg.out_ep_cb[ep & 0x7f]) { + usbd_core_cfg.out_ep_cb[ep & 0x7f](ep); + } + +#endif +} + +static void usbd_ep_in_handler(uint8_t ep) +{ +#if USBD_EP_CALLBACK_SEARCH_METHOD == USBD_EP_CALLBACK_LIST_SEARCH + usb_slist_t *i, *j, *k; + usb_slist_for_each(i, &usbd_class_head) + { + usbd_class_t *devclass = usb_slist_entry(i, struct usbd_class, list); + + usb_slist_for_each(j, &devclass->intf_list) + { + usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list); + + usb_slist_for_each(k, &intf->ep_list) + { + usbd_endpoint_t *ept = usb_slist_entry(k, struct usbd_endpoint, list); + + if ((ept->ep_addr == ep) && ept->ep_cb) { + ept->ep_cb(ep); + } + } + } + } +#else + + if (usbd_core_cfg.in_ep_cb[ep & 0x7f]) { + usbd_core_cfg.in_ep_cb[ep & 0x7f](ep); + } + +#endif +} + +static void usbd_class_event_notify_handler(uint8_t event, void *arg) +{ + usb_slist_t *i, *j; + usb_slist_for_each(i, &usbd_class_head) + { + usbd_class_t *devclass = usb_slist_entry(i, struct usbd_class, list); + + usb_slist_for_each(j, &devclass->intf_list) + { + usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list); + + if (intf->notify_handler) { + intf->notify_handler(event, arg); + } + } + } +} + +void usbd_event_notify_handler(uint8_t event, void *arg) +{ + switch (event) { + case USBD_EVENT_RESET: + usbd_set_address(0); + usbd_core_cfg.configured = 0; + usbd_core_cfg.configuration = 0; + struct usbd_endpoint_cfg ep0_cfg; + ep0_cfg.ep_mps = USB_CTRL_EP_MPS; + ep0_cfg.ep_type = USBD_EP_TYPE_CTRL; + ep0_cfg.ep_addr = USB_CONTROL_IN_EP0; + /*set USB_CONTROL_IN_EP0 nak*/ + usbd_ep_open(&ep0_cfg); + + ep0_cfg.ep_addr = USB_CONTROL_OUT_EP0; + /*set USB_CONTROL_OUT_EP0 ack to prepare receiving setup data*/ + usbd_ep_open(&ep0_cfg); +#if USBD_EP_CALLBACK_SEARCH_METHOD == USBD_EP_CALLBACK_ARR_SEARCH + usbd_ep_callback_register(); +#endif + + case USBD_EVENT_ERROR: + case USBD_EVENT_SOF: + case USBD_EVENT_CONNECTED: + case USBD_EVENT_CONFIGURED: + case USBD_EVENT_SUSPEND: + case USBD_EVENT_DISCONNECTED: + case USBD_EVENT_RESUME: + case USBD_EVENT_SET_INTERFACE: + case USBD_EVENT_SET_REMOTE_WAKEUP: + case USBD_EVENT_CLEAR_REMOTE_WAKEUP: + case USBD_EVENT_SET_HALT: + case USBD_EVENT_CLEAR_HALT: + usbd_class_event_notify_handler(event, arg); + break; + + case USBD_EVENT_SETUP_NOTIFY: + usbd_ep0_setup_handler(); + break; + + case USBD_EVENT_EP0_IN_NOTIFY: + usbd_ep0_in_handler(); + break; + + case USBD_EVENT_EP0_OUT_NOTIFY: + usbd_ep0_out_handler(); + break; + + case USBD_EVENT_EP_IN_NOTIFY: + usbd_ep_in_handler((uint32_t)arg); + break; + + case USBD_EVENT_EP_OUT_NOTIFY: + usbd_ep_out_handler((uint32_t)arg); + break; + + default: + USB_LOG_ERR("USB unknown event: %d\r\n", event); + break; + } +} + +void usbd_desc_register(const uint8_t *desc) +{ + usbd_core_cfg.descriptors = desc; +} +/* Register MS OS Descriptors version 1 */ +void usbd_msosv1_desc_register(struct usb_msosv1_descriptor *desc) +{ + msosv1_desc = desc; +} + +/* Register MS OS Descriptors version 2 */ +void usbd_msosv2_desc_register(struct usb_msosv2_descriptor *desc) +{ + msosv2_desc = desc; +} + +void usbd_bos_desc_register(struct usb_bos_descriptor *desc) +{ + bos_desc = desc; +} + +void usbd_class_register(usbd_class_t *devclass) +{ + usb_slist_add_tail(&usbd_class_head, &devclass->list); + usb_slist_init(&devclass->intf_list); +} + +void usbd_class_add_interface(usbd_class_t *devclass, usbd_interface_t *intf) +{ + static uint8_t intf_offset = 0; + intf->intf_num = intf_offset; + usb_slist_add_tail(&devclass->intf_list, &intf->list); + usb_slist_init(&intf->ep_list); + intf_offset++; +} + +void usbd_interface_add_endpoint(usbd_interface_t *intf, usbd_endpoint_t *ep) +{ + usb_slist_add_tail(&intf->ep_list, &ep->list); +} + +bool usb_device_is_configured(void) +{ + return usbd_core_cfg.configured; +} diff --git a/core/usbd_core.h b/core/usbd_core.h index 0cc33179..8213008a 100644 --- a/core/usbd_core.h +++ b/core/usbd_core.h @@ -1,138 +1,138 @@ -/** - * @file usbd_core.h - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#ifndef _USBD_CORE_H -#define _USBD_CORE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "usb_util.h" -#include "usb_def.h" -#include "usb_dc.h" - -enum usbd_event_type { - /** USB error reported by the controller */ - USBD_EVENT_ERROR, - /** USB reset */ - USBD_EVENT_RESET, - /** Start of Frame received */ - USBD_EVENT_SOF, - /** USB connection established, hardware enumeration is completed */ - USBD_EVENT_CONNECTED, - /** USB configuration done */ - USBD_EVENT_CONFIGURED, - /** USB connection suspended by the HOST */ - USBD_EVENT_SUSPEND, - /** USB connection lost */ - USBD_EVENT_DISCONNECTED, - /** USB connection resumed by the HOST */ - USBD_EVENT_RESUME, - - /** USB interface selected */ - USBD_EVENT_SET_INTERFACE, - /** USB interface selected */ - USBD_EVENT_SET_REMOTE_WAKEUP, - /** USB interface selected */ - USBD_EVENT_CLEAR_REMOTE_WAKEUP, - /** Set Feature ENDPOINT_HALT received */ - USBD_EVENT_SET_HALT, - /** Clear Feature ENDPOINT_HALT received */ - USBD_EVENT_CLEAR_HALT, - /** setup packet received */ - USBD_EVENT_SETUP_NOTIFY, - /** ep0 in packet received */ - USBD_EVENT_EP0_IN_NOTIFY, - /** ep0 out packet received */ - USBD_EVENT_EP0_OUT_NOTIFY, - /** ep in packet except ep0 received */ - USBD_EVENT_EP_IN_NOTIFY, - /** ep out packet except ep0 received */ - USBD_EVENT_EP_OUT_NOTIFY, - /** Initial USB connection status */ - USBD_EVENT_UNKNOWN -}; - -/** - * @brief Callback function signature for the USB Endpoint status - */ -typedef void (*usbd_endpoint_callback)(uint8_t ep); - -/** - * @brief Callback function signature for class specific requests - * - * Function which handles Class specific requests corresponding to an - * interface number specified in the device descriptor table. For host - * to device direction the 'len' and 'payload_data' contain the length - * of the received data and the pointer to the received data respectively. - * For device to host class requests, 'len' and 'payload_data' should be - * set by the callback function with the length and the address of the - * data to be transmitted buffer respectively. - */ -typedef int (*usbd_request_handler)(struct usb_setup_packet *setup, - uint8_t **data, uint32_t *transfer_len); - -/* callback function pointer structure for Application to handle events */ -typedef void (*usbd_notify_handler)(uint8_t event, void *arg); - -typedef struct usbd_endpoint { - usb_slist_t list; - uint8_t ep_addr; - usbd_endpoint_callback ep_cb; -} usbd_endpoint_t; - -typedef struct usbd_interface { - usb_slist_t list; - /** Handler for USB Class specific commands*/ - usbd_request_handler class_handler; - /** Handler for USB Vendor specific commands */ - usbd_request_handler vendor_handler; - /** Handler for USB custom specific commands */ - usbd_request_handler custom_handler; - /** Handler for USB event notify commands */ - usbd_notify_handler notify_handler; - uint8_t intf_num; - usb_slist_t ep_list; -} usbd_interface_t; - -typedef struct usbd_class { - usb_slist_t list; - const char *name; - usb_slist_t intf_list; -} usbd_class_t; - -void usbd_event_notify_handler(uint8_t event, void *arg); - -void usbd_desc_register(const uint8_t *desc); -void usbd_class_register(usbd_class_t *devclass); -void usbd_msosv1_desc_register(struct usb_msosv1_descriptor *desc); -void usbd_msosv2_desc_register(struct usb_msosv2_descriptor *desc); -void usbd_bos_desc_register(struct usb_bos_descriptor *desc); -void usbd_class_add_interface(usbd_class_t *devclass, usbd_interface_t *intf); -void usbd_interface_add_endpoint(usbd_interface_t *intf, usbd_endpoint_t *ep); -bool usb_device_is_configured(void); - -#ifdef __cplusplus -} -#endif - -#endif +/** + * @file usbd_core.h + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _USBD_CORE_H +#define _USBD_CORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "usb_util.h" +#include "usb_def.h" +#include "usb_dc.h" + +enum usbd_event_type { + /** USB error reported by the controller */ + USBD_EVENT_ERROR, + /** USB reset */ + USBD_EVENT_RESET, + /** Start of Frame received */ + USBD_EVENT_SOF, + /** USB connection established, hardware enumeration is completed */ + USBD_EVENT_CONNECTED, + /** USB configuration done */ + USBD_EVENT_CONFIGURED, + /** USB connection suspended by the HOST */ + USBD_EVENT_SUSPEND, + /** USB connection lost */ + USBD_EVENT_DISCONNECTED, + /** USB connection resumed by the HOST */ + USBD_EVENT_RESUME, + + /** USB interface selected */ + USBD_EVENT_SET_INTERFACE, + /** USB interface selected */ + USBD_EVENT_SET_REMOTE_WAKEUP, + /** USB interface selected */ + USBD_EVENT_CLEAR_REMOTE_WAKEUP, + /** Set Feature ENDPOINT_HALT received */ + USBD_EVENT_SET_HALT, + /** Clear Feature ENDPOINT_HALT received */ + USBD_EVENT_CLEAR_HALT, + /** setup packet received */ + USBD_EVENT_SETUP_NOTIFY, + /** ep0 in packet received */ + USBD_EVENT_EP0_IN_NOTIFY, + /** ep0 out packet received */ + USBD_EVENT_EP0_OUT_NOTIFY, + /** ep in packet except ep0 received */ + USBD_EVENT_EP_IN_NOTIFY, + /** ep out packet except ep0 received */ + USBD_EVENT_EP_OUT_NOTIFY, + /** Initial USB connection status */ + USBD_EVENT_UNKNOWN +}; + +/** + * @brief Callback function signature for the USB Endpoint status + */ +typedef void (*usbd_endpoint_callback)(uint8_t ep); + +/** + * @brief Callback function signature for class specific requests + * + * Function which handles Class specific requests corresponding to an + * interface number specified in the device descriptor table. For host + * to device direction the 'len' and 'payload_data' contain the length + * of the received data and the pointer to the received data respectively. + * For device to host class requests, 'len' and 'payload_data' should be + * set by the callback function with the length and the address of the + * data to be transmitted buffer respectively. + */ +typedef int (*usbd_request_handler)(struct usb_setup_packet *setup, + uint8_t **data, uint32_t *transfer_len); + +/* callback function pointer structure for Application to handle events */ +typedef void (*usbd_notify_handler)(uint8_t event, void *arg); + +typedef struct usbd_endpoint { + usb_slist_t list; + uint8_t ep_addr; + usbd_endpoint_callback ep_cb; +} usbd_endpoint_t; + +typedef struct usbd_interface { + usb_slist_t list; + /** Handler for USB Class specific commands*/ + usbd_request_handler class_handler; + /** Handler for USB Vendor specific commands */ + usbd_request_handler vendor_handler; + /** Handler for USB custom specific commands */ + usbd_request_handler custom_handler; + /** Handler for USB event notify commands */ + usbd_notify_handler notify_handler; + uint8_t intf_num; + usb_slist_t ep_list; +} usbd_interface_t; + +typedef struct usbd_class { + usb_slist_t list; + const char *name; + usb_slist_t intf_list; +} usbd_class_t; + +void usbd_event_notify_handler(uint8_t event, void *arg); + +void usbd_desc_register(const uint8_t *desc); +void usbd_class_register(usbd_class_t *devclass); +void usbd_msosv1_desc_register(struct usb_msosv1_descriptor *desc); +void usbd_msosv2_desc_register(struct usb_msosv2_descriptor *desc); +void usbd_bos_desc_register(struct usb_bos_descriptor *desc); +void usbd_class_add_interface(usbd_class_t *devclass, usbd_interface_t *intf); +void usbd_interface_add_endpoint(usbd_interface_t *intf, usbd_endpoint_t *ep); +bool usb_device_is_configured(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/usbh_core.c b/core/usbh_core.c index 41def3bb..9e77739c 100644 --- a/core/usbh_core.c +++ b/core/usbh_core.c @@ -1,989 +1,989 @@ -/** - * @file usbh_core.c - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#include "usbh_core.h" -#include "usbh_cdc_acm.h" -#include "usbh_hid.h" -#include "usbh_msc.h" - -static const char *speed_table[] = { "error speed", "low speed", "full speed", "high speed" }; - -static const struct usbh_class_driver *usbh_find_class_driver(uint8_t class, uint8_t subcalss, uint8_t protocol, uint16_t vid, uint16_t pid); -void usbh_hport_activate(struct usbh_hubport *hport); -void usbh_hport_deactivate(struct usbh_hubport *hport); - -/* general descriptor field offsets */ -#define DESC_bLength 0 /** Length offset */ -#define DESC_bDescriptorType 1 /** Descriptor type offset */ - -#define USB_DEV_ADDR_MAX 0x7f -#define USB_DEV_ADDR_MARK_OFFSET 5 -#define USB_DEV_ADDR_MARK_MASK 0x1f - -struct usbh_devaddr_priv { - /** - * alloctab[0]:addr from 0~31 - * alloctab[1]:addr from 32~63 - * alloctab[2]:addr from 64~95 - * alloctab[3]:addr from 96~127 - * - */ - uint8_t next; /* Next device address */ - uint32_t alloctab[4]; /* Bit allocation table */ -}; - -struct usbh_roothubport_priv { - struct usbh_hubport hport; /* Common hub port definitions */ - struct usbh_devaddr_priv devgen; /* Address generation data */ -}; - -struct usbh_core_priv { - struct usbh_roothubport_priv rhport[CONFIG_USBHOST_RHPORTS]; - volatile struct usbh_hubport *active_hport; /* Used to pass external hub port events */ - volatile bool pscwait; /* TRUE: Thread is waiting for port status change event */ - usb_osal_sem_t pscsem; /* Semaphore to wait for a port event */ -} usbh_core_cfg; - -static inline struct usbh_roothubport_priv *usbh_find_roothub_port(struct usbh_hubport *hport) -{ - while (hport->parent != NULL) { - hport = hport->parent->parent; - } - return (struct usbh_roothubport_priv *)hport; -} - -static int usbh_allocate_devaddr(struct usbh_devaddr_priv *devgen) -{ - uint8_t startaddr = devgen->next; - uint8_t devaddr; - int index; - int bitno; - - /* Loop until we find a valid device address */ - - for (;;) { - /* Try the next device address */ - - devaddr = devgen->next; - if (devgen->next >= 0x7f) { - devgen->next = 1; - } else { - devgen->next++; - } - - /* Is this address already allocated? */ - - index = devaddr >> 5; - bitno = devaddr & 0x1f; - if ((devgen->alloctab[index] & (1 << bitno)) == 0) { - /* No... allocate it now */ - - devgen->alloctab[index] |= (1 << bitno); - return (int)devaddr; - } - - /* This address has already been allocated. The following logic will - * prevent (unexpected) infinite loops. - */ - - if (startaddr == devaddr) { - /* We are back where we started... the are no free device address */ - - return -ENOMEM; - } - } -} - -static int usbh_free_devaddr(struct usbh_devaddr_priv *devgen, uint8_t devaddr) -{ - int index; - int bitno; - - if ((devaddr > 0) && (devaddr < USB_DEV_ADDR_MAX)) { - index = devaddr >> USB_DEV_ADDR_MARK_OFFSET; - bitno = devaddr & USB_DEV_ADDR_MARK_MASK; - - /* Free the address by clearing the associated bit in the alloctab[]; */ - if ((devgen->alloctab[index] |= (1 << bitno)) != 0) { - devgen->alloctab[index] &= ~(1 << bitno); - } else { - return -1; - } - /* Reset the next pointer if the one just released has a lower value */ - - if (devaddr < devgen->next) { - devgen->next = devaddr; - } - } - - return 0; -} - -static int usbh_devaddr_create(struct usbh_hubport *hport) -{ - struct usbh_roothubport_priv *rhport; - rhport = usbh_find_roothub_port(hport); - - return usbh_allocate_devaddr(&rhport->devgen); -} - -static int usbh_devaddr_destroy(struct usbh_hubport *hport, uint8_t dev_addr) -{ - struct usbh_roothubport_priv *rhport; - rhport = usbh_find_roothub_port(hport); - - return usbh_free_devaddr(&rhport->devgen, dev_addr); -} - -static int parse_device_descriptor(struct usbh_hubport *hport, struct usb_device_descriptor *desc, uint16_t length) -{ - if (desc->bLength != USB_SIZEOF_DEVICE_DESC) { - USB_LOG_ERR("invalid device bLength 0x%02x\r\n", desc->bLength); - return -EINVAL; - } else if (desc->bDescriptorType != USB_DESCRIPTOR_TYPE_DEVICE) { - USB_LOG_ERR("unexpected descriptor 0x%02x\r\n", desc->bDescriptorType); - return -EINVAL; - } else { - if (length <= 8) { - return 0; - } -#if 0 - USB_LOG_DBG("Device Descriptor:\r\n"); - USB_LOG_DBG("bLength: 0x%02x \r\n", desc->bLength); - USB_LOG_DBG("bDescriptorType: 0x%02x \r\n", desc->bDescriptorType); - USB_LOG_DBG("bcdUSB: 0x%04x \r\n", desc->bcdUSB); - USB_LOG_DBG("bDeviceClass: 0x%02x \r\n", desc->bDeviceClass); - USB_LOG_DBG("bDeviceSubClass: 0x%02x \r\n", desc->bDeviceSubClass); - USB_LOG_DBG("bDeviceProtocol: 0x%02x \r\n", desc->bDeviceProtocol); - USB_LOG_DBG("bMaxPacketSize0: 0x%02x \r\n", desc->bMaxPacketSize0); - USB_LOG_DBG("idVendor: 0x%04x \r\n", desc->idVendor); - USB_LOG_DBG("idProduct: 0x%04x \r\n", desc->idProduct); - USB_LOG_DBG("bcdDevice: 0x%04x \r\n", desc->bcdDevice); - USB_LOG_DBG("iManufacturer: 0x%02x \r\n", desc->iManufacturer); - USB_LOG_DBG("iProduct: 0x%02x \r\n", desc->iProduct); - USB_LOG_DBG("iSerialNumber: 0x%02x \r\n", desc->iSerialNumber); - USB_LOG_DBG("bNumConfigurations: 0x%02x\r\n", desc->bNumConfigurations); -#endif - hport->device_desc.bLength = desc->bLength; - hport->device_desc.bDescriptorType = desc->bDescriptorType; - hport->device_desc.bcdUSB = desc->bcdUSB; - hport->device_desc.bDeviceClass = desc->bDeviceClass; - hport->device_desc.bDeviceSubClass = desc->bDeviceSubClass; - hport->device_desc.bDeviceProtocol = desc->bDeviceProtocol; - hport->device_desc.bMaxPacketSize0 = desc->bMaxPacketSize0; - hport->device_desc.idVendor = desc->idVendor; - hport->device_desc.idProduct = desc->idProduct; - hport->device_desc.bcdDevice = desc->bcdDevice; - hport->device_desc.iManufacturer = desc->iManufacturer; - hport->device_desc.iProduct = desc->iProduct; - hport->device_desc.iSerialNumber = desc->iSerialNumber; - hport->device_desc.bNumConfigurations = desc->bNumConfigurations; - } - return 0; -} - -static int parse_config_descriptor(struct usbh_hubport *hport, struct usb_configuration_descriptor *desc, uint16_t length) -{ - uint32_t total_len = 0; - uint8_t ep_num = 0; - uint8_t intf_num = 0; - uint8_t *p = (uint8_t *)desc; - if (desc->bLength != USB_SIZEOF_CONFIG_DESC) { - USB_LOG_ERR("invalid device bLength 0x%02x\r\n", desc->bLength); - return -EINVAL; - } else if (desc->bDescriptorType != USB_DESCRIPTOR_TYPE_CONFIGURATION) { - USB_LOG_ERR("unexpected descriptor 0x%02x\r\n", desc->bDescriptorType); - return -EINVAL; - } else { - if (length <= USB_SIZEOF_CONFIG_DESC) { - return 0; - } -#if 0 - USB_LOG_DBG("Config Descriptor:\r\n"); - USB_LOG_DBG("bLength: 0x%02x \r\n", desc->bLength); - USB_LOG_DBG("bDescriptorType: 0x%02x \r\n", desc->bDescriptorType); - USB_LOG_DBG("wTotalLength: 0x%04x \r\n", desc->wTotalLength); - USB_LOG_DBG("bNumInterfaces: 0x%02x \r\n", desc->bNumInterfaces); - USB_LOG_DBG("bConfigurationValue: 0x%02x \r\n", desc->bConfigurationValue); - USB_LOG_DBG("iConfiguration: 0x%02x \r\n", desc->iConfiguration); - USB_LOG_DBG("bmAttributes: 0x%02x \r\n", desc->bmAttributes); - USB_LOG_DBG("bMaxPower: 0x%02x \r\n", desc->bMaxPower); -#endif - hport->config.config_desc.bLength = desc->bLength; - hport->config.config_desc.bDescriptorType = desc->bDescriptorType; - hport->config.config_desc.wTotalLength = desc->wTotalLength; - hport->config.config_desc.bNumInterfaces = desc->bNumInterfaces; - hport->config.config_desc.bConfigurationValue = desc->bConfigurationValue; - hport->config.config_desc.iConfiguration = desc->iConfiguration; - hport->config.config_desc.iConfiguration = desc->iConfiguration; - hport->config.config_desc.bmAttributes = desc->bmAttributes; - hport->config.config_desc.bMaxPower = desc->bMaxPower; - - if (length > USB_SIZEOF_CONFIG_DESC) { - while (p[DESC_bLength] && (total_len < desc->wTotalLength) && (intf_num < desc->bNumInterfaces)) { - p += p[DESC_bLength]; - total_len += p[DESC_bLength]; - if (p[DESC_bDescriptorType] == USB_DESCRIPTOR_TYPE_INTERFACE) { - struct usb_interface_descriptor *intf_desc = (struct usb_interface_descriptor *)p; -#if 0 - USB_LOG_DBG("Interface Descriptor:\r\n"); - USB_LOG_DBG("bLength: 0x%02x \r\n", intf_desc->bLength); - USB_LOG_DBG("bDescriptorType: 0x%02x \r\n", intf_desc->bDescriptorType); - USB_LOG_DBG("bInterfaceNumber: 0x%02x \r\n", intf_desc->bInterfaceNumber); - USB_LOG_DBG("bAlternateSetting: 0x%02x \r\n", intf_desc->bAlternateSetting); - USB_LOG_DBG("bNumEndpoints: 0x%02x \r\n", intf_desc->bNumEndpoints); - USB_LOG_DBG("bInterfaceClass: 0x%02x \r\n", intf_desc->bInterfaceClass); - USB_LOG_DBG("bInterfaceSubClass: 0x%02x \r\n", intf_desc->bInterfaceSubClass); - USB_LOG_DBG("bInterfaceProtocol: 0x%02x \r\n", intf_desc->bInterfaceProtocol); - USB_LOG_DBG("iInterface: 0x%02x \r\n", intf_desc->iInterface); -#endif - memset(&hport->config.intf[intf_num], 0, sizeof(struct usbh_interface)); - - hport->config.intf[intf_num].intf_desc.bLength = intf_desc->bLength; - hport->config.intf[intf_num].intf_desc.bDescriptorType = intf_desc->bDescriptorType; - hport->config.intf[intf_num].intf_desc.bInterfaceNumber = intf_desc->bInterfaceNumber; - hport->config.intf[intf_num].intf_desc.bAlternateSetting = intf_desc->bAlternateSetting; - hport->config.intf[intf_num].intf_desc.bNumEndpoints = intf_desc->bNumEndpoints; - hport->config.intf[intf_num].intf_desc.bInterfaceClass = intf_desc->bInterfaceClass; - hport->config.intf[intf_num].intf_desc.bInterfaceSubClass = intf_desc->bInterfaceSubClass; - hport->config.intf[intf_num].intf_desc.bInterfaceProtocol = intf_desc->bInterfaceProtocol; - hport->config.intf[intf_num].intf_desc.iInterface = intf_desc->iInterface; - ep_num = 0; - while (p[DESC_bLength] && (total_len < desc->wTotalLength) && (ep_num < intf_desc->bNumEndpoints)) { - p += p[DESC_bLength]; - total_len += p[DESC_bLength]; - if (p[DESC_bDescriptorType] == USB_DESCRIPTOR_TYPE_ENDPOINT) { - struct usb_endpoint_descriptor *ep_desc = (struct usb_endpoint_descriptor *)p; -#if 0 - USB_LOG_DBG("Endpoint Descriptor:\r\n"); - USB_LOG_DBG("bLength: 0x%02x \r\n", ep_desc->bLength); - USB_LOG_DBG("bDescriptorType: 0x%02x \r\n", ep_desc->bDescriptorType); - USB_LOG_DBG("bEndpointAddress: 0x%02x \r\n", ep_desc->bEndpointAddress); - USB_LOG_DBG("bmAttributes: 0x%02x \r\n", ep_desc->bmAttributes); - USB_LOG_DBG("wMaxPacketSize: 0x%04x \r\n", ep_desc->wMaxPacketSize); - USB_LOG_DBG("bInterval: 0x%02x \r\n", ep_desc->bInterval); -#endif - memset(&hport->config.intf[intf_num].ep[ep_num], 0, sizeof(struct usbh_endpoint)); - - hport->config.intf[intf_num].ep[ep_num].ep_desc.bLength = ep_desc->bLength; - hport->config.intf[intf_num].ep[ep_num].ep_desc.bDescriptorType = ep_desc->bDescriptorType; - hport->config.intf[intf_num].ep[ep_num].ep_desc.bEndpointAddress = ep_desc->bEndpointAddress; - hport->config.intf[intf_num].ep[ep_num].ep_desc.bmAttributes = ep_desc->bmAttributes; - hport->config.intf[intf_num].ep[ep_num].ep_desc.wMaxPacketSize = ep_desc->wMaxPacketSize; - hport->config.intf[intf_num].ep[ep_num].ep_desc.bInterval = ep_desc->bInterval; - ep_num++; - } - } - intf_num++; - } - } - } - } - return 0; -} -#if 0 -static int parse_string_descriptor(struct usbh_hubport *hport, struct usb_string_descriptor *desc, uint8_t str_idx, uint16_t length) -{ - uint8_t string[64 + 1] = { 0 }; - uint8_t *p = (uint8_t *)desc; - - if (desc->bDescriptorType != USB_DESCRIPTOR_TYPE_STRING) { - USB_LOG_ERR("unexpected descriptor 0x%02x\r\n", desc->bDescriptorType); - return -2; - } else { - p += 2; - for (uint32_t i = 0; i < (desc->bLength - 2) / 2; i++) { - string[i] = *p; - p += 2; - } - USB_LOG_DBG("string:%s\r\n", string); - } - return 0; -} -#endif - -static void usbh_print_hubport_info(struct usbh_hubport *hport) -{ - printf("Device Descriptor:\r\n"); - printf("bLength: 0x%02x \r\n", hport->device_desc.bLength); - printf("bDescriptorType: 0x%02x \r\n", hport->device_desc.bDescriptorType); - printf("bcdUSB: 0x%04x \r\n", hport->device_desc.bcdUSB); - printf("bDeviceClass: 0x%02x \r\n", hport->device_desc.bDeviceClass); - printf("bDeviceSubClass: 0x%02x \r\n", hport->device_desc.bDeviceSubClass); - printf("bDeviceProtocol: 0x%02x \r\n", hport->device_desc.bDeviceProtocol); - printf("bMaxPacketSize0: 0x%02x \r\n", hport->device_desc.bMaxPacketSize0); - printf("idVendor: 0x%04x \r\n", hport->device_desc.idVendor); - printf("idProduct: 0x%04x \r\n", hport->device_desc.idProduct); - printf("bcdDevice: 0x%04x \r\n", hport->device_desc.bcdDevice); - printf("iManufacturer: 0x%02x \r\n", hport->device_desc.iManufacturer); - printf("iProduct: 0x%02x \r\n", hport->device_desc.iProduct); - printf("iSerialNumber: 0x%02x \r\n", hport->device_desc.iSerialNumber); - printf("bNumConfigurations: 0x%02x\r\n", hport->device_desc.bNumConfigurations); - - printf("Config Descriptor:\r\n"); - printf("bLength: 0x%02x \r\n", hport->config.config_desc.bLength); - printf("bDescriptorType: 0x%02x \r\n", hport->config.config_desc.bDescriptorType); - printf("wTotalLength: 0x%04x \r\n", hport->config.config_desc.wTotalLength); - printf("bNumInterfaces: 0x%02x \r\n", hport->config.config_desc.bNumInterfaces); - printf("bConfigurationValue: 0x%02x \r\n", hport->config.config_desc.bConfigurationValue); - printf("iConfiguration: 0x%02x \r\n", hport->config.config_desc.iConfiguration); - printf("bmAttributes: 0x%02x \r\n", hport->config.config_desc.bmAttributes); - printf("bMaxPower: 0x%02x \r\n", hport->config.config_desc.bMaxPower); - - for (uint8_t i = 0; i < hport->config.config_desc.bNumInterfaces; i++) { - printf("Interface Descriptor:\r\n"); - printf("bLength: 0x%02x \r\n", hport->config.intf[i].intf_desc.bLength); - printf("bDescriptorType: 0x%02x \r\n", hport->config.intf[i].intf_desc.bDescriptorType); - printf("bInterfaceNumber: 0x%02x \r\n", hport->config.intf[i].intf_desc.bInterfaceNumber); - printf("bAlternateSetting: 0x%02x \r\n", hport->config.intf[i].intf_desc.bAlternateSetting); - printf("bNumEndpoints: 0x%02x \r\n", hport->config.intf[i].intf_desc.bNumEndpoints); - printf("bInterfaceClass: 0x%02x \r\n", hport->config.intf[i].intf_desc.bInterfaceClass); - printf("bInterfaceSubClass: 0x%02x \r\n", hport->config.intf[i].intf_desc.bInterfaceSubClass); - printf("bInterfaceProtocol: 0x%02x \r\n", hport->config.intf[i].intf_desc.bInterfaceProtocol); - printf("iInterface: 0x%02x \r\n", hport->config.intf[i].intf_desc.iInterface); - - for (uint8_t j = 0; j < hport->config.intf[i].intf_desc.bNumEndpoints; j++) { - printf("Endpoint Descriptor:\r\n"); - printf("bLength: 0x%02x \r\n", hport->config.intf[i].ep[j].ep_desc.bLength); - printf("bDescriptorType: 0x%02x \r\n", hport->config.intf[i].ep[j].ep_desc.bDescriptorType); - printf("bEndpointAddress: 0x%02x \r\n", hport->config.intf[i].ep[j].ep_desc.bEndpointAddress); - printf("bmAttributes: 0x%02x \r\n", hport->config.intf[i].ep[j].ep_desc.bmAttributes); - printf("wMaxPacketSize: 0x%04x \r\n", hport->config.intf[i].ep[j].ep_desc.wMaxPacketSize); - printf("bInterval: 0x%02x \r\n", hport->config.intf[i].ep[j].ep_desc.bInterval); - } - } -} - -static int usbh_enumerate(struct usbh_hubport *hport) -{ - struct usb_interface_descriptor *intf_desc; - struct usb_setup_packet *setup; - uint8_t *ep0_buffer; - uint8_t descsize; - int dev_addr; - uint8_t ep_mps; - int ret; - -#define USB_REQUEST_BUFFER_SIZE 256 - /* Allocate buffer for setup and data buffer */ - setup = usb_iomalloc(sizeof(struct usb_setup_packet)); - if (setup == NULL) { - USB_LOG_ERR("Fail to alloc setup\r\n"); - return -ENOMEM; - } - - ep0_buffer = usb_iomalloc(USB_REQUEST_BUFFER_SIZE); - if (ep0_buffer == NULL) { - USB_LOG_ERR("Fail to alloc ep0_buffer\r\n"); - return -ENOMEM; - } - - /* Pick an appropriate packet size for this device - * - * USB 2.0, Paragraph 5.5.3 "Control Transfer Packet Size Constraints" - * - * "An endpoint for control transfers specifies the maximum data - * payload size that the endpoint can accept from or transmit to - * the bus. The allowable maximum control transfer data payload - * sizes for full-speed devices is 8, 16, 32, or 64 bytes; for - * high-speed devices, it is 64 bytes and for low-speed devices, - * it is 8 bytes. This maximum applies to the data payloads of the - * Data packets following a Setup..." - */ - - if (hport->speed == USB_SPEED_HIGH) { - /* For high-speed, we must use 64 bytes */ - ep_mps = 64; - descsize = USB_SIZEOF_DEVICE_DESC; - } else { - /* Eight will work for both low- and full-speed */ - ep_mps = 8; - descsize = 8; - } - - /* Configure EP0 with the initial maximum packet size */ - usbh_ep0_reconfigure(hport->ep0, 0, ep_mps, hport->speed); - - /* Read the first 8 bytes of the device descriptor */ - setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; - setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; - setup->wValue = (uint16_t)((USB_DESCRIPTOR_TYPE_DEVICE << 8) | 0); - setup->wIndex = 0; - setup->wLength = descsize; - - ret = usbh_control_transfer(hport->ep0, setup, ep0_buffer); - if (ret < 0) { - USB_LOG_ERR("Failed to get device descriptor,errorcode:%d\r\n", ret); - goto errout; - } - - parse_device_descriptor(hport, (struct usb_device_descriptor *)ep0_buffer, descsize); - - /* Extract the correct max packetsize from the device descriptor */ - ep_mps = ((struct usb_device_descriptor *)ep0_buffer)->bMaxPacketSize0; - - /* And reconfigure EP0 with the correct maximum packet size */ - usbh_ep0_reconfigure(hport->ep0, 0, ep_mps, hport->speed); - - /* Assign a function address to the device connected to this port */ - dev_addr = usbh_devaddr_create(hport); - if (dev_addr < 0) { - USB_LOG_ERR("Failed to allocate devaddr,errorcode:%d\r\n", ret); - goto errout; - } - - /* Set the USB device address */ - setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; - setup->bRequest = USB_REQUEST_SET_ADDRESS; - setup->wValue = dev_addr; - setup->wIndex = 0; - setup->wLength = 0; - - ret = usbh_control_transfer(hport->ep0, setup, NULL); - if (ret < 0) { - USB_LOG_ERR("Failed to set devaddr,errorcode:%d\r\n", ret); - goto errout; - } - - /* wait device address set completely */ - usb_osal_msleep(2); - - /* Assign the function address to the port */ - hport->dev_addr = dev_addr; - - /* And reconfigure EP0 with the correct address */ - usbh_ep0_reconfigure(hport->ep0, dev_addr, ep_mps, hport->speed); - - /* Read the full device descriptor if hport is not in high speed*/ - if (descsize < USB_SIZEOF_DEVICE_DESC) { - setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; - setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; - setup->wValue = (uint16_t)((USB_DESCRIPTOR_TYPE_DEVICE << 8) | 0); - setup->wIndex = 0; - setup->wLength = USB_SIZEOF_DEVICE_DESC; - - ret = usbh_control_transfer(hport->ep0, setup, ep0_buffer); - if (ret < 0) { - USB_LOG_ERR("Failed to get full device descriptor,errorcode:%d\r\n", ret); - goto errout; - } - - parse_device_descriptor(hport, (struct usb_device_descriptor *)ep0_buffer, USB_SIZEOF_DEVICE_DESC); - } - - /* Read the first 9 bytes of the config descriptor */ - setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; - setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; - setup->wValue = (uint16_t)((USB_DESCRIPTOR_TYPE_CONFIGURATION << 8) | 0); - setup->wIndex = 0; - setup->wLength = USB_SIZEOF_CONFIG_DESC; - - ret = usbh_control_transfer(hport->ep0, setup, ep0_buffer); - if (ret < 0) { - USB_LOG_ERR("Failed to get config descriptor,errorcode:%d\r\n", ret); - goto errout; - } - - parse_config_descriptor(hport, (struct usb_configuration_descriptor *)ep0_buffer, USB_SIZEOF_CONFIG_DESC); - - /* Read the full size of the configuration data */ - uint16_t wTotalLength = ((struct usb_configuration_descriptor *)ep0_buffer)->wTotalLength; - - setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; - setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; - setup->wValue = (uint16_t)((USB_DESCRIPTOR_TYPE_CONFIGURATION << 8) | 0); - setup->wIndex = 0; - setup->wLength = wTotalLength; - - ret = usbh_control_transfer(hport->ep0, setup, ep0_buffer); - if (ret < 0) { - USB_LOG_ERR("Failed to get full config descriptor,errorcode:%d\r\n", ret); - goto errout; - } - - parse_config_descriptor(hport, (struct usb_configuration_descriptor *)ep0_buffer, wTotalLength); - - /* Select device configuration 1 */ - setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; - setup->bRequest = USB_REQUEST_SET_CONFIGURATION; - setup->wValue = 1; - setup->wIndex = 0; - setup->wLength = 0; - - ret = usbh_control_transfer(hport->ep0, setup, NULL); - if (ret < 0) { - USB_LOG_ERR("Failed to set configuration,errorcode:%d\r\n", ret); - goto errout; - } - - /*search supported class driver*/ - for (uint8_t i = 0; i < hport->config.config_desc.bNumInterfaces; i++) { - intf_desc = &hport->config.intf[i].intf_desc; - - struct usbh_class_driver *class_driver = (struct usbh_class_driver *)usbh_find_class_driver(intf_desc->bInterfaceClass, intf_desc->bInterfaceSubClass, intf_desc->bInterfaceProtocol, hport->device_desc.idVendor, hport->device_desc.idProduct); - - if (class_driver == NULL) { - USB_LOG_ERR("do not support Class:0x%02x,Subclass:0x%02x,Protocl:0x%02x\r\n", - intf_desc->bInterfaceClass, - intf_desc->bInterfaceSubClass, - intf_desc->bInterfaceProtocol); - - continue; - } - hport->config.intf[i].class_driver = class_driver; - - if (hport->config.intf[i].class_driver->connect) { - ret = CLASS_CONNECT(hport, i); - if (ret < 0) { - ret = CLASS_DISCONNECT(hport, i); - goto errout; - } - } - } - -errout: - if (ret < 0) { - usbh_hport_deactivate(hport); - } - - if (setup) { - usb_iofree(setup); - } - if (ep0_buffer) { - usb_iofree(ep0_buffer); - } - - return ret; -} - -static int usbh_portchange_wait(struct usbh_hubport **hport) -{ - struct usbh_hubport *connport = NULL; - uint32_t flags; - int ret; - - /* Loop until a change in connection state is detected */ - while (1) { - /* Check for a change in the connection state on any root hub port */ - flags = usb_osal_enter_critical_section(); - for (uint8_t port = USBH_HUB_PORT_START_INDEX; port <= CONFIG_USBHOST_RHPORTS; port++) { - connport = &usbh_core_cfg.rhport[port - 1].hport; - - if (connport->port_change) { - connport->port_change = false; - *hport = connport; - usb_osal_leave_critical_section(flags); - return 0; - } - } - /* Is a device connected to an external hub? */ - if (usbh_core_cfg.active_hport) { - connport = (struct usbh_hubport *)usbh_core_cfg.active_hport; - usbh_core_cfg.active_hport = NULL; - *hport = connport; - usb_osal_leave_critical_section(flags); - return 0; - } - /* No changes on any port. Wait for a connection/disconnection event and check again */ - usbh_core_cfg.pscwait = true; - usb_osal_leave_critical_section(flags); - ret = usb_osal_sem_take(usbh_core_cfg.pscsem); - if (ret < 0) { - return ret; - } - } -} - -static void usbh_portchange_detect_thread(void *argument) -{ - struct usbh_hubport *hport = NULL; - uint32_t flags; - - flags = usb_osal_enter_critical_section(); - usb_hc_init(); - - for (uint8_t port = USBH_HUB_PORT_START_INDEX; port <= CONFIG_USBHOST_RHPORTS; port++) { - usbh_core_cfg.rhport[port - 1].hport.port = port; - usbh_core_cfg.rhport[port - 1].devgen.next = 1; - usbh_hport_activate(&usbh_core_cfg.rhport[port - 1].hport); - } - - usb_osal_leave_critical_section(flags); - - while (1) { - usbh_portchange_wait(&hport); - if (hport->connected) { - /*if roothub port,reset port first*/ - if (ROOTHUB(hport)) { - /* Reset the host port */ - usbh_reset_port(hport->port); - usb_osal_msleep(200); - /* Get the current device speed */ - hport->speed = usbh_get_port_speed(hport->port); - USB_LOG_INFO("Bus %u, Port %u connected, %s\r\n", 1, hport->port, speed_table[hport->speed]); - } else { - USB_LOG_INFO("Bus %u, Port %u connected, %s\r\n", hport->parent->index, hport->port, speed_table[hport->speed]); - } - usb_osal_thread_suspend(g_lpworkq.thread); - usbh_enumerate(hport); - usb_osal_thread_resume(g_lpworkq.thread); - } else { - usbh_hport_deactivate(hport); - for (uint8_t i = 0; i < hport->config.config_desc.bNumInterfaces; i++) { - if (hport->config.intf[i].class_driver && hport->config.intf[i].class_driver->disconnect) { - CLASS_DISCONNECT(hport, i); - } - } - - hport->config.config_desc.bNumInterfaces = 0; - - if (ROOTHUB(hport)) { - USB_LOG_INFO("Bus %u,Port:%u disconnected\r\n", 1, hport->port); - } else { - USB_LOG_INFO("Bus %u,Port:%u disconnected\r\n", hport->parent->index, hport->port); - } - } - } -} - -void usbh_external_hport_connect(struct usbh_hubport *hport) -{ - uint32_t flags; - - usbh_hport_activate(hport); - - flags = usb_osal_enter_critical_section(); - - hport->connected = true; - usbh_core_cfg.active_hport = hport; - - if (usbh_core_cfg.pscwait) { - usbh_core_cfg.pscwait = false; - usb_osal_sem_give(usbh_core_cfg.pscsem); - } - - usb_osal_leave_critical_section(flags); -} - -void usbh_external_hport_disconnect(struct usbh_hubport *hport) -{ - uint32_t flags; - - flags = usb_osal_enter_critical_section(); - hport->connected = false; - usbh_core_cfg.active_hport = hport; - - if (usbh_core_cfg.pscwait) { - usbh_core_cfg.pscwait = false; - usb_osal_sem_give(usbh_core_cfg.pscsem); - } - - usb_osal_leave_critical_section(flags); -} - -void usbh_hport_activate(struct usbh_hubport *hport) -{ - struct usbh_endpoint_cfg ep0_cfg; - uint32_t flags; - - flags = usb_osal_enter_critical_section(); - memset(&ep0_cfg, 0, sizeof(struct usbh_endpoint_cfg)); - - ep0_cfg.ep_addr = 0x00; - ep0_cfg.ep_interval = 0x00; - ep0_cfg.ep_mps = 0x08; - ep0_cfg.ep_type = USB_ENDPOINT_TYPE_CONTROL; - ep0_cfg.hport = hport; - /* Allocate memory for roothub port control endpoint */ - usbh_ep_alloc(&hport->ep0, &ep0_cfg); - - usb_osal_leave_critical_section(flags); -} - -void usbh_hport_deactivate(struct usbh_hubport *hport) -{ - uint32_t flags; - - flags = usb_osal_enter_critical_section(); - /* Don't free the control pipe of root hub ports! */ - if (hport->parent != NULL && hport->ep0 != NULL) { - usb_ep_cancel(hport->ep0); - usbh_ep_free(hport->ep0); - hport->ep0 = NULL; - } - /* Free the device address if one has been assigned */ - usbh_devaddr_destroy(hport, hport->dev_addr); - hport->dev_addr = 0; - - usb_osal_leave_critical_section(flags); -} - -void usbh_event_notify_handler(uint8_t event, uint8_t rhport) -{ - switch (event) { - case USBH_EVENT_ATTACHED: - if (!usbh_core_cfg.rhport[rhport - 1].hport.connected) { - usbh_core_cfg.rhport[rhport - 1].hport.connected = true; - usbh_core_cfg.rhport[rhport - 1].hport.port_change = true; - if (usbh_core_cfg.pscwait) { - usbh_core_cfg.pscwait = false; - usb_osal_sem_give(usbh_core_cfg.pscsem); - } - } - break; - case USBH_EVENT_REMOVED: - if (usbh_core_cfg.rhport[rhport - 1].hport.connected) { - usbh_core_cfg.rhport[rhport - 1].hport.connected = false; - usbh_core_cfg.rhport[rhport - 1].hport.port_change = true; - if (usbh_core_cfg.pscwait) { - usbh_core_cfg.pscwait = false; - usb_osal_sem_give(usbh_core_cfg.pscsem); - } - } - break; - default: - break; - } -} - -int usbh_initialize(void) -{ - usb_osal_thread_t usb_thread; - - memset(&usbh_core_cfg, 0, sizeof(struct usbh_core_priv)); - - usbh_workq_initialize(); - - usbh_core_cfg.pscsem = usb_osal_sem_create(0); - if (usbh_core_cfg.pscsem == NULL) { - return -1; - } - - usb_thread = usb_osal_thread_create("usbh_psc", CONFIG_USBHOST_PSC_STACKSIZE, CONFIG_USBHOST_PSC_PRIO, usbh_portchange_detect_thread, NULL); - if (usb_thread == NULL) { - return -1; - } - - return 0; -} - -int lsusb(int argc, char **argv) -{ - usb_slist_t *hub_list; - uint8_t port; - - if (argc < 2) { - printf("Usage: lsusb [options]...\r\n"); - printf("List USB devices\r\n"); - printf(" -v, --verbose\r\n"); - printf(" Increase verbosity (show descriptors)\r\n"); - printf(" -s [[bus]:[devnum]]\r\n"); - printf(" Show only devices with specified device and/or bus numbers (in decimal)\r\n"); - printf(" -d vendor:[product]\r\n"); - printf(" Show only devices with the specified vendor and product ID numbers (in hexadecimal)\r\n"); - printf(" -t, --tree\r\n"); - printf(" Dump the physical USB device hierachy as a tree\r\n"); - printf(" -V, --version\r\n"); - printf(" Show version of program\r\n"); - printf(" -h, --help\r\n"); - printf(" Show usage and help\r\n"); - return 0; - } - - if (argc > 3) { - return 0; - } - - if (strcmp(argv[1], "-t") == 0) { - for (port = USBH_HUB_PORT_START_INDEX; port <= CONFIG_USBHOST_RHPORTS; port++) { - if (usbh_core_cfg.rhport[port - 1].hport.connected) { - printf("/: Bus %02u,VID:PID 0x%04x:0x%04x\r\n", USBH_ROOT_HUB_INDEX, usbh_core_cfg.rhport[port - 1].hport.device_desc.idVendor, usbh_core_cfg.rhport[port - 1].hport.device_desc.idProduct); - - for (uint8_t i = 0; i < usbh_core_cfg.rhport[port - 1].hport.config.config_desc.bNumInterfaces; i++) { - if (usbh_core_cfg.rhport[port - 1].hport.config.intf[i].class_driver->driver_name) { - printf(" |__Port %u,Port addr:0x%02x,If %u,ClassDriver=%s\r\n", usbh_core_cfg.rhport[port - 1].hport.port, usbh_core_cfg.rhport[port - 1].hport.dev_addr, - i, usbh_core_cfg.rhport[port - 1].hport.config.intf[i].class_driver->driver_name); - } - } - } - } - usb_slist_for_each(hub_list, &hub_class_head) - { - usbh_hub_t *hub_class = usb_slist_entry(hub_list, struct usbh_hub, list); - - for (port = USBH_HUB_PORT_START_INDEX; port <= hub_class->nports; port++) { - if (hub_class->child[port - 1].connected) { - printf("/: Bus %02u,VID:PID 0x%04x:0x%04x\r\n", hub_class->index, hub_class->child[port - 1].device_desc.idVendor, hub_class->child[port - 1].device_desc.idProduct); - - for (uint8_t i = 0; i < hub_class->child[port - 1].config.config_desc.bNumInterfaces; i++) { - if (hub_class->child[port - 1].config.intf[i].class_driver->driver_name) { - printf(" |__Port %u,Port addr:0x%02x,If %u,ClassDriver=%s\r\n", hub_class->child[port - 1].port, hub_class->child[port - 1].dev_addr, - i, hub_class->child[port - 1].config.intf[i].class_driver->driver_name); - } - } - } - } - } - } else if (strcmp(argv[1], "-v") == 0) { - for (port = USBH_HUB_PORT_START_INDEX; port <= CONFIG_USBHOST_RHPORTS; port++) { - if (usbh_core_cfg.rhport[port - 1].hport.connected) { - printf("Bus %02u,Port %u,Port addr:0x%02x,VID:PID 0x%04x:0x%04x\r\n", USBH_ROOT_HUB_INDEX, usbh_core_cfg.rhport[port - 1].hport.port, usbh_core_cfg.rhport[port - 1].hport.dev_addr, - usbh_core_cfg.rhport[port - 1].hport.device_desc.idVendor, usbh_core_cfg.rhport[port - 1].hport.device_desc.idProduct); - usbh_print_hubport_info(&usbh_core_cfg.rhport[port - 1].hport); - } - } - - usb_slist_for_each(hub_list, &hub_class_head) - { - usbh_hub_t *hub_class = usb_slist_entry(hub_list, struct usbh_hub, list); - - for (port = USBH_HUB_PORT_START_INDEX; port <= hub_class->nports; port++) { - if (hub_class->child[port - 1].connected) { - printf("Bus %02u,Port %u,Port addr:0x%02x,VID:PID 0x%04x:0x%04x\r\n", hub_class->index, hub_class->child[port - 1].port, hub_class->child[port - 1].dev_addr, - hub_class->child[port - 1].device_desc.idVendor, hub_class->child[port - 1].device_desc.idProduct); - usbh_print_hubport_info(&hub_class->child[port - 1]); - } - } - } - } - - return 0; -} - -struct usbh_hubport *usbh_get_hubport(uint8_t dev_addr) -{ - usb_slist_t *hub_list; - uint8_t port; - - for (port = USBH_HUB_PORT_START_INDEX; port <= CONFIG_USBHOST_RHPORTS; port++) { - if (usbh_core_cfg.rhport[port - 1].hport.connected) { - if (usbh_core_cfg.rhport[port - 1].hport.dev_addr == dev_addr) { - return &usbh_core_cfg.rhport[port - 1].hport; - } - } - } - usb_slist_for_each(hub_list, &hub_class_head) - { - usbh_hub_t *hub_class = usb_slist_entry(hub_list, struct usbh_hub, list); - - for (port = USBH_HUB_PORT_START_INDEX; port <= hub_class->nports; port++) { - if (hub_class->child[port - 1].connected) { - if (hub_class->child[port - 1].dev_addr == dev_addr) { - return &hub_class->child[port - 1]; - } - } - } - } - return NULL; -} - -void *usbh_get_class(uint8_t dev_addr, uint8_t intf) -{ - usb_slist_t *hub_list; - uint8_t port; - - for (port = USBH_HUB_PORT_START_INDEX; port <= CONFIG_USBHOST_RHPORTS; port++) { - if (usbh_core_cfg.rhport[port - 1].hport.connected) { - if (usbh_core_cfg.rhport[port - 1].hport.dev_addr == dev_addr) { - if (usbh_core_cfg.rhport[port - 1].hport.config.intf[intf].priv) { - return usbh_core_cfg.rhport[port - 1].hport.config.intf[intf].priv; - } - } - } - } - usb_slist_for_each(hub_list, &hub_class_head) - { - usbh_hub_t *hub_class = usb_slist_entry(hub_list, struct usbh_hub, list); - - for (port = USBH_HUB_PORT_START_INDEX; port <= hub_class->nports; port++) { - if (hub_class->child[port - 1].connected) { - if (hub_class->child[port - 1].dev_addr == dev_addr) { - if (hub_class->child[port - 1].config.intf[intf].priv) { - return hub_class->child[port - 1].config.intf[intf].priv; - } - } - } - } - } - return NULL; -} - -const struct usbh_class_info class_info_table[] = { - - { .class = USB_DEVICE_CLASS_CDC, - .subclass = CDC_ABSTRACT_CONTROL_MODEL, - .protocol = CDC_COMMON_PROTOCOL_AT_COMMANDS, - .vid = 0x00, - .pid = 0x00, - .class_driver = &cdc_acm_class_driver }, - { .class = USB_DEVICE_CLASS_HID, - .subclass = HID_SUBCLASS_BOOTIF, - .protocol = HID_PROTOCOL_KEYBOARD, - .vid = 0x00, - .pid = 0x00, - .class_driver = &hid_class_driver }, - { .class = USB_DEVICE_CLASS_HID, - .subclass = HID_SUBCLASS_BOOTIF, - .protocol = HID_PROTOCOL_MOUSE, - .vid = 0x00, - .pid = 0x00, - .class_driver = &hid_class_driver }, - { .class = USB_DEVICE_CLASS_MASS_STORAGE, - .subclass = MSC_SUBCLASS_SCSI, - .protocol = MSC_PROTOCOL_BULK_ONLY, - .vid = 0x00, - .pid = 0x00, - .class_driver = &msc_class_driver }, -#ifdef CONFIG_USBHOST_HUB - { .class = USB_DEVICE_CLASS_HUB, - .subclass = 0, - .protocol = 0, - .vid = 0x00, - .pid = 0x00, - .class_driver = &hub_class_driver }, - { .class = USB_DEVICE_CLASS_HUB, - .subclass = 0, - .protocol = 1, - .vid = 0x00, - .pid = 0x00, - .class_driver = &hub_class_driver }, -#endif -}; - -static const struct usbh_class_driver *usbh_find_class_driver(uint8_t class, uint8_t subcalss, uint8_t protocol, uint16_t vid, uint16_t pid) -{ - for (uint8_t i = 0; i < sizeof(class_info_table) / sizeof(class_info_table[0]); i++) { - if (class == class_info_table[i].class && - subcalss == class_info_table[i].subclass && - protocol == class_info_table[i].protocol) { - /* If this is a vendor-specific class ID, then the VID and PID have to match as well. */ - if (class == USB_DEVICE_CLASS_VEND_SPECIFIC) { - if (vid == class_info_table[i].vid && - pid == class_info_table[i].pid) { - return class_info_table[i].class_driver; - } - } - return class_info_table[i].class_driver; - } - } - - return NULL; -} +/** + * @file usbh_core.c + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#include "usbh_core.h" +#include "usbh_cdc_acm.h" +#include "usbh_hid.h" +#include "usbh_msc.h" + +static const char *speed_table[] = { "error speed", "low speed", "full speed", "high speed" }; + +static const struct usbh_class_driver *usbh_find_class_driver(uint8_t class, uint8_t subcalss, uint8_t protocol, uint16_t vid, uint16_t pid); +void usbh_hport_activate(struct usbh_hubport *hport); +void usbh_hport_deactivate(struct usbh_hubport *hport); + +/* general descriptor field offsets */ +#define DESC_bLength 0 /** Length offset */ +#define DESC_bDescriptorType 1 /** Descriptor type offset */ + +#define USB_DEV_ADDR_MAX 0x7f +#define USB_DEV_ADDR_MARK_OFFSET 5 +#define USB_DEV_ADDR_MARK_MASK 0x1f + +struct usbh_devaddr_priv { + /** + * alloctab[0]:addr from 0~31 + * alloctab[1]:addr from 32~63 + * alloctab[2]:addr from 64~95 + * alloctab[3]:addr from 96~127 + * + */ + uint8_t next; /* Next device address */ + uint32_t alloctab[4]; /* Bit allocation table */ +}; + +struct usbh_roothubport_priv { + struct usbh_hubport hport; /* Common hub port definitions */ + struct usbh_devaddr_priv devgen; /* Address generation data */ +}; + +struct usbh_core_priv { + struct usbh_roothubport_priv rhport[CONFIG_USBHOST_RHPORTS]; + volatile struct usbh_hubport *active_hport; /* Used to pass external hub port events */ + volatile bool pscwait; /* TRUE: Thread is waiting for port status change event */ + usb_osal_sem_t pscsem; /* Semaphore to wait for a port event */ +} usbh_core_cfg; + +static inline struct usbh_roothubport_priv *usbh_find_roothub_port(struct usbh_hubport *hport) +{ + while (hport->parent != NULL) { + hport = hport->parent->parent; + } + return (struct usbh_roothubport_priv *)hport; +} + +static int usbh_allocate_devaddr(struct usbh_devaddr_priv *devgen) +{ + uint8_t startaddr = devgen->next; + uint8_t devaddr; + int index; + int bitno; + + /* Loop until we find a valid device address */ + + for (;;) { + /* Try the next device address */ + + devaddr = devgen->next; + if (devgen->next >= 0x7f) { + devgen->next = 1; + } else { + devgen->next++; + } + + /* Is this address already allocated? */ + + index = devaddr >> 5; + bitno = devaddr & 0x1f; + if ((devgen->alloctab[index] & (1 << bitno)) == 0) { + /* No... allocate it now */ + + devgen->alloctab[index] |= (1 << bitno); + return (int)devaddr; + } + + /* This address has already been allocated. The following logic will + * prevent (unexpected) infinite loops. + */ + + if (startaddr == devaddr) { + /* We are back where we started... the are no free device address */ + + return -ENOMEM; + } + } +} + +static int usbh_free_devaddr(struct usbh_devaddr_priv *devgen, uint8_t devaddr) +{ + int index; + int bitno; + + if ((devaddr > 0) && (devaddr < USB_DEV_ADDR_MAX)) { + index = devaddr >> USB_DEV_ADDR_MARK_OFFSET; + bitno = devaddr & USB_DEV_ADDR_MARK_MASK; + + /* Free the address by clearing the associated bit in the alloctab[]; */ + if ((devgen->alloctab[index] |= (1 << bitno)) != 0) { + devgen->alloctab[index] &= ~(1 << bitno); + } else { + return -1; + } + /* Reset the next pointer if the one just released has a lower value */ + + if (devaddr < devgen->next) { + devgen->next = devaddr; + } + } + + return 0; +} + +static int usbh_devaddr_create(struct usbh_hubport *hport) +{ + struct usbh_roothubport_priv *rhport; + rhport = usbh_find_roothub_port(hport); + + return usbh_allocate_devaddr(&rhport->devgen); +} + +static int usbh_devaddr_destroy(struct usbh_hubport *hport, uint8_t dev_addr) +{ + struct usbh_roothubport_priv *rhport; + rhport = usbh_find_roothub_port(hport); + + return usbh_free_devaddr(&rhport->devgen, dev_addr); +} + +static int parse_device_descriptor(struct usbh_hubport *hport, struct usb_device_descriptor *desc, uint16_t length) +{ + if (desc->bLength != USB_SIZEOF_DEVICE_DESC) { + USB_LOG_ERR("invalid device bLength 0x%02x\r\n", desc->bLength); + return -EINVAL; + } else if (desc->bDescriptorType != USB_DESCRIPTOR_TYPE_DEVICE) { + USB_LOG_ERR("unexpected descriptor 0x%02x\r\n", desc->bDescriptorType); + return -EINVAL; + } else { + if (length <= 8) { + return 0; + } +#if 0 + USB_LOG_DBG("Device Descriptor:\r\n"); + USB_LOG_DBG("bLength: 0x%02x \r\n", desc->bLength); + USB_LOG_DBG("bDescriptorType: 0x%02x \r\n", desc->bDescriptorType); + USB_LOG_DBG("bcdUSB: 0x%04x \r\n", desc->bcdUSB); + USB_LOG_DBG("bDeviceClass: 0x%02x \r\n", desc->bDeviceClass); + USB_LOG_DBG("bDeviceSubClass: 0x%02x \r\n", desc->bDeviceSubClass); + USB_LOG_DBG("bDeviceProtocol: 0x%02x \r\n", desc->bDeviceProtocol); + USB_LOG_DBG("bMaxPacketSize0: 0x%02x \r\n", desc->bMaxPacketSize0); + USB_LOG_DBG("idVendor: 0x%04x \r\n", desc->idVendor); + USB_LOG_DBG("idProduct: 0x%04x \r\n", desc->idProduct); + USB_LOG_DBG("bcdDevice: 0x%04x \r\n", desc->bcdDevice); + USB_LOG_DBG("iManufacturer: 0x%02x \r\n", desc->iManufacturer); + USB_LOG_DBG("iProduct: 0x%02x \r\n", desc->iProduct); + USB_LOG_DBG("iSerialNumber: 0x%02x \r\n", desc->iSerialNumber); + USB_LOG_DBG("bNumConfigurations: 0x%02x\r\n", desc->bNumConfigurations); +#endif + hport->device_desc.bLength = desc->bLength; + hport->device_desc.bDescriptorType = desc->bDescriptorType; + hport->device_desc.bcdUSB = desc->bcdUSB; + hport->device_desc.bDeviceClass = desc->bDeviceClass; + hport->device_desc.bDeviceSubClass = desc->bDeviceSubClass; + hport->device_desc.bDeviceProtocol = desc->bDeviceProtocol; + hport->device_desc.bMaxPacketSize0 = desc->bMaxPacketSize0; + hport->device_desc.idVendor = desc->idVendor; + hport->device_desc.idProduct = desc->idProduct; + hport->device_desc.bcdDevice = desc->bcdDevice; + hport->device_desc.iManufacturer = desc->iManufacturer; + hport->device_desc.iProduct = desc->iProduct; + hport->device_desc.iSerialNumber = desc->iSerialNumber; + hport->device_desc.bNumConfigurations = desc->bNumConfigurations; + } + return 0; +} + +static int parse_config_descriptor(struct usbh_hubport *hport, struct usb_configuration_descriptor *desc, uint16_t length) +{ + uint32_t total_len = 0; + uint8_t ep_num = 0; + uint8_t intf_num = 0; + uint8_t *p = (uint8_t *)desc; + if (desc->bLength != USB_SIZEOF_CONFIG_DESC) { + USB_LOG_ERR("invalid device bLength 0x%02x\r\n", desc->bLength); + return -EINVAL; + } else if (desc->bDescriptorType != USB_DESCRIPTOR_TYPE_CONFIGURATION) { + USB_LOG_ERR("unexpected descriptor 0x%02x\r\n", desc->bDescriptorType); + return -EINVAL; + } else { + if (length <= USB_SIZEOF_CONFIG_DESC) { + return 0; + } +#if 0 + USB_LOG_DBG("Config Descriptor:\r\n"); + USB_LOG_DBG("bLength: 0x%02x \r\n", desc->bLength); + USB_LOG_DBG("bDescriptorType: 0x%02x \r\n", desc->bDescriptorType); + USB_LOG_DBG("wTotalLength: 0x%04x \r\n", desc->wTotalLength); + USB_LOG_DBG("bNumInterfaces: 0x%02x \r\n", desc->bNumInterfaces); + USB_LOG_DBG("bConfigurationValue: 0x%02x \r\n", desc->bConfigurationValue); + USB_LOG_DBG("iConfiguration: 0x%02x \r\n", desc->iConfiguration); + USB_LOG_DBG("bmAttributes: 0x%02x \r\n", desc->bmAttributes); + USB_LOG_DBG("bMaxPower: 0x%02x \r\n", desc->bMaxPower); +#endif + hport->config.config_desc.bLength = desc->bLength; + hport->config.config_desc.bDescriptorType = desc->bDescriptorType; + hport->config.config_desc.wTotalLength = desc->wTotalLength; + hport->config.config_desc.bNumInterfaces = desc->bNumInterfaces; + hport->config.config_desc.bConfigurationValue = desc->bConfigurationValue; + hport->config.config_desc.iConfiguration = desc->iConfiguration; + hport->config.config_desc.iConfiguration = desc->iConfiguration; + hport->config.config_desc.bmAttributes = desc->bmAttributes; + hport->config.config_desc.bMaxPower = desc->bMaxPower; + + if (length > USB_SIZEOF_CONFIG_DESC) { + while (p[DESC_bLength] && (total_len < desc->wTotalLength) && (intf_num < desc->bNumInterfaces)) { + p += p[DESC_bLength]; + total_len += p[DESC_bLength]; + if (p[DESC_bDescriptorType] == USB_DESCRIPTOR_TYPE_INTERFACE) { + struct usb_interface_descriptor *intf_desc = (struct usb_interface_descriptor *)p; +#if 0 + USB_LOG_DBG("Interface Descriptor:\r\n"); + USB_LOG_DBG("bLength: 0x%02x \r\n", intf_desc->bLength); + USB_LOG_DBG("bDescriptorType: 0x%02x \r\n", intf_desc->bDescriptorType); + USB_LOG_DBG("bInterfaceNumber: 0x%02x \r\n", intf_desc->bInterfaceNumber); + USB_LOG_DBG("bAlternateSetting: 0x%02x \r\n", intf_desc->bAlternateSetting); + USB_LOG_DBG("bNumEndpoints: 0x%02x \r\n", intf_desc->bNumEndpoints); + USB_LOG_DBG("bInterfaceClass: 0x%02x \r\n", intf_desc->bInterfaceClass); + USB_LOG_DBG("bInterfaceSubClass: 0x%02x \r\n", intf_desc->bInterfaceSubClass); + USB_LOG_DBG("bInterfaceProtocol: 0x%02x \r\n", intf_desc->bInterfaceProtocol); + USB_LOG_DBG("iInterface: 0x%02x \r\n", intf_desc->iInterface); +#endif + memset(&hport->config.intf[intf_num], 0, sizeof(struct usbh_interface)); + + hport->config.intf[intf_num].intf_desc.bLength = intf_desc->bLength; + hport->config.intf[intf_num].intf_desc.bDescriptorType = intf_desc->bDescriptorType; + hport->config.intf[intf_num].intf_desc.bInterfaceNumber = intf_desc->bInterfaceNumber; + hport->config.intf[intf_num].intf_desc.bAlternateSetting = intf_desc->bAlternateSetting; + hport->config.intf[intf_num].intf_desc.bNumEndpoints = intf_desc->bNumEndpoints; + hport->config.intf[intf_num].intf_desc.bInterfaceClass = intf_desc->bInterfaceClass; + hport->config.intf[intf_num].intf_desc.bInterfaceSubClass = intf_desc->bInterfaceSubClass; + hport->config.intf[intf_num].intf_desc.bInterfaceProtocol = intf_desc->bInterfaceProtocol; + hport->config.intf[intf_num].intf_desc.iInterface = intf_desc->iInterface; + ep_num = 0; + while (p[DESC_bLength] && (total_len < desc->wTotalLength) && (ep_num < intf_desc->bNumEndpoints)) { + p += p[DESC_bLength]; + total_len += p[DESC_bLength]; + if (p[DESC_bDescriptorType] == USB_DESCRIPTOR_TYPE_ENDPOINT) { + struct usb_endpoint_descriptor *ep_desc = (struct usb_endpoint_descriptor *)p; +#if 0 + USB_LOG_DBG("Endpoint Descriptor:\r\n"); + USB_LOG_DBG("bLength: 0x%02x \r\n", ep_desc->bLength); + USB_LOG_DBG("bDescriptorType: 0x%02x \r\n", ep_desc->bDescriptorType); + USB_LOG_DBG("bEndpointAddress: 0x%02x \r\n", ep_desc->bEndpointAddress); + USB_LOG_DBG("bmAttributes: 0x%02x \r\n", ep_desc->bmAttributes); + USB_LOG_DBG("wMaxPacketSize: 0x%04x \r\n", ep_desc->wMaxPacketSize); + USB_LOG_DBG("bInterval: 0x%02x \r\n", ep_desc->bInterval); +#endif + memset(&hport->config.intf[intf_num].ep[ep_num], 0, sizeof(struct usbh_endpoint)); + + hport->config.intf[intf_num].ep[ep_num].ep_desc.bLength = ep_desc->bLength; + hport->config.intf[intf_num].ep[ep_num].ep_desc.bDescriptorType = ep_desc->bDescriptorType; + hport->config.intf[intf_num].ep[ep_num].ep_desc.bEndpointAddress = ep_desc->bEndpointAddress; + hport->config.intf[intf_num].ep[ep_num].ep_desc.bmAttributes = ep_desc->bmAttributes; + hport->config.intf[intf_num].ep[ep_num].ep_desc.wMaxPacketSize = ep_desc->wMaxPacketSize; + hport->config.intf[intf_num].ep[ep_num].ep_desc.bInterval = ep_desc->bInterval; + ep_num++; + } + } + intf_num++; + } + } + } + } + return 0; +} +#if 0 +static int parse_string_descriptor(struct usbh_hubport *hport, struct usb_string_descriptor *desc, uint8_t str_idx, uint16_t length) +{ + uint8_t string[64 + 1] = { 0 }; + uint8_t *p = (uint8_t *)desc; + + if (desc->bDescriptorType != USB_DESCRIPTOR_TYPE_STRING) { + USB_LOG_ERR("unexpected descriptor 0x%02x\r\n", desc->bDescriptorType); + return -2; + } else { + p += 2; + for (uint32_t i = 0; i < (desc->bLength - 2) / 2; i++) { + string[i] = *p; + p += 2; + } + USB_LOG_DBG("string:%s\r\n", string); + } + return 0; +} +#endif + +static void usbh_print_hubport_info(struct usbh_hubport *hport) +{ + printf("Device Descriptor:\r\n"); + printf("bLength: 0x%02x \r\n", hport->device_desc.bLength); + printf("bDescriptorType: 0x%02x \r\n", hport->device_desc.bDescriptorType); + printf("bcdUSB: 0x%04x \r\n", hport->device_desc.bcdUSB); + printf("bDeviceClass: 0x%02x \r\n", hport->device_desc.bDeviceClass); + printf("bDeviceSubClass: 0x%02x \r\n", hport->device_desc.bDeviceSubClass); + printf("bDeviceProtocol: 0x%02x \r\n", hport->device_desc.bDeviceProtocol); + printf("bMaxPacketSize0: 0x%02x \r\n", hport->device_desc.bMaxPacketSize0); + printf("idVendor: 0x%04x \r\n", hport->device_desc.idVendor); + printf("idProduct: 0x%04x \r\n", hport->device_desc.idProduct); + printf("bcdDevice: 0x%04x \r\n", hport->device_desc.bcdDevice); + printf("iManufacturer: 0x%02x \r\n", hport->device_desc.iManufacturer); + printf("iProduct: 0x%02x \r\n", hport->device_desc.iProduct); + printf("iSerialNumber: 0x%02x \r\n", hport->device_desc.iSerialNumber); + printf("bNumConfigurations: 0x%02x\r\n", hport->device_desc.bNumConfigurations); + + printf("Config Descriptor:\r\n"); + printf("bLength: 0x%02x \r\n", hport->config.config_desc.bLength); + printf("bDescriptorType: 0x%02x \r\n", hport->config.config_desc.bDescriptorType); + printf("wTotalLength: 0x%04x \r\n", hport->config.config_desc.wTotalLength); + printf("bNumInterfaces: 0x%02x \r\n", hport->config.config_desc.bNumInterfaces); + printf("bConfigurationValue: 0x%02x \r\n", hport->config.config_desc.bConfigurationValue); + printf("iConfiguration: 0x%02x \r\n", hport->config.config_desc.iConfiguration); + printf("bmAttributes: 0x%02x \r\n", hport->config.config_desc.bmAttributes); + printf("bMaxPower: 0x%02x \r\n", hport->config.config_desc.bMaxPower); + + for (uint8_t i = 0; i < hport->config.config_desc.bNumInterfaces; i++) { + printf("Interface Descriptor:\r\n"); + printf("bLength: 0x%02x \r\n", hport->config.intf[i].intf_desc.bLength); + printf("bDescriptorType: 0x%02x \r\n", hport->config.intf[i].intf_desc.bDescriptorType); + printf("bInterfaceNumber: 0x%02x \r\n", hport->config.intf[i].intf_desc.bInterfaceNumber); + printf("bAlternateSetting: 0x%02x \r\n", hport->config.intf[i].intf_desc.bAlternateSetting); + printf("bNumEndpoints: 0x%02x \r\n", hport->config.intf[i].intf_desc.bNumEndpoints); + printf("bInterfaceClass: 0x%02x \r\n", hport->config.intf[i].intf_desc.bInterfaceClass); + printf("bInterfaceSubClass: 0x%02x \r\n", hport->config.intf[i].intf_desc.bInterfaceSubClass); + printf("bInterfaceProtocol: 0x%02x \r\n", hport->config.intf[i].intf_desc.bInterfaceProtocol); + printf("iInterface: 0x%02x \r\n", hport->config.intf[i].intf_desc.iInterface); + + for (uint8_t j = 0; j < hport->config.intf[i].intf_desc.bNumEndpoints; j++) { + printf("Endpoint Descriptor:\r\n"); + printf("bLength: 0x%02x \r\n", hport->config.intf[i].ep[j].ep_desc.bLength); + printf("bDescriptorType: 0x%02x \r\n", hport->config.intf[i].ep[j].ep_desc.bDescriptorType); + printf("bEndpointAddress: 0x%02x \r\n", hport->config.intf[i].ep[j].ep_desc.bEndpointAddress); + printf("bmAttributes: 0x%02x \r\n", hport->config.intf[i].ep[j].ep_desc.bmAttributes); + printf("wMaxPacketSize: 0x%04x \r\n", hport->config.intf[i].ep[j].ep_desc.wMaxPacketSize); + printf("bInterval: 0x%02x \r\n", hport->config.intf[i].ep[j].ep_desc.bInterval); + } + } +} + +static int usbh_enumerate(struct usbh_hubport *hport) +{ + struct usb_interface_descriptor *intf_desc; + struct usb_setup_packet *setup; + uint8_t *ep0_buffer; + uint8_t descsize; + int dev_addr; + uint8_t ep_mps; + int ret; + +#define USB_REQUEST_BUFFER_SIZE 256 + /* Allocate buffer for setup and data buffer */ + setup = usb_iomalloc(sizeof(struct usb_setup_packet)); + if (setup == NULL) { + USB_LOG_ERR("Fail to alloc setup\r\n"); + return -ENOMEM; + } + + ep0_buffer = usb_iomalloc(USB_REQUEST_BUFFER_SIZE); + if (ep0_buffer == NULL) { + USB_LOG_ERR("Fail to alloc ep0_buffer\r\n"); + return -ENOMEM; + } + + /* Pick an appropriate packet size for this device + * + * USB 2.0, Paragraph 5.5.3 "Control Transfer Packet Size Constraints" + * + * "An endpoint for control transfers specifies the maximum data + * payload size that the endpoint can accept from or transmit to + * the bus. The allowable maximum control transfer data payload + * sizes for full-speed devices is 8, 16, 32, or 64 bytes; for + * high-speed devices, it is 64 bytes and for low-speed devices, + * it is 8 bytes. This maximum applies to the data payloads of the + * Data packets following a Setup..." + */ + + if (hport->speed == USB_SPEED_HIGH) { + /* For high-speed, we must use 64 bytes */ + ep_mps = 64; + descsize = USB_SIZEOF_DEVICE_DESC; + } else { + /* Eight will work for both low- and full-speed */ + ep_mps = 8; + descsize = 8; + } + + /* Configure EP0 with the initial maximum packet size */ + usbh_ep0_reconfigure(hport->ep0, 0, ep_mps, hport->speed); + + /* Read the first 8 bytes of the device descriptor */ + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; + setup->wValue = (uint16_t)((USB_DESCRIPTOR_TYPE_DEVICE << 8) | 0); + setup->wIndex = 0; + setup->wLength = descsize; + + ret = usbh_control_transfer(hport->ep0, setup, ep0_buffer); + if (ret < 0) { + USB_LOG_ERR("Failed to get device descriptor,errorcode:%d\r\n", ret); + goto errout; + } + + parse_device_descriptor(hport, (struct usb_device_descriptor *)ep0_buffer, descsize); + + /* Extract the correct max packetsize from the device descriptor */ + ep_mps = ((struct usb_device_descriptor *)ep0_buffer)->bMaxPacketSize0; + + /* And reconfigure EP0 with the correct maximum packet size */ + usbh_ep0_reconfigure(hport->ep0, 0, ep_mps, hport->speed); + + /* Assign a function address to the device connected to this port */ + dev_addr = usbh_devaddr_create(hport); + if (dev_addr < 0) { + USB_LOG_ERR("Failed to allocate devaddr,errorcode:%d\r\n", ret); + goto errout; + } + + /* Set the USB device address */ + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = USB_REQUEST_SET_ADDRESS; + setup->wValue = dev_addr; + setup->wIndex = 0; + setup->wLength = 0; + + ret = usbh_control_transfer(hport->ep0, setup, NULL); + if (ret < 0) { + USB_LOG_ERR("Failed to set devaddr,errorcode:%d\r\n", ret); + goto errout; + } + + /* wait device address set completely */ + usb_osal_msleep(2); + + /* Assign the function address to the port */ + hport->dev_addr = dev_addr; + + /* And reconfigure EP0 with the correct address */ + usbh_ep0_reconfigure(hport->ep0, dev_addr, ep_mps, hport->speed); + + /* Read the full device descriptor if hport is not in high speed*/ + if (descsize < USB_SIZEOF_DEVICE_DESC) { + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; + setup->wValue = (uint16_t)((USB_DESCRIPTOR_TYPE_DEVICE << 8) | 0); + setup->wIndex = 0; + setup->wLength = USB_SIZEOF_DEVICE_DESC; + + ret = usbh_control_transfer(hport->ep0, setup, ep0_buffer); + if (ret < 0) { + USB_LOG_ERR("Failed to get full device descriptor,errorcode:%d\r\n", ret); + goto errout; + } + + parse_device_descriptor(hport, (struct usb_device_descriptor *)ep0_buffer, USB_SIZEOF_DEVICE_DESC); + } + + /* Read the first 9 bytes of the config descriptor */ + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; + setup->wValue = (uint16_t)((USB_DESCRIPTOR_TYPE_CONFIGURATION << 8) | 0); + setup->wIndex = 0; + setup->wLength = USB_SIZEOF_CONFIG_DESC; + + ret = usbh_control_transfer(hport->ep0, setup, ep0_buffer); + if (ret < 0) { + USB_LOG_ERR("Failed to get config descriptor,errorcode:%d\r\n", ret); + goto errout; + } + + parse_config_descriptor(hport, (struct usb_configuration_descriptor *)ep0_buffer, USB_SIZEOF_CONFIG_DESC); + + /* Read the full size of the configuration data */ + uint16_t wTotalLength = ((struct usb_configuration_descriptor *)ep0_buffer)->wTotalLength; + + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; + setup->wValue = (uint16_t)((USB_DESCRIPTOR_TYPE_CONFIGURATION << 8) | 0); + setup->wIndex = 0; + setup->wLength = wTotalLength; + + ret = usbh_control_transfer(hport->ep0, setup, ep0_buffer); + if (ret < 0) { + USB_LOG_ERR("Failed to get full config descriptor,errorcode:%d\r\n", ret); + goto errout; + } + + parse_config_descriptor(hport, (struct usb_configuration_descriptor *)ep0_buffer, wTotalLength); + + /* Select device configuration 1 */ + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = USB_REQUEST_SET_CONFIGURATION; + setup->wValue = 1; + setup->wIndex = 0; + setup->wLength = 0; + + ret = usbh_control_transfer(hport->ep0, setup, NULL); + if (ret < 0) { + USB_LOG_ERR("Failed to set configuration,errorcode:%d\r\n", ret); + goto errout; + } + + /*search supported class driver*/ + for (uint8_t i = 0; i < hport->config.config_desc.bNumInterfaces; i++) { + intf_desc = &hport->config.intf[i].intf_desc; + + struct usbh_class_driver *class_driver = (struct usbh_class_driver *)usbh_find_class_driver(intf_desc->bInterfaceClass, intf_desc->bInterfaceSubClass, intf_desc->bInterfaceProtocol, hport->device_desc.idVendor, hport->device_desc.idProduct); + + if (class_driver == NULL) { + USB_LOG_ERR("do not support Class:0x%02x,Subclass:0x%02x,Protocl:0x%02x\r\n", + intf_desc->bInterfaceClass, + intf_desc->bInterfaceSubClass, + intf_desc->bInterfaceProtocol); + + continue; + } + hport->config.intf[i].class_driver = class_driver; + + if (hport->config.intf[i].class_driver->connect) { + ret = CLASS_CONNECT(hport, i); + if (ret < 0) { + ret = CLASS_DISCONNECT(hport, i); + goto errout; + } + } + } + +errout: + if (ret < 0) { + usbh_hport_deactivate(hport); + } + + if (setup) { + usb_iofree(setup); + } + if (ep0_buffer) { + usb_iofree(ep0_buffer); + } + + return ret; +} + +static int usbh_portchange_wait(struct usbh_hubport **hport) +{ + struct usbh_hubport *connport = NULL; + uint32_t flags; + int ret; + + /* Loop until a change in connection state is detected */ + while (1) { + /* Check for a change in the connection state on any root hub port */ + flags = usb_osal_enter_critical_section(); + for (uint8_t port = USBH_HUB_PORT_START_INDEX; port <= CONFIG_USBHOST_RHPORTS; port++) { + connport = &usbh_core_cfg.rhport[port - 1].hport; + + if (connport->port_change) { + connport->port_change = false; + *hport = connport; + usb_osal_leave_critical_section(flags); + return 0; + } + } + /* Is a device connected to an external hub? */ + if (usbh_core_cfg.active_hport) { + connport = (struct usbh_hubport *)usbh_core_cfg.active_hport; + usbh_core_cfg.active_hport = NULL; + *hport = connport; + usb_osal_leave_critical_section(flags); + return 0; + } + /* No changes on any port. Wait for a connection/disconnection event and check again */ + usbh_core_cfg.pscwait = true; + usb_osal_leave_critical_section(flags); + ret = usb_osal_sem_take(usbh_core_cfg.pscsem); + if (ret < 0) { + return ret; + } + } +} + +static void usbh_portchange_detect_thread(void *argument) +{ + struct usbh_hubport *hport = NULL; + uint32_t flags; + + flags = usb_osal_enter_critical_section(); + usb_hc_init(); + + for (uint8_t port = USBH_HUB_PORT_START_INDEX; port <= CONFIG_USBHOST_RHPORTS; port++) { + usbh_core_cfg.rhport[port - 1].hport.port = port; + usbh_core_cfg.rhport[port - 1].devgen.next = 1; + usbh_hport_activate(&usbh_core_cfg.rhport[port - 1].hport); + } + + usb_osal_leave_critical_section(flags); + + while (1) { + usbh_portchange_wait(&hport); + if (hport->connected) { + /*if roothub port,reset port first*/ + if (ROOTHUB(hport)) { + /* Reset the host port */ + usbh_reset_port(hport->port); + usb_osal_msleep(200); + /* Get the current device speed */ + hport->speed = usbh_get_port_speed(hport->port); + USB_LOG_INFO("Bus %u, Port %u connected, %s\r\n", 1, hport->port, speed_table[hport->speed]); + } else { + USB_LOG_INFO("Bus %u, Port %u connected, %s\r\n", hport->parent->index, hport->port, speed_table[hport->speed]); + } + usb_osal_thread_suspend(g_lpworkq.thread); + usbh_enumerate(hport); + usb_osal_thread_resume(g_lpworkq.thread); + } else { + usbh_hport_deactivate(hport); + for (uint8_t i = 0; i < hport->config.config_desc.bNumInterfaces; i++) { + if (hport->config.intf[i].class_driver && hport->config.intf[i].class_driver->disconnect) { + CLASS_DISCONNECT(hport, i); + } + } + + hport->config.config_desc.bNumInterfaces = 0; + + if (ROOTHUB(hport)) { + USB_LOG_INFO("Bus %u,Port:%u disconnected\r\n", 1, hport->port); + } else { + USB_LOG_INFO("Bus %u,Port:%u disconnected\r\n", hport->parent->index, hport->port); + } + } + } +} + +void usbh_external_hport_connect(struct usbh_hubport *hport) +{ + uint32_t flags; + + usbh_hport_activate(hport); + + flags = usb_osal_enter_critical_section(); + + hport->connected = true; + usbh_core_cfg.active_hport = hport; + + if (usbh_core_cfg.pscwait) { + usbh_core_cfg.pscwait = false; + usb_osal_sem_give(usbh_core_cfg.pscsem); + } + + usb_osal_leave_critical_section(flags); +} + +void usbh_external_hport_disconnect(struct usbh_hubport *hport) +{ + uint32_t flags; + + flags = usb_osal_enter_critical_section(); + hport->connected = false; + usbh_core_cfg.active_hport = hport; + + if (usbh_core_cfg.pscwait) { + usbh_core_cfg.pscwait = false; + usb_osal_sem_give(usbh_core_cfg.pscsem); + } + + usb_osal_leave_critical_section(flags); +} + +void usbh_hport_activate(struct usbh_hubport *hport) +{ + struct usbh_endpoint_cfg ep0_cfg; + uint32_t flags; + + flags = usb_osal_enter_critical_section(); + memset(&ep0_cfg, 0, sizeof(struct usbh_endpoint_cfg)); + + ep0_cfg.ep_addr = 0x00; + ep0_cfg.ep_interval = 0x00; + ep0_cfg.ep_mps = 0x08; + ep0_cfg.ep_type = USB_ENDPOINT_TYPE_CONTROL; + ep0_cfg.hport = hport; + /* Allocate memory for roothub port control endpoint */ + usbh_ep_alloc(&hport->ep0, &ep0_cfg); + + usb_osal_leave_critical_section(flags); +} + +void usbh_hport_deactivate(struct usbh_hubport *hport) +{ + uint32_t flags; + + flags = usb_osal_enter_critical_section(); + /* Don't free the control pipe of root hub ports! */ + if (hport->parent != NULL && hport->ep0 != NULL) { + usb_ep_cancel(hport->ep0); + usbh_ep_free(hport->ep0); + hport->ep0 = NULL; + } + /* Free the device address if one has been assigned */ + usbh_devaddr_destroy(hport, hport->dev_addr); + hport->dev_addr = 0; + + usb_osal_leave_critical_section(flags); +} + +void usbh_event_notify_handler(uint8_t event, uint8_t rhport) +{ + switch (event) { + case USBH_EVENT_ATTACHED: + if (!usbh_core_cfg.rhport[rhport - 1].hport.connected) { + usbh_core_cfg.rhport[rhport - 1].hport.connected = true; + usbh_core_cfg.rhport[rhport - 1].hport.port_change = true; + if (usbh_core_cfg.pscwait) { + usbh_core_cfg.pscwait = false; + usb_osal_sem_give(usbh_core_cfg.pscsem); + } + } + break; + case USBH_EVENT_REMOVED: + if (usbh_core_cfg.rhport[rhport - 1].hport.connected) { + usbh_core_cfg.rhport[rhport - 1].hport.connected = false; + usbh_core_cfg.rhport[rhport - 1].hport.port_change = true; + if (usbh_core_cfg.pscwait) { + usbh_core_cfg.pscwait = false; + usb_osal_sem_give(usbh_core_cfg.pscsem); + } + } + break; + default: + break; + } +} + +int usbh_initialize(void) +{ + usb_osal_thread_t usb_thread; + + memset(&usbh_core_cfg, 0, sizeof(struct usbh_core_priv)); + + usbh_workq_initialize(); + + usbh_core_cfg.pscsem = usb_osal_sem_create(0); + if (usbh_core_cfg.pscsem == NULL) { + return -1; + } + + usb_thread = usb_osal_thread_create("usbh_psc", CONFIG_USBHOST_PSC_STACKSIZE, CONFIG_USBHOST_PSC_PRIO, usbh_portchange_detect_thread, NULL); + if (usb_thread == NULL) { + return -1; + } + + return 0; +} + +int lsusb(int argc, char **argv) +{ + usb_slist_t *hub_list; + uint8_t port; + + if (argc < 2) { + printf("Usage: lsusb [options]...\r\n"); + printf("List USB devices\r\n"); + printf(" -v, --verbose\r\n"); + printf(" Increase verbosity (show descriptors)\r\n"); + printf(" -s [[bus]:[devnum]]\r\n"); + printf(" Show only devices with specified device and/or bus numbers (in decimal)\r\n"); + printf(" -d vendor:[product]\r\n"); + printf(" Show only devices with the specified vendor and product ID numbers (in hexadecimal)\r\n"); + printf(" -t, --tree\r\n"); + printf(" Dump the physical USB device hierachy as a tree\r\n"); + printf(" -V, --version\r\n"); + printf(" Show version of program\r\n"); + printf(" -h, --help\r\n"); + printf(" Show usage and help\r\n"); + return 0; + } + + if (argc > 3) { + return 0; + } + + if (strcmp(argv[1], "-t") == 0) { + for (port = USBH_HUB_PORT_START_INDEX; port <= CONFIG_USBHOST_RHPORTS; port++) { + if (usbh_core_cfg.rhport[port - 1].hport.connected) { + printf("/: Bus %02u,VID:PID 0x%04x:0x%04x\r\n", USBH_ROOT_HUB_INDEX, usbh_core_cfg.rhport[port - 1].hport.device_desc.idVendor, usbh_core_cfg.rhport[port - 1].hport.device_desc.idProduct); + + for (uint8_t i = 0; i < usbh_core_cfg.rhport[port - 1].hport.config.config_desc.bNumInterfaces; i++) { + if (usbh_core_cfg.rhport[port - 1].hport.config.intf[i].class_driver->driver_name) { + printf(" |__Port %u,Port addr:0x%02x,If %u,ClassDriver=%s\r\n", usbh_core_cfg.rhport[port - 1].hport.port, usbh_core_cfg.rhport[port - 1].hport.dev_addr, + i, usbh_core_cfg.rhport[port - 1].hport.config.intf[i].class_driver->driver_name); + } + } + } + } + usb_slist_for_each(hub_list, &hub_class_head) + { + usbh_hub_t *hub_class = usb_slist_entry(hub_list, struct usbh_hub, list); + + for (port = USBH_HUB_PORT_START_INDEX; port <= hub_class->nports; port++) { + if (hub_class->child[port - 1].connected) { + printf("/: Bus %02u,VID:PID 0x%04x:0x%04x\r\n", hub_class->index, hub_class->child[port - 1].device_desc.idVendor, hub_class->child[port - 1].device_desc.idProduct); + + for (uint8_t i = 0; i < hub_class->child[port - 1].config.config_desc.bNumInterfaces; i++) { + if (hub_class->child[port - 1].config.intf[i].class_driver->driver_name) { + printf(" |__Port %u,Port addr:0x%02x,If %u,ClassDriver=%s\r\n", hub_class->child[port - 1].port, hub_class->child[port - 1].dev_addr, + i, hub_class->child[port - 1].config.intf[i].class_driver->driver_name); + } + } + } + } + } + } else if (strcmp(argv[1], "-v") == 0) { + for (port = USBH_HUB_PORT_START_INDEX; port <= CONFIG_USBHOST_RHPORTS; port++) { + if (usbh_core_cfg.rhport[port - 1].hport.connected) { + printf("Bus %02u,Port %u,Port addr:0x%02x,VID:PID 0x%04x:0x%04x\r\n", USBH_ROOT_HUB_INDEX, usbh_core_cfg.rhport[port - 1].hport.port, usbh_core_cfg.rhport[port - 1].hport.dev_addr, + usbh_core_cfg.rhport[port - 1].hport.device_desc.idVendor, usbh_core_cfg.rhport[port - 1].hport.device_desc.idProduct); + usbh_print_hubport_info(&usbh_core_cfg.rhport[port - 1].hport); + } + } + + usb_slist_for_each(hub_list, &hub_class_head) + { + usbh_hub_t *hub_class = usb_slist_entry(hub_list, struct usbh_hub, list); + + for (port = USBH_HUB_PORT_START_INDEX; port <= hub_class->nports; port++) { + if (hub_class->child[port - 1].connected) { + printf("Bus %02u,Port %u,Port addr:0x%02x,VID:PID 0x%04x:0x%04x\r\n", hub_class->index, hub_class->child[port - 1].port, hub_class->child[port - 1].dev_addr, + hub_class->child[port - 1].device_desc.idVendor, hub_class->child[port - 1].device_desc.idProduct); + usbh_print_hubport_info(&hub_class->child[port - 1]); + } + } + } + } + + return 0; +} + +struct usbh_hubport *usbh_get_hubport(uint8_t dev_addr) +{ + usb_slist_t *hub_list; + uint8_t port; + + for (port = USBH_HUB_PORT_START_INDEX; port <= CONFIG_USBHOST_RHPORTS; port++) { + if (usbh_core_cfg.rhport[port - 1].hport.connected) { + if (usbh_core_cfg.rhport[port - 1].hport.dev_addr == dev_addr) { + return &usbh_core_cfg.rhport[port - 1].hport; + } + } + } + usb_slist_for_each(hub_list, &hub_class_head) + { + usbh_hub_t *hub_class = usb_slist_entry(hub_list, struct usbh_hub, list); + + for (port = USBH_HUB_PORT_START_INDEX; port <= hub_class->nports; port++) { + if (hub_class->child[port - 1].connected) { + if (hub_class->child[port - 1].dev_addr == dev_addr) { + return &hub_class->child[port - 1]; + } + } + } + } + return NULL; +} + +void *usbh_get_class(uint8_t dev_addr, uint8_t intf) +{ + usb_slist_t *hub_list; + uint8_t port; + + for (port = USBH_HUB_PORT_START_INDEX; port <= CONFIG_USBHOST_RHPORTS; port++) { + if (usbh_core_cfg.rhport[port - 1].hport.connected) { + if (usbh_core_cfg.rhport[port - 1].hport.dev_addr == dev_addr) { + if (usbh_core_cfg.rhport[port - 1].hport.config.intf[intf].priv) { + return usbh_core_cfg.rhport[port - 1].hport.config.intf[intf].priv; + } + } + } + } + usb_slist_for_each(hub_list, &hub_class_head) + { + usbh_hub_t *hub_class = usb_slist_entry(hub_list, struct usbh_hub, list); + + for (port = USBH_HUB_PORT_START_INDEX; port <= hub_class->nports; port++) { + if (hub_class->child[port - 1].connected) { + if (hub_class->child[port - 1].dev_addr == dev_addr) { + if (hub_class->child[port - 1].config.intf[intf].priv) { + return hub_class->child[port - 1].config.intf[intf].priv; + } + } + } + } + } + return NULL; +} + +const struct usbh_class_info class_info_table[] = { + + { .class = USB_DEVICE_CLASS_CDC, + .subclass = CDC_ABSTRACT_CONTROL_MODEL, + .protocol = CDC_COMMON_PROTOCOL_AT_COMMANDS, + .vid = 0x00, + .pid = 0x00, + .class_driver = &cdc_acm_class_driver }, + { .class = USB_DEVICE_CLASS_HID, + .subclass = HID_SUBCLASS_BOOTIF, + .protocol = HID_PROTOCOL_KEYBOARD, + .vid = 0x00, + .pid = 0x00, + .class_driver = &hid_class_driver }, + { .class = USB_DEVICE_CLASS_HID, + .subclass = HID_SUBCLASS_BOOTIF, + .protocol = HID_PROTOCOL_MOUSE, + .vid = 0x00, + .pid = 0x00, + .class_driver = &hid_class_driver }, + { .class = USB_DEVICE_CLASS_MASS_STORAGE, + .subclass = MSC_SUBCLASS_SCSI, + .protocol = MSC_PROTOCOL_BULK_ONLY, + .vid = 0x00, + .pid = 0x00, + .class_driver = &msc_class_driver }, +#ifdef CONFIG_USBHOST_HUB + { .class = USB_DEVICE_CLASS_HUB, + .subclass = 0, + .protocol = 0, + .vid = 0x00, + .pid = 0x00, + .class_driver = &hub_class_driver }, + { .class = USB_DEVICE_CLASS_HUB, + .subclass = 0, + .protocol = 1, + .vid = 0x00, + .pid = 0x00, + .class_driver = &hub_class_driver }, +#endif +}; + +static const struct usbh_class_driver *usbh_find_class_driver(uint8_t class, uint8_t subcalss, uint8_t protocol, uint16_t vid, uint16_t pid) +{ + for (uint8_t i = 0; i < sizeof(class_info_table) / sizeof(class_info_table[0]); i++) { + if (class == class_info_table[i].class && + subcalss == class_info_table[i].subclass && + protocol == class_info_table[i].protocol) { + /* If this is a vendor-specific class ID, then the VID and PID have to match as well. */ + if (class == USB_DEVICE_CLASS_VEND_SPECIFIC) { + if (vid == class_info_table[i].vid && + pid == class_info_table[i].pid) { + return class_info_table[i].class_driver; + } + } + return class_info_table[i].class_driver; + } + } + + return NULL; +} diff --git a/core/usbh_core.h b/core/usbh_core.h index ee74e352..740c5380 100644 --- a/core/usbh_core.h +++ b/core/usbh_core.h @@ -1,128 +1,128 @@ -/** - * @file usbh_core.h - * - * Copyright (c) 2022 sakumisu - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ -#ifndef _USBH_CORE_H -#define _USBH_CORE_H - -#include "usb_util.h" -#include "usb_def.h" -#include "usb_hc.h" -#include "usb_osal.h" -#include "usb_workq.h" -#include "usbh_hub.h" -#include "usb_config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define USBH_ROOT_HUB_INDEX 1 /* roothub index*/ -#define USBH_EX_HUB_INDEX 2 /* external hub index */ -#define USBH_HUB_PORT_START_INDEX 1 /* first hub port index */ - -#ifdef CONFIG_USBHOST_HUB -#define ROOTHUB(hport) ((hport)->parent == NULL) -#else -#define ROOTHUB(hport) true -#endif - -#define CLASS_CONNECT(hport,i) ((hport)->config.intf[i].class_driver->connect(hport, i)) -#define CLASS_DISCONNECT(hport,i) ((hport)->config.intf[i].class_driver->disconnect(hport, i)) - -enum usbh_event_type { - USBH_EVENT_ATTACHED, - USBH_EVENT_REMOVED, -}; - -struct usbh_class_info { - uint8_t class; /* Base device class code */ - uint8_t subclass; /* Sub-class, depends on base class. Eg. */ - uint8_t protocol; /* Protocol, depends on base class. Eg. */ - uint16_t vid; /* Vendor ID (for vendor/product specific devices) */ - uint16_t pid; /* Product ID (for vendor/product specific devices) */ - const struct usbh_class_driver *class_driver; -}; - -struct usbh_hubport; -struct usbh_class_driver { - const char *driver_name; - int (*connect)(struct usbh_hubport *hport, uint8_t intf); - int (*disconnect)(struct usbh_hubport *hport, uint8_t intf); -}; - -typedef struct usbh_endpoint { - struct usb_endpoint_descriptor ep_desc; -} usbh_endpoint_t; - -typedef struct usbh_interface { - struct usb_interface_descriptor intf_desc; - struct usbh_endpoint ep[CONFIG_USBHOST_EP_NUM]; - struct usbh_class_driver *class_driver; - void *priv; -} usbh_interface_t; - -typedef struct usbh_configuration { - struct usb_configuration_descriptor config_desc; - struct usbh_interface intf[CONFIG_USBHOST_INTF_NUM]; -} usbh_configuration_t; - -typedef struct usbh_hubport { - bool connected; /* True: device connected; false: disconnected */ - bool port_change; /* True: port changed; false: port do not change */ - uint8_t port; /* Hub port index */ - uint8_t dev_addr; /* device address */ - uint8_t speed; /* device speed */ - usbh_epinfo_t ep0; /* control ep info */ - struct usb_device_descriptor device_desc; - struct usbh_configuration config; -#if 0 - uint8_t* config_desc; -#endif - struct usbh_hub *parent; /*if NULL, is roothub*/ -} usbh_hubport_t; - -typedef struct usbh_hub { - usb_slist_t list; - uint8_t index; /* Hub index */ - uint8_t nports; /* Hub port number */ - uint8_t dev_addr; /* Hub device address */ - usbh_epinfo_t intin; - uint8_t *int_buffer; - struct usb_setup_packet *setup; - struct hub_port_status *port_status; - struct usb_hub_descriptor hub_desc; - struct usbh_hubport child[CONFIG_USBHOST_EHPORTS]; - struct usbh_hubport *parent; /* Parent hub port */ - struct usb_work work; -} usbh_hub_t; - -void usbh_event_notify_handler(uint8_t event, uint8_t rhport); - -int usbh_initialize(void); -int lsusb(int argc, char **argv); -struct usbh_hubport *usbh_get_hubport(uint8_t dev_addr); -void *usbh_get_class(uint8_t dev_addr, uint8_t intf); - -#ifdef __cplusplus -} -#endif - -#endif +/** + * @file usbh_core.h + * + * Copyright (c) 2022 sakumisu + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _USBH_CORE_H +#define _USBH_CORE_H + +#include "usb_util.h" +#include "usb_def.h" +#include "usb_hc.h" +#include "usb_osal.h" +#include "usb_workq.h" +#include "usbh_hub.h" +#include "usb_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define USBH_ROOT_HUB_INDEX 1 /* roothub index*/ +#define USBH_EX_HUB_INDEX 2 /* external hub index */ +#define USBH_HUB_PORT_START_INDEX 1 /* first hub port index */ + +#ifdef CONFIG_USBHOST_HUB +#define ROOTHUB(hport) ((hport)->parent == NULL) +#else +#define ROOTHUB(hport) true +#endif + +#define CLASS_CONNECT(hport,i) ((hport)->config.intf[i].class_driver->connect(hport, i)) +#define CLASS_DISCONNECT(hport,i) ((hport)->config.intf[i].class_driver->disconnect(hport, i)) + +enum usbh_event_type { + USBH_EVENT_ATTACHED, + USBH_EVENT_REMOVED, +}; + +struct usbh_class_info { + uint8_t class; /* Base device class code */ + uint8_t subclass; /* Sub-class, depends on base class. Eg. */ + uint8_t protocol; /* Protocol, depends on base class. Eg. */ + uint16_t vid; /* Vendor ID (for vendor/product specific devices) */ + uint16_t pid; /* Product ID (for vendor/product specific devices) */ + const struct usbh_class_driver *class_driver; +}; + +struct usbh_hubport; +struct usbh_class_driver { + const char *driver_name; + int (*connect)(struct usbh_hubport *hport, uint8_t intf); + int (*disconnect)(struct usbh_hubport *hport, uint8_t intf); +}; + +typedef struct usbh_endpoint { + struct usb_endpoint_descriptor ep_desc; +} usbh_endpoint_t; + +typedef struct usbh_interface { + struct usb_interface_descriptor intf_desc; + struct usbh_endpoint ep[CONFIG_USBHOST_EP_NUM]; + struct usbh_class_driver *class_driver; + void *priv; +} usbh_interface_t; + +typedef struct usbh_configuration { + struct usb_configuration_descriptor config_desc; + struct usbh_interface intf[CONFIG_USBHOST_INTF_NUM]; +} usbh_configuration_t; + +typedef struct usbh_hubport { + bool connected; /* True: device connected; false: disconnected */ + bool port_change; /* True: port changed; false: port do not change */ + uint8_t port; /* Hub port index */ + uint8_t dev_addr; /* device address */ + uint8_t speed; /* device speed */ + usbh_epinfo_t ep0; /* control ep info */ + struct usb_device_descriptor device_desc; + struct usbh_configuration config; +#if 0 + uint8_t* config_desc; +#endif + struct usbh_hub *parent; /*if NULL, is roothub*/ +} usbh_hubport_t; + +typedef struct usbh_hub { + usb_slist_t list; + uint8_t index; /* Hub index */ + uint8_t nports; /* Hub port number */ + uint8_t dev_addr; /* Hub device address */ + usbh_epinfo_t intin; + uint8_t *int_buffer; + struct usb_setup_packet *setup; + struct hub_port_status *port_status; + struct usb_hub_descriptor hub_desc; + struct usbh_hubport child[CONFIG_USBHOST_EHPORTS]; + struct usbh_hubport *parent; /* Parent hub port */ + struct usb_work work; +} usbh_hub_t; + +void usbh_event_notify_handler(uint8_t event, uint8_t rhport); + +int usbh_initialize(void); +int lsusb(int argc, char **argv); +struct usbh_hubport *usbh_get_hubport(uint8_t dev_addr); +void *usbh_get_class(uint8_t dev_addr, uint8_t intf); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usb_config.h b/usb_config.h index 9715686a..c36ee8e9 100644 --- a/usb_config.h +++ b/usb_config.h @@ -1,46 +1,46 @@ -#ifndef _USB_CONFIG_H -#define _USB_CONFIG_H - -/* USB DEVICE Configuration */ - -/* USB HOST Configuration */ -#ifndef CONFIG_USBHOST_RHPORTS -#define CONFIG_USBHOST_RHPORTS 1 -#endif - -#ifndef CONFIG_USBHOST_EHPORTS -#define CONFIG_USBHOST_EHPORTS 4 -#endif - -#ifndef CONFIG_USBHOST_INTF_NUM -#define CONFIG_USBHOST_INTF_NUM 6 -#endif - -#ifndef CONFIG_USBHOST_EP_NUM -#define CONFIG_USBHOST_EP_NUM 2 -#endif - -#ifndef CONFIG_USBHOST_HPWORKQ_PRIO -#define CONFIG_USBHOST_HPWORKQ_PRIO 5 -#endif -#ifndef CONFIG_USBHOST_HPWORKQ_STACKSIZE -#define CONFIG_USBHOST_HPWORKQ_STACKSIZE 2048 -#endif - -#ifndef CONFIG_USBHOST_LPWORKQ_PRIO -#define CONFIG_USBHOST_LPWORKQ_PRIO 1 -#endif -#ifndef CONFIG_USBHOST_LPWORKQ_STACKSIZE -#define CONFIG_USBHOST_LPWORKQ_STACKSIZE 2048 -#endif - -#ifndef CONFIG_USBHOST_PSC_PRIO -#define CONFIG_USBHOST_PSC_PRIO 4 -#endif -#ifndef CONFIG_USBHOST_PSC_STACKSIZE -#define CONFIG_USBHOST_PSC_STACKSIZE 2048 -#endif - -#define CONFIG_USBHOST_ASYNCH - +#ifndef _USB_CONFIG_H +#define _USB_CONFIG_H + +/* USB DEVICE Configuration */ + +/* USB HOST Configuration */ +#ifndef CONFIG_USBHOST_RHPORTS +#define CONFIG_USBHOST_RHPORTS 1 +#endif + +#ifndef CONFIG_USBHOST_EHPORTS +#define CONFIG_USBHOST_EHPORTS 4 +#endif + +#ifndef CONFIG_USBHOST_INTF_NUM +#define CONFIG_USBHOST_INTF_NUM 6 +#endif + +#ifndef CONFIG_USBHOST_EP_NUM +#define CONFIG_USBHOST_EP_NUM 2 +#endif + +#ifndef CONFIG_USBHOST_HPWORKQ_PRIO +#define CONFIG_USBHOST_HPWORKQ_PRIO 5 +#endif +#ifndef CONFIG_USBHOST_HPWORKQ_STACKSIZE +#define CONFIG_USBHOST_HPWORKQ_STACKSIZE 2048 +#endif + +#ifndef CONFIG_USBHOST_LPWORKQ_PRIO +#define CONFIG_USBHOST_LPWORKQ_PRIO 1 +#endif +#ifndef CONFIG_USBHOST_LPWORKQ_STACKSIZE +#define CONFIG_USBHOST_LPWORKQ_STACKSIZE 2048 +#endif + +#ifndef CONFIG_USBHOST_PSC_PRIO +#define CONFIG_USBHOST_PSC_PRIO 4 +#endif +#ifndef CONFIG_USBHOST_PSC_STACKSIZE +#define CONFIG_USBHOST_PSC_STACKSIZE 2048 +#endif + +#define CONFIG_USBHOST_ASYNCH + #endif \ No newline at end of file