Name
Peripheral Controller Drivers — Structure and Interface
Description
This section is mainly of interest to developers who want to write a new peripheral controller driver. It describes the interface used by the USB stack to initiate PCD operations and the API that a PCD can use to interact with the USB stack.
PCD Objects
The main interface between the USB stack and each type of PCD is the usb_pcd object:
struct usb_pcd { const char *name; // Driver name int pad; // Initialization etc. void (*init)( void ); // Initialize controller(s) int (*attach)( usb_pcdi *pcdi, usb_target *tgt ); // Attach to hardware int (*detach)( usb_pcdi *pcdi, usb_target *tgt ); // Detach from hardware // Endpoint attach/detach int (*endpoint_attach)( usb_pcdi *pcdi, usb_target_endpoint *tep ); int (*endpoint_detach)( usb_pcdi *pcdi, usb_target_endpoint *tep ); // Set/clear endpoint stall int (*endpoint_stall)( usb_pcdi *pcdi, usb_target_endpoint *tep, int stall ); // Transfer handling int (*submit)( usb_target *tgt, usb_tfr *tfr ); // Submit transfer (chain) int (*cancel)( usb_target *tgt, usb_tfr *tfr ); // Cancel transfer // Controller operation void (*poll)( usb_pcdi *pcdi ); // Poll controller for events int (*set_address)( usb_pcdi *pcdi, usb_uint8 addr ); // Set new target address };
The fields are as follows:
- name
- This is a pointer to a string that names this device. It is mainly used for debugging.
- init
This is called once by the USB stack to initialize all PCDs of this type. In combination with platform code this function should enumerate all the PCDs of the supported type and eventually call
usb_pcdi_register()
to make the controller available to the USB stack.The call to
usb_pcdi_register()
is passed a usb_pcdi object that the PCD should allocate in its private data structures.While this function should locate the devices and initialize the PCD data structures it should not access the Peripheral Controller hardware at this point.
- attach
- This is called to attach the PCD to the hardware. This is when the hardware should be initialized, interrupt handlers registered and everything made ready for transfers to occur.
- detach
This is called to detach the PCD from the hardware. It should undo the initialization done by the attach function, leaving the device free for other software to take control.
The main reason for this attach/detach mechanism is to allow OTG devices to be shared between host and peripheral drivers.
- endpoint_attach
This is called to create an endpoint in the peripheral controller. The PCD should use the endpoint descriptor in the usb_target_endpoint object to create an endpoint of the correct type and direction for the device.
The PCD will typically allocate controller and driver data structures to represent this endpoint. If the underlying controller only supports a limited number of endpoints, then the driver should either fail excess endpoint attachments, or arrange to share the physical endpoints between a larger number of virtual endpoints. If the PCD endpoint is created successfully the it should assign a pointer to it to the
pcd
field in the usb_target_endpoint object.- endpoint_detach
-
This is called when the target is detached, or changes its
active interface. It undoes the resource allocation made in
endpoint_attach
. Additionally, this function must cancel any transfers that are pending on the endpoint. Depending on the nature of the controller, these transfer cancellations and the eventual deallocation of the endpoint may happen after this function returns. - submit
-
This is called to submit a transfer to the controller. Internally,
this function should extract the PCD private data from the
target
pcdi
field and the endpoint from the target's usb_target_endpoint object for the transfer's endpoint address. The PCD is free to use thehcd_endpoint
andhcd_list
fields in the usb_tfr object; the latter should be initialized before use. - cancel
-
This is called to cancel a pending transfer. The transfer will
not necessarily be available for reuse once this function
returns. This is only guaranteed once the transfer's callback is
invoked, either with a
USB_TFR_CANCELLED
status, some other error, or evenUSB_OK
. - poll
This is called from the main USB handling loop to give the PCD the chance to service the hardware. Controller operations may be handled either in this function or in the ISR or DSR; however, callbacks must be made from this function. The PCD should test the hardware for transfer completion, device attach/detach and errors and handle them here.
If a transfer completes in this polling routine its callback may either be invoked directly by calling
usb_tfr_callback_pop()
or may be deferred for later processing by callingusb_tfr_complete_async()
. The latter is preferable since it avoids any problems of recursion if the callback submits another transfer.The simplest way to write an PCD is to do all device event handling in the
poll()
routine. If the controller supports interrupts then the PCD can callusb_signal_poll()
to cause the poll routine to be called. If it makes sense to handle device events in the ISR or DSR, callbacks, such as returning transfers, should still happen in the poll routine.- set_address
- This is called to set the address of the target in the peripheral controller. This is called after the host has sent a SET_ADDRESS command.
There is just one instance of the usb_pcd object for each type of peripheral controller. However, there may be more than one physical device of each type on the board. Each of these is represented by a usb_pcdi object:
struct usb_pcdi { usb_node node; // Link in PDCI list char *name; // Instance name usb_uint8 state; // Controller state usb_pcd *pcd; // Pointer to PCD usb_target *tgt; // Current attached target void *pcdi; // Driver private data };
- node
-
Node in list of active PCD instances. This need not be
initialized by the PCD, it is initialize by
usb_pcdi_register()
. - name
-
Name of this interface. This should distinguish it from all
other PCDIs, and is used by
usb_pcdi_find_by_name()
to locate this PCDI. This field must be initialized by the PCD before callingusb_pcdi_register()
. - pcd
-
A pointer to the usb_pcd object for the controlling
driver. This field must be initialized by the PCD before
calling
usb_pcdi_register()
. - tgt
-
When a target is attached to a PCDI by calling
usb_target_attach()
, a pointer to the target is placed here. This field should be initialized to NULL by the PCD before callingusb_pcdi_register()
. - pcdi
-
A pointer to a per-instance data structure in the PCD. This
field must be initialized by the PCD before calling
usb_pcdi_register()
.
2024-03-18 | eCosPro Non-Commercial Public License |