/* ChibiOS - Copyright (C) 2006..2017 Giovanni Di Sirio Copyright (C) 2015..2019 Diego Ismirlian, (dismirlian(at)google's mail) Licensed 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 HAL_USBH_H_ #define HAL_USBH_H_ #include "hal.h" #ifndef HAL_USE_USBH #define HAL_USE_USBH FALSE #endif #ifndef HAL_USBH_USE_FTDI #define HAL_USBH_USE_FTDI FALSE #endif #ifndef HAL_USBH_USE_HUB #define HAL_USBH_USE_HUB FALSE #endif #ifndef HAL_USBH_USE_MSD #define HAL_USBH_USE_MSD FALSE #endif #ifndef HAL_USBH_USE_UVC #define HAL_USBH_USE_UVC FALSE #endif #ifndef HAL_USBH_USE_AOA #define HAL_USBH_USE_AOA FALSE #endif #ifndef HAL_USBH_USE_HID #define HAL_USBH_USE_HID FALSE #endif #ifndef HAL_USBH_USE_ADDITIONAL_CLASS_DRIVERS #define HAL_USBH_USE_ADDITIONAL_CLASS_DRIVERS FALSE #endif #ifndef HAL_USBH_USE_IAD #define HAL_USBH_USE_IAD HAL_USBH_USE_UVC #endif #if (HAL_USE_USBH == TRUE) || defined(__DOXYGEN__) #include "osal.h" #include "usbh/list.h" #include "usbh/defs.h" /*===========================================================================*/ /* Derived constants and error checks. */ /*===========================================================================*/ #if !HAL_USBH_USE_HUB #define USBH_MAX_ADDRESSES 1 #else #define USBH_MAX_ADDRESSES (HAL_USBHHUB_MAX_PORTS + 1) #endif enum usbh_status { USBH_STATUS_STOPPED = 0, USBH_STATUS_STARTED, USBH_STATUS_SUSPENDED, }; /* These correspond to the USB spec */ enum usbh_devstatus { USBH_DEVSTATUS_DISCONNECTED = 0, USBH_DEVSTATUS_ATTACHED, USBH_DEVSTATUS_CONNECTED, USBH_DEVSTATUS_DEFAULT, USBH_DEVSTATUS_ADDRESS, USBH_DEVSTATUS_CONFIGURED, }; enum usbh_devspeed { USBH_DEVSPEED_LOW = 0, USBH_DEVSPEED_FULL, USBH_DEVSPEED_HIGH, }; enum usbh_epdir { USBH_EPDIR_IN = 0x80, USBH_EPDIR_OUT = 0 }; enum usbh_eptype { USBH_EPTYPE_CTRL = 0, USBH_EPTYPE_ISO = 1, USBH_EPTYPE_BULK = 2, USBH_EPTYPE_INT = 3, }; enum usbh_epstatus { USBH_EPSTATUS_UNINITIALIZED = 0, USBH_EPSTATUS_CLOSED, USBH_EPSTATUS_OPEN, USBH_EPSTATUS_HALTED, }; enum usbh_urbstatus { USBH_URBSTATUS_UNINITIALIZED = 0, USBH_URBSTATUS_INITIALIZED, USBH_URBSTATUS_PENDING, USBH_URBSTATUS_ERROR, USBH_URBSTATUS_TIMEOUT, USBH_URBSTATUS_CANCELLED, USBH_URBSTATUS_STALL, USBH_URBSTATUS_DISCONNECTED, USBH_URBSTATUS_OK, }; /*===========================================================================*/ /* Driver data structures and types. */ /*===========================================================================*/ /* forward declarations */ typedef struct USBHDriver USBHDriver; typedef struct usbh_port usbh_port_t; typedef struct usbh_device usbh_device_t; typedef struct usbh_ep usbh_ep_t; typedef struct usbh_urb usbh_urb_t; typedef struct usbh_baseclassdriver usbh_baseclassdriver_t; typedef struct usbh_classdriverinfo usbh_classdriverinfo_t; #if HAL_USBH_USE_HUB typedef struct USBHHubDriver USBHHubDriver; #endif /* typedefs */ typedef enum usbh_status usbh_status_t; typedef enum usbh_devspeed usbh_devspeed_t; typedef enum usbh_devstatus usbh_devstatus_t; typedef enum usbh_epdir usbh_epdir_t; typedef enum usbh_eptype usbh_eptype_t; typedef enum usbh_epstatus usbh_epstatus_t; typedef enum usbh_urbstatus usbh_urbstatus_t; typedef uint16_t usbh_portstatus_t; typedef uint16_t usbh_portcstatus_t; typedef void (*usbh_completion_cb)(usbh_urb_t *); /* include the low level driver; the required definitions are above */ #include "hal_usbh_lld.h" #define USBH_DEFINE_BUFFER(var) USBH_LLD_DEFINE_BUFFER(var) #define USBH_DECLARE_STRUCT_MEMBER(member) USBH_LLD_DECLARE_STRUCT_MEMBER(member) struct usbh_urb { usbh_ep_t *ep; void *userData; usbh_completion_cb callback; const void *setup_buff; void *buff; uint32_t requestedLength; uint32_t actualLength; usbh_urbstatus_t status; thread_reference_t waitingThread; thread_reference_t abortingThread; /* Low level part */ _usbh_urb_ll_data }; struct usbh_ep { usbh_device_t *device; usbh_ep_t *next; usbh_epstatus_t status; uint8_t address; bool in; usbh_eptype_t type; uint16_t wMaxPacketSize; uint8_t bInterval; /* debug */ const char *name; /* Low-level part */ _usbh_ep_ll_data }; struct usbh_device { USBHDriver *host; /* shortcut to host */ usbh_ep_t ctrl; usbh_ep_t *endpoints; usbh_baseclassdriver_t *drivers; uint16_t langID0; usbh_devstatus_t status; usbh_devspeed_t speed; USBH_DECLARE_STRUCT_MEMBER(usbh_device_descriptor_t devDesc); USBH_DECLARE_STRUCT_MEMBER(usbh_config_descriptor_t basicConfigDesc); uint8_t *fullConfigurationDescriptor; uint8_t keepFullCfgDesc; uint8_t address; uint8_t bConfiguration; /* Low level part */ _usbh_device_ll_data }; struct usbh_port { #if HAL_USBH_USE_HUB USBHHubDriver *hub; #endif usbh_portstatus_t status; usbh_portcstatus_t c_status; usbh_port_t *next; uint8_t number; usbh_device_t device; /* Low level part */ _usbh_port_ll_data }; #if USBH_DEBUG_ENABLE struct usbh_dq { int rem; int sz; uint8_t *first; uint8_t *next; uint8_t *start; uint8_t *end; }; typedef struct usbh_dq usbh_dq_t; struct usbh_debug_helper { uint8_t buff[USBH_DEBUG_BUFFER]; THD_WORKING_AREA(thd_wa, 512); usbh_dq_t dq; systime_t first; systime_t last; bool ena; bool on; thread_reference_t tr; }; #endif struct USBHDriver { usbh_status_t status; uint8_t address_bitmap[(USBH_MAX_ADDRESSES + 7) / 8]; usbh_port_t rootport; #if HAL_USBH_USE_HUB struct list_head hubs; #endif /* Low level part */ _usbhdriver_ll_data #if USBH_DEBUG_ENABLE && USBH_DEBUG_MULTI_HOST /* debug */ struct usbh_debug_helper debug; #endif }; #if USBH_DEBUG_ENABLE && !USBH_DEBUG_MULTI_HOST /* debug */ extern struct usbh_debug_helper usbh_debug; #endif /*===========================================================================*/ /* External declarations. */ /*===========================================================================*/ /*===========================================================================*/ /* Main driver API. */ /*===========================================================================*/ #ifdef __cplusplus extern "C" { #endif /* Main functions */ void usbhObjectInit(USBHDriver *usbh); void usbhInit(void); void usbhStart(USBHDriver *usbh); void usbhStop(USBHDriver *usbh); void usbhSuspend(USBHDriver *usbh); void usbhResume(USBHDriver *usbh); /* Device-related */ #if USBH_DEBUG_ENABLE && USBH_DEBUG_ENABLE_INFO void usbhDevicePrintInfo(usbh_device_t *dev); void usbhDevicePrintConfiguration(const usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem); #else # define usbhDevicePrintInfo(dev) do {} while(0) # define usbhDevicePrintConfiguration(dev, descriptor, rem) do {} while(0) #endif bool usbhDeviceReadString(usbh_device_t *dev, char *dest, uint8_t size, uint8_t index, uint16_t langID); static inline usbh_port_t *usbhDeviceGetPort(usbh_device_t *dev) { return container_of(dev, usbh_port_t, device); } /* Synchronous API */ usbh_urbstatus_t usbhSynchronousTransfer(usbh_ep_t *ep, void *data, uint32_t len, uint32_t *actual_len, systime_t timeout); static inline usbh_urbstatus_t usbhBulkTransfer(usbh_ep_t *ep, void *data, uint32_t len, uint32_t *actual_len, systime_t timeout) { osalDbgAssert(ep->type == USBH_EPTYPE_BULK, "wrong ep"); return usbhSynchronousTransfer(ep, data, len, actual_len, timeout); } usbh_urbstatus_t usbhControlRequest(usbh_device_t *dev, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength, uint8_t *buff); usbh_urbstatus_t usbhControlRequestExtended(usbh_device_t *dev, const usbh_control_request_t *req, uint8_t *buff, uint32_t *actual_len, systime_t timeout); /* Standard request helpers */ bool usbhStdReqGetDeviceDescriptor(usbh_device_t *dev, uint16_t wLength, uint8_t *buf); bool usbhStdReqGetConfigurationDescriptor(usbh_device_t *dev, uint8_t index, uint16_t wLength, uint8_t *buf); bool usbhStdReqGetStringDescriptor(usbh_device_t *dev, uint8_t index, uint16_t langID, uint16_t wLength, uint8_t *buf); bool usbhStdReqSetInterface(usbh_device_t *dev, uint8_t bInterfaceNumber, uint8_t bAlternateSetting); bool usbhStdReqGetInterface(usbh_device_t *dev, uint8_t bInterfaceNumber, uint8_t *bAlternateSetting); /* Endpoint/pipe management */ void usbhEPObjectInit(usbh_ep_t *ep, usbh_device_t *dev, const usbh_endpoint_descriptor_t *desc); static inline void usbhEPOpen(usbh_ep_t *ep) { osalDbgCheck(ep != 0); osalSysLock(); osalDbgAssert(ep->status == USBH_EPSTATUS_CLOSED, "invalid state"); usbh_lld_ep_open(ep); ep->next = ep->device->endpoints; ep->device->endpoints = ep; osalSysUnlock(); } static inline void usbhEPCloseS(usbh_ep_t *ep) { osalDbgCheck(ep != 0); osalDbgCheckClassS(); osalDbgAssert(ep->status != USBH_EPSTATUS_UNINITIALIZED, "invalid state"); if (ep->status == USBH_EPSTATUS_CLOSED) return; usbh_lld_ep_close(ep); } static inline void usbhEPClose(usbh_ep_t *ep) { osalSysLock(); usbhEPCloseS(ep); osalSysUnlock(); } bool usbhEPReset(usbh_ep_t *ep); static inline bool usbhEPIsPeriodic(usbh_ep_t *ep) { osalDbgCheck(ep != NULL); return (ep->type & 1) != 0; } static inline bool usbhURBIsBusy(usbh_urb_t *urb) { osalDbgCheck(urb != NULL); return (urb->status == USBH_URBSTATUS_PENDING); } static inline void usbhEPSetName(usbh_ep_t *ep, const char *name) { ep->name = name; } /* URB management */ void usbhURBObjectInit(usbh_urb_t *urb, usbh_ep_t *ep, usbh_completion_cb callback, void *user, void *buff, uint32_t len); void usbhURBObjectResetI(usbh_urb_t *urb); void usbhURBSubmitI(usbh_urb_t *urb); bool usbhURBCancelI(usbh_urb_t *urb); msg_t usbhURBSubmitAndWaitS(usbh_urb_t *urb, systime_t timeout); void usbhURBCancelAndWaitS(usbh_urb_t *urb); msg_t usbhURBWaitTimeoutS(usbh_urb_t *urb, systime_t timeout); static inline void usbhURBSubmit(usbh_urb_t *urb) { osalSysLock(); usbhURBSubmitI(urb); osalOsRescheduleS(); osalSysUnlock(); } static inline bool usbhURBCancel(usbh_urb_t *urb) { bool ret; osalSysLock(); ret = usbhURBCancelI(urb); osalOsRescheduleS(); osalSysUnlock(); return ret; } /* Main loop */ void usbhMainLoop(USBHDriver *usbh); #ifdef __cplusplus } #endif /*===========================================================================*/ /* Class driver definitions and API. */ /*===========================================================================*/ typedef struct usbh_classdriver_vmt usbh_classdriver_vmt_t; struct usbh_classdriver_vmt { void (*init)(void); usbh_baseclassdriver_t *(*load)(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem); void (*unload)(usbh_baseclassdriver_t *drv); /* TODO: add power control, suspend, etc */ }; struct usbh_classdriverinfo { const char *name; const usbh_classdriver_vmt_t *vmt; }; #define _usbh_base_classdriver_data \ const usbh_classdriverinfo_t *info; \ usbh_device_t *dev; \ usbh_baseclassdriver_t *next; struct usbh_baseclassdriver { _usbh_base_classdriver_data }; #endif #endif /* HAL_USBH_H_ */