General Information
Model
No particular memory interface is required by µC/USB-Device's driver model. Therefore, the USB device controller may use the assistance of a Direct Memory Access (DMA) controller to transfer data or handle the data transfers directly.
API
All device drivers must declare an instance of the appropriate device driver API structure as a global variable within the source code. The API structure is an ordered list of function pointers utilized by µC/USB-Device when device hardware services are required.
A sample device driver API structure is shown below.
const USBD_DRV_API USBD_DrvAPI_<controller> = { USBD_DrvInit, (1) USBD_DrvStart, (2) USBD_DrvStop, (3) USBD_DrvAddrSet, (4) USBD_DrvAddrEn, (5) USBD_DrvCfgSet, (6) USBD_DrvCfgClr, (7) USBD_DrvGetFrameNbr, (8) USBD_DrvEP_Open, (9) USBD_DrvEP_Close, (10) USBD_DrvEP_RxStart, (11) USBD_DrvEP_Rx, (12) USBD_DrvEP_RxZLP, (13) USBD_DrvEP_Tx, (14) USBD_DrvEP_TxStart, (15) USBD_DrvEP_TxZLP, (16) USBD_DrvEP_Abort, (17) USBD_DrvEP_Stall, (18) USBD_DrvISR_Handler (19) };
(1) Device initialization/add
(2) Device start
(3) Device stop
(4) Assign device address
(5) Enable device address
(6) Set device configuration
(7) Clear device configuration
(8) Retrieve frame number
(9) Open device endpoint
(10) Close device endpoint
(11) Configure device endpoint to receive data
(12) Receive from device endpoint
(13) Receive zero-length packet from device endpoint
(14) Configure device endpoint to transmit data
(15) Transmit to device endpoint
(16) Transmit zero-length packet to device endpoint
(17) Abort device endpoint transfer
(18) Stall device endpoint
(19) Device interrupt service routine (ISR) handler
Some non-essential functions can also be declared as null pointers. The functions that can be declared as null pointers are: AddrSet()
, AddrEn()
, CfgSet()
, CfgClr()
and FrameNbrGet()
. Please note that while these functions are not essential for the core to work properly, the USB device driver used may require some or all of them to work correctly.
The Listing - Device Driver Interface API with Null Pointers shows a sample API structure with only the mandatory functions declared.
const USBD_DRV_API USBD_DrvAPI_<controller> = { USBD_DrvInit, USBD_DrvStart, USBD_DrvStop, DEF_NULL, DEF_NULL, DEF_NULL, DEF_NULL, DEF_NULL, USBD_DrvEP_Open, USBD_DrvEP_Close, USBD_DrvEP_RxStart, USBD_DrvEP_Rx, USBD_DrvEP_RxZLP, USBD_DrvEP_Tx, USBD_DrvEP_TxStart, USBD_DrvEP_TxZLP, USBD_DrvEP_Abort, USBD_DrvEP_Stall, USBD_DrvISR_Handler };
The details of each device driver API function are described in the Device Controller Driver API Reference.
It is the device driver developers’ responsibility to ensure that the required functions listed within the API are properly implemented and that the order of the functions within the API structure is correct.
µC/USB-Device device driver API function names may not be unique. Name clashes between device drivers are avoided by never globally prototyping device driver functions and ensuring that all references to functions within the driver are obtained by pointers within the API structure. The developer may arbitrarily name the functions within the source file so long as the API structure is properly declared. The user application should never need to call API functions. Unless special care is taken, calling device driver functions may lead to unpredictable results due to reentrancy.
When writing your own device driver, you can assume that each driver API function accepts a pointer to a structure of the type USBD_DRV
as one of its parameters. Through this structure, you will be able to access the following fields:
typedef struct usbd_drv USBD_DRV; typedef usb_drv { CPU_INT08U DevNbr; (1) USBD_DRV_API *API_Ptr; (2) USBD_DRV_CFG *CfgPtr; (3) void *DataPtr; (4) USBD_DRV_BSP_API *BSP_API_Ptr; (5) };
(1) Unique index to identify device.
(2) Pointer to USB device controller driver API.
(3) Pointer to USB device controller driver configuration.
(4) Pointer to USB device controller driver specific data.
(5) Pointer to USB device controller BSP.
Memory Allocation
Memory allocation in the driver can be simplified by the use of memory allocation functions available from Micrium’s µC/LIB module. µC/LIB’s memory allocation functions provide allocation of memory from dedicated memory space (e.g., USB RAM) or general purpose heap. The driver may use the pool functionality offered by µC/LIB. Memory pools use fixed-sized blocks that can be dynamically allocated and freed during application execution. Memory pools may be convenient to manage objects needed by the driver. The objects could be for instance data structures mandatory for DMA operations. For more information on using µC/LIB memory allocation functions, consult the µC/LIB documentation.
CPU and Board Support
In order for device drivers to be platform-independent, it is necessary to provide a layer of code that abstracts details such as clocks, interrupt controllers, input/output (I/O) pins, and other hardware modules configuration. With this board support package (BSP) code layer, it is possible for the majority of the USB device stack to be independent of any specific hardware, and for device drivers to be reused on different architectures and bus configurations without the need to modify stack or driver source code. These procedures are also referred as the USB BSP for a particular development board.
A sample device BSP interface API structure is shown below.
const USBD_DRV_BSP_API USBD_DrvBSP_<controller> = { USBD_BSP_Init, (1) USBD_BSP_Conn, (2) USBD_BSP_Disconn (3) };
(1) Device BSP initialization function pointer
(2) Device BSP connect function pointer
(3) Device BSP disconnect function pointer
The details of each device BSP API function are described in the Device Driver BSP Functions Reference.