Developing a new USB host application
Background
In the USB system, the host software controls the bus and talks to the target devices following the rules defined by the specification. A device is represented by a configuration that is a collection of one or more interfaces. Each interface comprises one or more endpoints. Each endpoint is represented as a logical pipe from the application software perspective.
The host application software registers a callback with the USB host stack, which notifies the application about the device attach/detach events and determines whether the device is supported or not. The following figure shows the enumeration and detachment flow.
|
|
The USB host stack is a few lines of code executed before starting communication with the USB device. The examples on the USB stack are written with class driver APIs. Class drivers work with the host API as a supplement to the functionality. They make it easy to achieve the target functionality (see example sources for details) without dealing with the implementation of standard routines. The following code steps are taken inside a host application driver for any specific device.
Parent topic:Developing a new USB host application
How to develop a new host application
Creating a project
Perform the following steps to create a project.
Create a new application directory under
<install_dir>/boards/<board>/usb_examples/usb_host_<class>_<application>
to locate the application source files and header files. For example,<install_dir>/boards/<board>/usb_examples/usb_host_hid_mouse
.Copy the following files from the similar existing applications to the application directory that is created in step 1.
app.c
usb_host_config.h
The app.c file contains the common initialization code for USB host and the usb_host_config.h file contains the configuration MACROs for the USB host.
Copy the bm directory from the similar existing application directory to the new application directory. Remove the unused project directory from the bm directory. Modify the project directory name to the new application project name. For example,
to create toolchain-IAR, board-frdmk64 class-hid related application
, create the new applicationhid_test
based on a similar existing applicationhid_mouse
.Copy
<install_dir>/boards/frdmk64f/usb_examples/usb_host_hid_mouse/bm
to
<install_dir>/boards/frdmk64f/usb_examples/usb_host_hid_test/bm
Modify the project file name to the new application project file name, for example, from
host_hid_mouse_bm.ewp
tohost_hid_test_bm.ewp
. Globally replace the existing name to the new project name by editing the project files. Thehost_hid_test_bm.ewp
file includes the new application project setting.Create a new source file to implement the main application function, application task function, and the callback function. The name of this file is similar to the new application name, such as
host_mouse.c
andhost_keyboard.c
.
The following sections describe the steps to modify application files created in the steps above to match the new application.
Parent topic:How to develop a new host application
Main application function flow
In the main application function, follow these steps:
|
|
Initialize the USB clock.
Call the MCUXpresso SDK API to initialize the KHCI, the EHCI USB clock, or other controller.
Initialize the host controller.
This allows the stack to initialize the necessary memory required to run the stack and register the callback function to the stack.
For example:
status = USB_HostInit(CONTROLLER_ID, &g_HostHandle, USB_HostEvent);
Enable the USB ISR.
Set the USB interrupt priority and enable the USB interrupt.
Initialize the host stack task and application task.
For example (Bare metal):
while (1) { USB_HostTaskFn(g_HostHandle); USB_HostMsdTask(&g_MsdCommandInstance);
Note that in this code, the g_MsdCommandInstance variable contains all states and pointers used by the application to control or operate the device. If implementing the application task as USB_HostHidTestTask and use g_HidTestInstance to maintain the application states, modify the code as follows:
while (1) { USB_HostTaskFn(g_HostHandle); USB_HostHidTestTask(&g_HidTestInstance); }
Parent topic:How to develop a new host application
Event callback function
In the app.c file, there is one USB_HostEvent
function. By default, the function is registered to the host stack when calling the USB_HostInit
. In the USB Host stack, customers do not have to write any enumeration code. When the device is connected to the host controller, the USB Host stack enumerates the device. The device attach/detach events are notified by this callback function.
Application needs to implement one or more functions to correspond to one class process. These application functions are called in the USB_HostEvent
. The device’s configuration handle and interface list are passed to the application through the function so that the application can determine whether the device is supported by this application.
There are four events in the callback: kUSB_HostEventAttach, kUSB_HostEventNotSupported, kUSB_HostEventEnumerationDone, and kUSB_HostEventDetach
.
The events occur as follows:
When one device is attached, host stack notifies
kUSB_HostEventAttach
.The application returns
kStatus_USB_Success
to notify the host stack that the device configuration is supported by this class application, or return thekStatus_USB_NotSupported
to notify the host stack that the device configuration is not supported by this class application.The Host stack continues for enumeration if the device is supported by the application and notifies
kUSB_HostEventEnumerationDone
when the enumeration is done.The Host stack checks the next device’s configuration if the current configuration is not supported by the application.
When the Host stack checks all configurations and all are not supported by the application, it notifies the
kUSB_HostEventNotSupported
.When the device detaches, the Host stack notifies the
kUSB_HostEventDetach
.
This is the sample code for the HID mouse application. The USB_HostHidMouseEvent
function should be called bythe USB_HostEvent
. In this code, the g_HostHidMouse
variable contains all states and pointers used by the application to control or operate the device:
usb_status_t USB_HostHidMouseEvent
(
usb_device_handle deviceHandle,
usb_host_configuration_handle configurationHandle,
uint32_t eventCode
)
{
/* Process the same and supported device's configuration handle */
static usb_host_configuration_handle s_ConfigHandle = NULL;
usb_status_t status = kStatus_USB_Success;
uint8_t id;
usb_host_configuration_t *configuration;
uint8_t interfaceIndex;
usb_host_interface_t *interface;
switch (eventCode)
{
case kUSB_HostEventAttach:
/* judge whether is configurationHandle supported */
configuration = (usb_host_configuration_t *)configurationHandle;
for (interfaceIndex = 0; interfaceIndex < configuration->interfaceCount; ++interfaceIndex)
{
interface = &configuration->interfaceList[interfaceIndex];
id = interface->interfaceDesc->bInterfaceClass;
if (id != USB_HOST_HID_CLASS_CODE)
{
continue;
}
id = interface->interfaceDesc->bInterfaceSubClass;
if ((id != USB_HOST_HID_SUBCLASS_CODE_NONE) && (id != USB_HOST_HID_SUBCLASS_CODE_BOOT))
{
continue;
}
id = interface->interfaceDesc->bInterfaceProtocol;
if (id != USB_HOST_HID_PROTOCOL_MOUSE)
{
continue;
}
else
{
/* the interface is supported by the application */
g_HostHidMouse.deviceHandle = deviceHandle;
g_HostHidMouse.interfaceHandle = interface;
s_ConfigHandle = configurationHandle;
return kStatus_USB_Success;
}
}
status = kStatus_USB_NotSupported;
break;
case kUSB_HostEventNotSupported:
break;
case kUSB_HostEventEnumerationDone:
if (s_ConfigHandle == configurationHandle)
{
if ((g_HostHidMouse.deviceHandle != NULL) && (g_HostHidMouse.interfaceHandle != NULL))
{
/* the device enumeration is done */
if (g_HostHidMouse.deviceState == kStatus_DEV_Idle)
{
g_HostHidMouse.deviceState = kStatus_DEV_Attached;
}
else
{
usb_echo("not idle mouse instance\r\n");
}
}
}
break;
case kUSB_HostEventDetach:
if (s_ConfigHandle == configurationHandle)
{
/* the device is detached */
s_ConfigHandle = NULL;
if (g_HostHidMouse.deviceState != kStatus_DEV_Idle)
{
g_HostHidMouse.deviceState = kStatus_DEV_Detached;
}
}
break;
default:
break;
}
return status;
}
If implementing the callback as USB_HostHidTestEvent
, use g_HidTestInstance
, and support the device that the class code is USB_HOST_HID_TEST_CLASS_CODE, sub-class code is USB_HOST_HID_TEST_SUBCLASS_CODE, and the protocol is USB_HOST_HID_TEST_PROTOCOL. The code can be modified as follows:
usb_status_t USB_HostHidMouseEvent
(
usb_device_handle deviceHandle,
usb_host_configuration_handle configurationHandle,
uint32_t eventCode
)
{
/* Process the same and supported device's configuration handle */
static usb_host_configuration_handle s_ConfigHandle = NULL;
usb_status_t status = kStatus_USB_Success;
uint8_t id;
usb_host_configuration_t *configuration;
uint8_t interfaceIndex;
usb_host_interface_t *interface;
switch (eventCode)
{
case kUSB_HostEventAttach:
/* judge whether is configurationHandle supported */
configuration = (usb_host_configuration_t *)configurationHandle;
for (interfaceIndex = 0; interfaceIndex < configuration->interfaceCount; ++interfaceIndex)
{
interface = &configuration->interfaceList[interfaceIndex];
id = interface->interfaceDesc->bInterfaceClass;
if (id != USB_HOST_HID_TEST_CLASS_CODE)
{
continue;
}
id = interface->interfaceDesc->bInterfaceSubClass;
if (id != USB_HOST_HID_TEST_SUBCLASS_CODE)
{
continue;
}
id = interface->interfaceDesc->bInterfaceProtocol;
if (id != USB_HOST_HID_TEST_PROTOCOL)
{
continue;
}
else
{
/* the interface is supported by the application */
g_HidTestInstance.deviceHandle = deviceHandle;
g_HidTestInstance.interfaceHandle = interface;
s_ConfigHandle = configurationHandle;
return kStatus_USB_Success;
}
}
status = kStatus_USB_NotSupported;
break;
case kUSB_HostEventNotSupported:
break;
case kUSB_HostEventEnumerationDone:
if (s_ConfigHandle == configurationHandle)
{
if ((g_HidTestInstance.deviceHandle != NULL) && (g_HidTestInstance.interfaceHandle != NULL))
{
/* the device enumeration is done */
if (g_HidTestInstance.deviceState == kStatus_DEV_Idle)
{
g_HidTestInstance.deviceState = kStatus_DEV_Attached;
}
else
{
usb_echo("not idle mouse instance\r\n");
}
}
}
break;
case kUSB_HostEventDetach:
if (s_ConfigHandle == configurationHandle)
{
/* the device is detached */
s_ConfigHandle = NULL;
if (g_HidTestInstance.deviceState != kStatus_DEV_Idle)
{
g_HidTestInstance.deviceState = kStatus_DEV_Detached;
}
}
break;
default:
break;
}
return status;
}
Note that the kStatus_DEV_Attached
, kStatus_DEV_Detached
MACROs are defined in the example.
Parent topic:How to develop a new host application
Class initialization
When the supported device is attached, the device’s class needs to be initialized.
For example, the HID mouse initialization flow is as follows:
|
|
Call class initialization function to initialize the class instance.
Call class set interface function to set the class interface
When the set interface callback returns successfully, the application can run.
Parent topic:How to develop a new host application
Sending/Receiving data to/from the device
The transfer flow is as follows:
Call the
USB_hostClassxxx
API to begin the transfer.The transfer result is notified by the callback function that is passed as a parameter.
The HID mouse host uses the following code to receive data from the device:
USB_HostHidRecv(classHandle, mouseBuffer, bufferLength, callbackFunction, callbackParameter);
Parent topic:How to develop a new host application
Parent topic:Developing a new USB host application
Parent topic:Developing a new USB application