ETC USB_UCLINUX_DRIVER_V03

MCF5272 USB SW Developer Manual.
uClinux Device Driver for CBI &
Isochronous Transfers.
M5272/USB/UCLD/CBII
Rev. 0.3 05/2002
m
M
i
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
CONTENTS
Paragraph
1.
1.1.
1.2.
1.3.
2.
2.1.
2.2.
3.
3.1.
3.2.
3.3.
3.4.
4.
4.1.
4.2.
4.3.
4.4.
4.5.
5.
Title
Page
Introduction.........................................................................................1-1
Driver capabilities................................................................................................ 1-1
Related files.......................................................................................................... 1-2
Quick start Guide. ................................................................................................ 1-2
Driver Installation. ...............................................................................2-1
Compiling the Driver with uClinux kernel. ......................................................... 2-1
Compiling the Driver as a Module....................................................................... 2-2
Driver Interface....................................................................................3-1
IOCTL commands................................................................................................ 3-2
Read/Write operations.......................................................................................... 3-3
Asynchronous notification. .................................................................................. 3-4
Example of using read()/write()/ioctl() calls by Client. ...................................... 3-5
Driver Initialization. .............................................................................4-1
Initialization of Descriptor Pointers and Variables.............................................. 4-1
Initialization of Endpoints.................................................................................... 4-1
Initialization of Configuration RAM. .................................................................. 4-2
Initialization of FIFO Module.............................................................................. 4-2
Initialization of Interrupts. ................................................................................... 4-4
Control, Bulk, Interrupt Data Transfer.................................................5-1
5.1.
Device-to-Host Data Transfer.............................................................................. 5-1
5.1.1.
Initiating a Data Transfer. ............................................................................ 5-4
5.1.2.
Continuation of a Data Transfer................................................................... 5-7
5.1.3.
Completion of Data IN Transfer. ............................................................... 5-10
5.2.
Host-to-Device Data Transfer............................................................................ 5-13
5.2.1.
Initiating a Data Transfer. .......................................................................... 5-15
5.2.2.
Continuation of a Data Transfer................................................................. 5-18
5.2.3.
Completion of a Data OUT Transfer. ........................................................ 5-20
6.
Isochronous Data Transfer..................................................................6-1
ii
M
PRELIMINAR Y—SUBJECT TO CHANGE WITHOUT NOTICE
6.1.
6.2.
6.3.
6.4.
6.5.
6.6.
7.
Requests queue and buffer headers...................................................................... 6-1
Device-to-Host Data Transfer.............................................................................. 6-2
Monitoring the Host Software During IN Transfers............................................ 6-4
Host-to-Device Data Transfer.............................................................................. 6-9
Monitoring the Host Software During OUT Transfers...................................... 6-10
Monitoring the Device-side Application During OUT Transfers...................... 6-11
7.1.
7.2.
7.3.
7.4.
Vendor Request Handling. ..................................................................7-1
8.
Accepting a request from the Host....................................................................... 7-1
Data OUT request handling. ................................................................................ 7-2
Data IN request handling. .................................................................................... 7-3
No data stage request handling. ........................................................................... 7-5
8.1.
8.2.
8.3.
Miscellaneous Operations...................................................................8-1
9.
Port Reset Handling. ............................................................................................ 8-1
Change of Configuration Handling. ..................................................................... 8-2
Example of events handling in Client application. .............................................. 8-2
9.1.
9.2.
9.3.
9.4.
9.5.
9.6.
9.7.
9.8.
9.9.
9.10.
9.11.
9.12.
9.13.
9.14.
9.15.
9.16.
9.17.
9.18.
9.19.
9.20.
9.21.
9.22.
9.23.
9.24.
9.25.
USB Device Driver Function Specification. .........................................9-1
usb_bus_state_chg_service. ................................................................................. 9-1
usb_devcfg_service.............................................................................................. 9-2
usb_endpoint0_isr. ............................................................................................... 9-3
usb_endpoint_isr. ................................................................................................. 9-4
usb_ep_is_busy, USB_EP_BUSY ioctl command. ............................................. 9-5
USB_EP_STALL ioctl command........................................................................ 9-6
usb_ep_wait, USB_EP_WAIT ioctl command.................................................... 9-7
usb_fetch_command, USB_GET_COMMAND ioctl command......................... 9-8
usb_fifo_init. ........................................................................................................ 9-9
USB_GET_CURRENT_CONFIG ioctl command........................................ 9-10
USB_GET_FRAME_NUMBER ioctl command. ......................................... 9-11
usb_get_desc. ................................................................................................. 9-12
usb_get_request.............................................................................................. 9-13
usb_init, USB_INIT ioctl command. ............................................................. 9-14
usb_in_service................................................................................................ 9-15
usb_isochronous_transfer_service. ................................................................ 9-16
usb_isr_init..................................................................................................... 9-17
usb_make_power_of_two. ............................................................................. 9-18
usb_out_service.............................................................................................. 9-19
usb_rx_data. ................................................................................................... 9-20
USB_SEND_ZLP ioctl command. ................................................................ 9-21
USB_SET_FINAL_FRAME ioctl command. ............................................... 9-22
USB_SET_START_FRAME ioctl command. .............................................. 9-23
usb_sort_ep_array. ......................................................................................... 9-24
usb_tx_data. ................................................................................................... 9-25
iii
M
PRELIMINAR Y—SUBJECT TO CHANGE WITHOUT NOTICE
9.26.
9.27.
9.28.
10.
usb_vendreq_done. ........................................................................................ 9-26
usb_vendreq_service...................................................................................... 9-27
Interface functions.......................................................................................... 9-28
Appendix 1: File Transfer Application............................................ 10-1
10.1.
Introduction. ................................................................................................... 10-1
10.1.1. Important Notes.......................................................................................... 10-1
10.1.2. Capabilities of File Transfer Application................................................... 10-1
10.1.3. Related Files............................................................................................... 10-1
10.2.
UFTP Protocol Description............................................................................ 10-2
10.2.1. USB Usage................................................................................................. 10-2
10.2.2. Status Values.............................................................................................. 10-2
10.2.3. UFTP Command Descriptions................................................................... 10-3
10.2.3.1.
10.2.3.2.
10.2.3.3.
10.2.3.4.
10.2.3.5.
10.2.3.6.
UFTP_READ command: 01h........................................................................................... 10-3
UFTP_WRITE command: 02h. ........................................................................................ 10-3
UFTP_GET_FILE_INFO command: 03h........................................................................ 10-4
UFTP_GET_DIR command: 04h..................................................................................... 10-4
UFTP_SET_TRANSFER_LENGTH command: 05h..................................................... 10-5
UFTP_DELETE command: 06h....................................................................................... 10-6
10.3.
Implementation of File Transfer Application. ............................................... 10-7
10.3.1. Initializing the Driver................................................................................. 10-7
10.3.2. Program Execution..................................................................................... 10-8
10.3.2.1.
10.3.2.2.
10.3.2.3.
10.3.2.4.
10.3.2.5.
10.3.2.6.
10.3.2.7.
UFTP_READ command execution. ................................................................................. 10-9
UFTP_WRITE command execution. ............................................................................. 10-11
UFTP_GET_FILE_INFO command execution............................................................. 10-13
UFTP_GET_DIR command execution. ......................................................................... 10-13
UFTP_SET_TRANSFER_LENGTH command execution.......................................... 10-14
UFTP_DELETE command execution............................................................................ 10-14
Request for string descriptor handling. .......................................................................... 10-14
10.4.
USB File Transfer Application Function Specification. .............................. 10-17
10.4.1. accept_event............................................................................................. 10-19
10.4.2. do_command_delete. ............................................................................... 10-20
10.4.3. do_command_get_dir............................................................................... 10-21
10.4.4. do_command_get_file_info. .................................................................... 10-22
10.4.5. do_command_read................................................................................... 10-23
10.4.6. do_command_set_transfer_length. .......................................................... 10-24
10.4.7. do_command_write.................................................................................. 10-25
10.4.8. fetch_command........................................................................................ 10-26
10.4.9. get_string_descriptor................................................................................ 10-27
10.4.10.
read_file. .............................................................................................. 10-28
10.4.11.
write_file.............................................................................................. 10-29
11.
Appendix 2: Audio Application. ..................................................... 11-1
11.1.
Introduction. ................................................................................................... 11-1
11.1.1. Important Notes.......................................................................................... 11-1
11.1.2. Capabilities of the Audio Application. ...................................................... 11-1
11.1.3. Related Files............................................................................................... 11-1
iv
M
PRELIMINAR Y—SUBJECT TO CHANGE WITHOUT NOTICE
11.2.
Implementation of USB Audio Application .................................................. 11-2
11.2.1. USB Usage................................................................................................. 11-2
11.2.2. Initializing the Driver................................................................................. 11-2
11.2.3. Program Execution Flow. .......................................................................... 11-3
11.2.4. USB_AUDIO_START command execution. ............................................ 11-4
11.2.5. USB_AUDIO_STOP command execution. ............................................... 11-6
11.2.6. USB_AUDIO_SET_VOLUME command execution. .............................. 11-6
11.2.7. START_TEST_OUT_TRANSFER command execution. ........................ 11-7
11.2.8. START_TEST_IN_TRANSFER command execution. ............................ 11-7
11.2.9. START_TEST_INOUT_TRANSFER command execution. .................... 11-8
11.2.10.
Request for string descriptor handling. .................................................. 11-9
11.2.10.1.
11.2.10.2.
Memory layout for string descriptors .......................................................................... 11-9
Sending the string descriptor to the Host .................................................................. 11-11
11.3.
USB Audio Application Function Specification.......................................... 11-12
11.3.1. accept_event............................................................................................. 11-13
11.3.2. buffer_init................................................................................................. 11-14
11.3.3. clear_buffer .............................................................................................. 11-15
11.3.4. get_string_descriptor................................................................................ 11-16
11.3.5. init_audio_headers ................................................................................... 11-17
11.3.6. init_buffer_headers .................................................................................. 11-18
11.3.7. main_task ................................................................................................. 11-19
11.3.8. print_buffer_contents ............................................................................... 11-20
11.3.9. print_transfer_status................................................................................. 11-21
11.3.10.
process_data......................................................................................... 11-22
11.3.11.
test_case1_handler ............................................................................... 11-23
11.3.12.
test_case2_handler ............................................................................... 11-24
11.3.13.
test_case3_handler ............................................................................... 11-25
v
M
PRELIMINAR Y—SUBJECT TO CHANGE WITHOUT NOTICE
ILLUSTRATIONS
Figure
Title
Page
Fig 5-1. Stages of data transfer by the Driver.................................................................. 5-3
Fig 5-2. Algorithm of usb_tx_data() function. ................................................................ 5-5
Fig 5-3. Algorithm of usb_in_service() function. ............................................................ 5-9
Fig 5-4. The stages of receiving data by Driver............................................................. 5-14
Fig 5-5. Algorithm of usb_rx_data() function. .............................................................. 5-16
Fig 5-6. Algorithm of usb_out_service() function. ........................................................ 5-19
Fig 10-1. Memory layout for string descriptors........................................................... 10-15
Fig 11-1. Memory layout for string descriptors........................................................... 11-10
vi
M
PRELIMINAR Y—SUBJECT TO CHANGE WITHOUT NOTICE
About this document.
This document describes initialization and functionality of uClinux USB Device Driver
(CBI & Isochronous transfer types), and how to use it in user applications.
Audience.
This document targets uClinux software developers using the MCF5272 processor.
Suggested reading.
[1]
[2]
[3]
Universal Serial Bus 1.1 Specification.
MCF5272 ColdFire Integrated Microprocessor. User’s manual. Chapter 12.
Linux Device Drivers By Alessandro Rubini & Jonathan Corbet
Definitions, Acronyms, and Abbreviations.
The following list defines the acronyms and abbreviations used in this document.
CBI
EOP
EOT
FIFO
IMR
RAM
SOF
USB
ZLP
Control / Bulk / Interrupt
End of Packet
End of Transfer
Hardware on-chip First-In-First-Out buffer
Interrupt Mask Register
Random Access Memory
Start of Frame
Universal Serial Bus
Zero Length Packet
vii
M
PRELIMINAR Y—SUBJECT TO CHANGE WITHOUT NOTICE
1. Introduction.
This document describes a device-side uClinux USB Driver, developed for the MCF5272
processor. The document is divided logically into two parts. The first part describes the
functionality of the Driver. It covers data transferring to/from the Host, accepting vendor
specific commands, and describes how the Driver notifies the Client application about
such events as completion of transfer, reset, changing of configuration, etc. Each chapter
in the first part describes in full detail all routines, which perform some concrete
functionality, global structures and variables, explains how they work together as a
whole, and why it works in this way.
The second part (Chapter 9) is a specification of each function of the Driver. It gives a
short description of each function, it's arguments and returned values. Also, an example is
shown of the calling of each routine. Appendix 1 describes a File Transfer Application
example and Appendix 2 describes an Audio Application example.
1.1. Driver capabilities.
•
•
•
•
•
•
Compatible with uClinux kernels 2.0.x, 2.4.x. The Driver can be compiled for
2.0.x, 2.4.x kernel versions without any change in the Driver’s source code.
Can be compiled with uClinux kernel or can operate like a loadable kernel
module for dynamic Driver installation/removal.
Simultaneous data transfers on different endpoints. Thus, if transfers require
different endpoints, the Driver will handle these transfers independently and
simultaneously (the Driver does not wait until the transfer for some other endpoint
finishes; if required endpoint is free, it starts a new transfer immediately).
Transfer data in both directions on endpoint number zero in the same way as
for other endpoints. The Driver ONLY dedicates an endpoint number zero in
order to accept commands from the Host. The usual data transfers from the Host
to the Device and from the Device to the Host are available on endpoint number
zero.
Notifies Client application about reset and changing of configuration events and
command arrival, using asynchronous notification mechanism. It allows the
Client application to process a new command at any time, even while executing
another command.
During Isochronous IN/OUT transfers the Driver can perform (if device-side
Client application needs it) per-frame monitoring of the Host-side software,
when it is working in real-time.
If the Host s/w is not working in real-time i.e. misses frames (in some
frames does not send IN/OUT tokens), the Driver sustains the sample rate relative
to the device (it emulates sending of data to the Host) and notifies the device-side
Client application about missed frames by the Host s/w. Therefore, when the
Driver device-side s/w is still being synchronized with USB, and when sending of
M
Introduction.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
1-1
tokens is resumed, the Device will send not the old data but the actual data (for IN
transfers).
1.2. Related files.
The following files are relevant to Driver:
• usb.h – Driver’s interface definition. This file must be used by the Client
application.
• usb_defs.h – Driver’s functions, global constants and structures definitions.
• usb.c – implementation of Driver’s functions.
• descriptors.h – types definition for device, configuration, interface, endpoint,
and string descriptors. This file must be used by Client application.
• mcf5272.h – definition of some basic data types, macros for work with MCF5272
Registers.
1.3. Quick start Guide.
To start using the Driver by the Client application, the following steps must be
performed:
1) The Driver must be installed into uClinux (see Chapter 2).
2) Appropriate device file(s) must be opened (e.g. if the Client application uses
endpoints 0 and 1, it should open USB_EP0_FILE_NAME and USB_EP1_FILE_NAME
files (defined in usb.h)).
…
int usb_dev_file;
int usb_ep1_file;
…
usb_dev_file = open(USB_EP0_FILE_NAME, O_RDWR);
if (usb_dev_file < 0)
{
printf
("Can't
open
device
file:
USB_EP0_FILE_NAME);
exit(-1);
}
usb_ep1_file = open(USB_EP1_FILE_NAME, O_WRONLY);
if (usb_ep1_file < 0)
{
printf
("Can't
open
device
file:
USB_EP1_FILE_NAME);
close(usb_dev_file);
exit(-1);
}
%s\n",
%s\n",
3) The Client application can then enable asynchronous notification and set up a
handler for the SIGIO signal (refer to section 3.3 for details):
M
Introduction.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
1-2
struct sigaction act;
…
act.sa_handler = &accept_event;
act.sa_mask = 0;
act.sa_flags = 0;
sigaction(SIGIO, &act, NULL);
fcntl(usb_dev_file, F_SETOWN, getpid());
oflags = fcntl(usb_dev_file, F_GETFL);
fcntl(usb_dev_file, F_SETFL, oflags | FASYNC);
4) The Client application must initialize the Driver. It is accomplished by calling the
USB_INIT ioctl (which initializes the Driver). The Client application needs to fill
the DESC_INFO structure (defined in usb.h file):
extern USB_DEVICE_DESC Descriptors;
device_desc.pDescriptor = (uint8 *) &Descriptors;
device_desc.DescSize = usb_get_desc_size();
ioctl(usb_dev_file, USB_INIT, &device_desc);
An example of Descriptors definition can be found in the cbi_desc.c (or iso_desc.c)
file.
5) Now the Client application can use i/o functions (refer to section 3.4) and handle
commands to the device (refer to sections 7.1; 10.3.2).
M
Introduction.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
1-3
2. Driver Installation.
This chapter describes how to install the USB Device Driver for the uClinux system.
Depending on the task, the Driver can behave like a loadable kernel module or can be
compiled with the uClinux kernel. In order to make use of the first way, the uClinux
kernel must have loadable kernel modules support.
The installation process will be described for the uClinux-coldfire-2.0.38.1pre7-1
distribution from Lineo. For other uClinux distribution versions, the installation process
is very similar. It is assumed that a uClinux development environment is installed. All
directory names here are given relative to the uClinux top directory.
2.1. Compiling the Driver with uClinux kernel.
To compile the Driver with the uCLinux kernel and to startup with it, the following steps
must be accomplished:
1. Copy USB Device Driver’s source files (usb.c, usb.h, usb_defs.h,
descriptors.h, mcf5272.h) into the linux/Drivers/char directory.
2. Edit the following files:
linux/arch/m68knommu/config.in
Add the following lines to the file in the appropriate menu section (i.e. in the
program group the USB Driver is to show up in during 'make config' (for
example in the section ‘character Devices' after 'if [ "$CONFIG_COLDFIRE" =
"y" ]; then')):
bool 'MCF5272 USB support' CONFIG_COLDFIRE_USB
linux/Drivers/char/Makefile
Add the following lines to the file:
ifeq ($(CONFIG_COLDFIRE_USB),y)
L_OBJS += usb.o
endif
linux/Drivers/char/mem.c
Add the following lines to the file (e.g. in chr_dev_init() function):
#ifdef CONFIG_COLDFIRE_USB
init_usb();
#endif
M
Driver Installation.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
2-1
3. Create Device files. Depending on the version of the uClinux distribution, either
create them in 'romfs/dev/'directory by going to this directory and typing:
mknod
mknod
mknod
mknod
mknod
mknod
mknod
mknod
usb0
usb1
usb2
usb3
usb4
usb5
usb6
usb7
c
c
c
c
c
c
c
c
127
127
127
127
127
127
127
127
0
1
2
3
4
5
6
7
or copy from 'Devices/' directory of Driver's distribution tree to 'romfs/dev/'.
4. When doing 'make config' answer 'Yes' to the question about MCF5272 USB
support.
5. Do 'make dep'.
6. Do 'make'.
The USB Device Driver will be compiled with the uClinux kernel and included in
'image.bin'.
The image can easily be run from the "dBUG" monitor of the M5272C3 evaluation
board.
Load the image into the evaluation board. Network download may be used:
dn -i image.bin
To start running the image use the "go" command.
go 20000
Following this, uClinux will start and the USB Driver will be available for the Client
Application.
2.2. Compiling the Driver as a Module.
To compile the USB Device Driver as a module, the following steps must be
performed:
M
Driver Installation.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
2-2
1. Copy 'Makefile' file from 'module/' directory of Driver's distribution tree to
the location of the Driver's source files. Go to this location.
2. Edit 'Makefile'. Put the correct values for DEBUG, BASEDIR etc.
3. Type 'make'. This will compile the Driver and create 'usb.o' file.
4. Type 'make install' if 'usb.o' is to be placed in romfs.
5. Go to the location of uClinux distribution. Type 'make image' to update the
'image.bin' file with new romfs (if 'usb.o' was placed there).
6. Start uClinux. Type 'insmod usb.o' if 'usb.o' is located in romfs or
'insmod (some other place)/usb.o' if 'usb.o' is located is some
other place. This will load the USB Device Driver in memory and register it.
Now the Client Application will be able to use the Driver.
M
Driver Installation.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
2-3
3. Driver Interface.
This chapter describes the uClinux USB Device Driver’s interface and how to use the
Driver’s functionality with the Client application.
The USB Device Driver represents a character Device Driver. Character Devices are
accessed through Device files (or nodes) in the file system. The USB Device Driver (and
USB Device) is accessed through eight Device files (usb0 – usb7), which represent each
endpoint. These files are located in the /dev directory.
Each of the USB Device files has a major number 127 (the major number identifies the
Driver associated with the Device file). The minor number of the Device file coincides
with the corresponding endpoint number (the minor number is used by the Driver and is
used to monitor which endpoint is accessed, e.g. usb3 file corresponds to endpoint 3 and
has major number 127, minor number 3).
As per the above, each endpoint is represented by a separate Device. The kernel uses the
file_operations structure to access the Driver’s functions. Each field in the structure
points to the function in the Driver that implements a specific operation, or is NULL for
unsupported operations. In the USB Driver file_operations looks like the following:
struct file_operations Fops = {
NULL,
/* owner */
NULL,
/* seek */
usb_Device_read, /* read */
usb_Device_write, /* write */
NULL,
/* readdir */
NULL,
/* select */
usb_Device_ioctl,
/* ioctl */
NULL,
/* mmap */
usb_Device_open, /* open */
NULL,
/* flush */
usb_Device_release
/* close */
usb_Device_fasync /* fasync */
};
Thus, the USB Driver supports the following operations:
read – usb_Device_read is invoked when Client application reads from Device;
write – usb_Device_write is invoked when Client application writes to Device;
ioctl – usb_Device_ioctl is invoked to handle control I/O from Client application;
open – usb_Device_open is invoked when Client application opens the Device;
close – usb_Device_release is invoked when Client application closes the Device.
Also it uses an asynchronous notification mechanism to notify the Client about different
events.
M
Driver Interface.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
3-1
3.1. IOCTL commands.
The ioctl system call is used to control the USB Device Driver and to access it's
features, such as initializing the Device, changing operating modes, etc.
In the USB Driver the following ioctl commands are implemented:
USB_COMMAND_ACCEPTED – sends CMD_OVER as a response to vendor specific requests
to the Host.
USB_EP_BUSY – this command checks if the corresponding endpoint is busy. It takes one
parameter (endpoint number) and calls usb_ep_is_busy() (see Chapter 9.6).
USB_EP_STALL – stalls a given endpoint.
USB_EP_WAIT – this call does not return control while an endpoint is busy or while the
number of requests in the queue is more than specified in the parameter (for Isochronous
transfers only). It calls usb_ep_wait() (see Chapter 8.7). For CBI transfers, it returns
the number of bytes transferred during the last operation or a negative value in case of
error.
USB_GET_COMMAND – this call returns the last command. It takes one argument (the pointer
to
the
structure
where
the
Driver
must
place
the
command)
and
calls
usb_fetch_command().
USB_GET_CURRENT_CONFIG – this command returns current configuration and alternate
setting number.
USB_GET_FRAME_NUMBER – returns the current frame number. Implemented for CBI &
Isochronous Driver only.
USB_INIT – this command is intended for Driver initialization. It takes one argument (the
pointer to the structure that holds the address and size of the Device descriptor and
address of array of string descriptors) and calls usb_init() (see Chapter 4).
USB_NOT_SUPPORTED_COMMAND – sends CMD_OVER and CMD_ERROR as a response
to a vendor specific request to the Host (indicating that the received command is not
supported by the Device).
USB_SET_FINAL_FRAME – specifies the frame number from which Isochronous transfer
monitoring will be stopped. Implemented for CBI & Isochronous Driver only.
USB_SET_SEND_ZLP – this command sets the sendZLP variable to TRUE for the
corresponding endpoint (for more information about sendZLP see 5.1.3).
M
Driver Interface.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
3-2
USB_SET_START_FRAME – specifies the frame number from which Isochronous transfer
monitoring will begin. Implemented for CBI & Isochronous Driver only.
Some of the ioctl commands (such as USB_EP_BUSY, USB_EP_WAIT, USB_EP_STALL,
USB_SET_SEND_ZLP) may be called on any endpoint and affect this specified endpoint.
Others (such as USB_GET_CURRENT_CONFIG, USB_GET_COMMAND, USB_INIT,
USB_COMMAND_ACCEPTED, USB_NOT_SUPPORTED_COMMAND, ) can be called only on
endpoint 0 and affect the whole USB Driver or Device.
3.2. Read/Write operations.
The USB Device Driver performs only asynchronous read/write operations. This means
that the Client application only initiates a transfer by calling read() or write()
functions that return almost immediately, and only then can proceed with data processing.
To check if the transfer is finished, the Client application can call USB_EP_BUSY or
USB_EP_WAIT ioctl commands.
When the Client application calls the read() function, usb_rx_data() is invoked. It
returns only data that were located in the FIFO buffer. The next data transfer is performed
through the interrupt handler usb_out_service() (see Chapter 5.2 for details). The
interrupt handler writes data from the FIFO buffer directly to the application’s memory
space. This is the fastest way, but it will not work on systems using memory protection
(on the MCF5272 with uClinux it works correctly).
When the Client application calls the write() function, usb_tx_data() is invoked. It
writes only the amount of data equal to the FIFO free space. The next data transfer is
performed through the interrupt handler usb_in_service() (see Chapter 5.1 for details).
The interrupt handler reads data directly from the application’s memory space. This is the
fastest way, but it will not work on systems using memory protection (on the MCF5272
with uClinux it works correctly).
To determine the number of bytes transferred during the last operation (read or write), the
Client application needs to call USB_EP_WAIT ioctl. If the function read() or write() is
called on a busy endpoint, it returns the –EBUSY error.
M
Driver Interface.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
3-3
3.3. Asynchronous notification.
The Driver may use the asynchronous notification mechanism to notify the Client
application about the arrival of a new command, or other events (bus reset or
configuration change). This allows the Client application to process a new command (or
handle a configuration change event) at any time even while executing another command.
To use this feature, the Client application must first accomplish the following steps:
-
Specify a process as the “owner” of the file:
fcntl(ep0_file, F_SETOWN, getpid());
-
Set the FASYNC flag in the Device:
flags = fcntl(ep0_file, F_GETFL);
fcntl(ep0_file, F_SETFL, flags | FASYNC);
-
Set up SIGIO handler:
signal(SIGIO, &accept_event);
is better */
/* dummy sample; sigaction()
In the signal handler USB_GET_COMMAND , ioctl should be called to find out what
happened.
M
Driver Interface.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
3-4
3.4. Example of using read()/write()/ioctl() calls by
Client.
int usb_ep1;
char * buffer;
int size;
/* Open Device file */
usb_ep1 = open(USB_EP1_FILE_NAME, O_WRONLY);
(ep1) */
/* Bulk-in endpoint
if (usb_ep1 < 0)
{
printf ("Can't open Device file: %s\n", USB_EP1_FILE_NAME);
exit(-1);
}
….
write(usb_ep1, buffer, 100);
/* Write 100 bytes of data
from buffer to endpoint 1 */
size = ioctl(usb_ep1, USB_EP_WAIT); /* Wait (sleep) while transfer
is in progress */
….
M
Driver Interface.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
3-5
4. Driver Initialization.
This chapter describes step-by-step the initialization of the Driver. The initialization is
combined into one function – usb_init(), which is called from USB_INIT ioctl
command handler. Different parts of this function are described in separate subsections.
4.1. Initialization of Descriptor Pointers and
Variables.
Initialization of the Driver starts from initialization of it's global variable NewC (refer to
Chapter 6):
DEVICE_COMMAND * NewC = NULL;
To start work with the Driver, the Client application must call the USB_INIT ioctl
command. The only argument this call has is the pointer to structure that holds an address
and size of device descriptor. usb_init() fetches the addresses from the structure and
initializes global pointer: usb_device_descriptor (pointer to device descriptor):
usb_device_descriptor = descriptor_info -> pDescriptor;
Then, it initializes its local variables: PConfigRam – pointer to hardware on-chip
Configuration memory, pDevDesc – pointer to device descriptor, and DescSize – size of
device descriptor. The value of DescSize must be incremented by 3 (refer to Chapter
4.3).
4.2. Initialization of Endpoints.
Initialization of endpoints starts form initialization of endpoint number zero. The type of
transfer for that endpoint should be set to CONTROL (0). The size of packet is taken from
the device descriptor:
ep[0].packet_size
>bMaxPacketSize0;
=
((USB_DEVICE_DESC
*)pDevDesc)-
Length of the FIFO-buffer for this endpoint is equal to four maximum size packets
(FIFO_DEPTH is equal to 4):
ep[0].fifo_length = (uint16)(ep[0].packet_size * FIFO_DEPTH);
No buffer is allocated for endpoint number zero yet, so fields start, length, and
position should be cleared. The state of the endpoint is USB_CONFIGURED (according to
USB 1.1 specification, any transfers can be performed with an unconfigured device via
endpoint zero). It is not the same as the state of the device such as default,
M
Driver Initialization.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
4-1
addressed, or configured. This field indicates whether the endpoint is able to transmit
/ receive data or not.
The rest of the endpoints must be disabled:
for (i = 1; i < NUM_ENDPOINTS; i++)
ep[i].ttype = DISABLED;
4.3. Initialization of Configuration RAM.
To access the configuration RAM of the USB module, that memory must first be
disabled, otherwise an access error results. The Driver clears the CFG_RAM_VAL bit of USB
Endpoint 0 Control Register (EP0CTL) and disables the USB module:
MCF5272_WR_USB_EP0CTL(imm, 0);
Then, the configuration RAM is loaded with the descriptors:
for (i = 0; i < (DescSize/4); i++)
pConfigRam[i] = pDevDesc[i];
The configuration RAM is long-word accessible only. The compiler performs division by
4 as a right shift by 2. In order not to decrease the actual size of descriptors, 3 was added
to DescSize (refer to Chapter 4.1). Descriptors can be stored in configuration RAM in a
4 bytes format.
4.4. Initialization of FIFO Module.
The
initialization
of the FIFO module is combined into one function –
usb_fifo_init(). This function is also called from usb_devcfg_service() routine.
According the documentation for the MCF5272 USB Module, the following restrictions
apply:
• EPnCFG[FIFO_SIZE] must be a power of 2.
• EPnCFG[FIFO_ADDR] must be aligned to a boundary defined by the
EPnCFG[FIFO_SIZE] field.
• The FIFO space for an endpoint defined by FIFO_SIZE and FIFO_ADDR must
not overlap with the FIFO space for any other endpoint with the same direction.
In order to meet these restrictions, usb_fifo_init() allocates two arrays of pointers to
endpoints – one for IN endpoints, other – for OUT endpoints:
USB_EP_STATE *pIN[NUM_ENDPOINTS];
USB_EP_STATE *pOUT[NUM_ENDPOINTS];
M
Driver Initialization.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
4-2
Endpoint number zero is always present and bi-directional. Thus its address should be
stored in both arrays:
pIN[0] = &ep[0];
pOUT[0] = &ep[0];
nIN = nOUT = 1;
Then the function sorts the endpoints by direction and allocates them into two arrays:
for (i = 1; i < NUM_ENDPOINTS; i++)
{
if (ep[i].ttype != DISABLED)
{
if (ep[i].dir == IN)
pIN[nIN++] = &ep[i];
else
pOUT[nOUT++] = &ep[i];
}
}
For the first call of usb_fifo_init() (from usb_init()), all these endpoints are
disabled. Thus arrays pIN and pOUT contain address of endpoint number zero only.
Then it calls usb_make_power_of_two() passing the length of FIFO buffer for each
endpoint:
for (i = 0; i < nIN; i++)
usb_make_power_of_two(&(pIN[i]->fifo_length));
for (i = 0; i < nOUT; i++)
usb_make_power_of_two(&(pOUT[i]->fifo_length));
usb_make_power_of_two() finds nearest higher power of 2 and stores it into
fifo_length.
usb_fifo_init() then sorts endpoints (their addresses in arrays pIN and pOUT) by
fifo_length in descending order:
usb_sort_ep_array(pIN, nIN);
usb_sort_ep_array(pOUT, nOUT);
This must be done in order to eliminate fragmentation of the FIFO buffer when allocating
space for each active endpoint. Thus, addresses in the FIFO buffer for endpoints can be
calculated in a simple way:
INpos = 0;
OUTpos = 0;
for (i = 0; i < nIN; i++)
{
pIN[i]->in_fifo_start = INpos;
INpos += pIN[i]->fifo_length;
M
Driver Initialization.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
4-3
}
for (i = 0; i < nOUT; i++)
{
pOUT[i]->out_fifo_start = OUTpos;
OUTpos += pOUT[i]->fifo_length;
}
Finally, the maximum length of the packet, the size of the FIFO buffer, and the address of
the FIFO buffer for each endpoint should be stored in the appropriate configuration
register. In the first instance, this is done for endpoint number zero:
/* Initialize Endpoint 0 IN FIFO */
MCF5272_WR_USB_IEP0CFG(imm, 0
| (ep[0].packet_size << 22)
| (ep[0].fifo_length << 11)
| ep[0].in_fifo_start);
/* Initialize Endpoint 0 OUT FIFO */
MCF5272_WR_USB_OEP0CFG(imm, 0
| (ep[0].packet_size << 22)
| (ep[0].fifo_length << 11)
| ep[0].out_fifo_start);
then for the remaining endpoints:
for (i = 1; i < NUM_ENDPOINTS; i++)
{
if (ep[i].ttype != DISABLED)
{
if (ep[i].dir == IN)
/* Initialize Endpoint i FIFO */
MCF5272_WR_USB_EPCFG(imm, i, 0
| (ep[i].packet_size << 22)
| (ep[i].fifo_length << 11)
| ep[i].in_fifo_start);
else
/* Initialize Endpoint i FIFO */
MCF5272_WR_USB_EPCFG(imm, i, 0
| (ep[i].packet_size << 22)
| (ep[i].fifo_length << 11)
| ep[i].out_fifo_start);
}
}
4.5. Initialization of Interrupts.
The registration of interrupt handlers within uClinux is implemented in init_module()
routine:
request_irq(77,
(EP0)", NULL);
request_irq(78,
(EP1)", NULL);
M
usb_endpoint0_isr,
SA_INTERRUPT,
"ColdFire
USB
usb_endpoint_isr,
SA_INTERRUPT,
"ColdFire
USB
Driver Initialization.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
4-4
request_irq(79,
(EP2)", NULL);
request_irq(80,
(EP3)", NULL);
request_irq(81,
(EP4)", NULL);
request_irq(82,
(EP5)", NULL);
request_irq(83,
(EP6)", NULL);
request_irq(84,
(EP7)", NULL);
usb_endpoint_isr,
SA_INTERRUPT,
"ColdFire
USB
usb_endpoint_isr,
SA_INTERRUPT,
"ColdFire
USB
usb_endpoint_isr,
SA_INTERRUPT,
"ColdFire
USB
usb_endpoint_isr,
SA_INTERRUPT,
"ColdFire
USB
usb_endpoint_isr,
SA_INTERRUPT,
"ColdFire
USB
usb_endpoint_isr,
SA_INTERRUPT,
"ColdFire
USB
The rest of initialization of interrupts is combined into one function – usb_isr_init().
First, it clears any pending interrupts in all endpoints:
MCF5272_WR_USB_EP0ISR(imm,
MCF5272_WR_USB_EP1ISR(imm,
MCF5272_WR_USB_EP2ISR(imm,
MCF5272_WR_USB_EP3ISR(imm,
MCF5272_WR_USB_EP4ISR(imm,
MCF5272_WR_USB_EP5ISR(imm,
MCF5272_WR_USB_EP6ISR(imm,
MCF5272_WR_USB_EP7ISR(imm,
0x0001FFFF);
0x001F);
0x001F);
0x001F);
0x001F);
0x001F);
0x001F);
0x001F);
Then, the function enables the desired interrupts for all endpoints:
MCF5272_WR_USB_EP0IMR(imm, 0
| MCF5272_USB_EP0IMR_DEV_CFG_EN
| MCF5272_USB_EP0IMR_VEND_REQ_EN
| MCF5272_USB_EP0IMR_WAKE_CHG_EN
| MCF5272_USB_EP0IMR_RESUME_EN
| MCF5272_USB_EP0IMR_SUSPEND_EN
| MCF5272_USB_EP0IMR_RESET_EN
| MCF5272_USB_EP0IMR_OUT_EOT_EN
| MCF5272_USB_EP0IMR_OUT_EOP_EN
| MCF5272_USB_EP0IMR_IN_EOT_EN
| MCF5272_USB_EP0IMR_IN_EOP_EN
| MCF5272_USB_EP0IMR_UNHALT_EN
| MCF5272_USB_EP0IMR_HALT_EN);
MCF5272_WR_USB_EP1IMR(imm, 0x001F);
MCF5272_WR_USB_EP2IMR(imm, 0x001F);
Finally, it sets up an interrupt priority level for each endpoint, by initializing the
corresponding Interrupt Control Registers:
MCF5272_WR_SIM_ICR2(imm, 0
| (0x00008888)
| (USB_EP0_LEVEL << 12)
| (USB_EP1_LEVEL << 8)
| (USB_EP2_LEVEL << 4)
| (USB_EP3_LEVEL << 0));
MCF5272_WR_SIM_ICR3(imm, 0
M
Driver Initialization.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
4-5
|
|
|
|
|
(0x88880000)
(USB_EP4_LEVEL
(USB_EP5_LEVEL
(USB_EP6_LEVEL
(USB_EP7_LEVEL
<<
<<
<<
<<
28)
24)
20)
16));
Following this operation, usb_init() enables the USB controller and Configuration
RAM:
MCF5272_WR_USB_EP0CTL(imm, 0
| MCF5272_USB_EP0CTL_USB_EN
| MCF5272_USB_EP0CTL_CFG_RAM_VAL);
Now, transfers are permitted for endpoint number zero only. To enable other endpoints,
the Host must first set up the configuration.
M
Driver Initialization.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
4-6
5. Control, Bulk, Interrupt Data Transfer.
This chapter describes how the Driver supports Control, Bulk, and Interrupt transfer
types, describing how to initiate a transfer and complete it correctly.
5.1. Device-to-Host Data Transfer.
To transfer data from the device to the Host, the write() function shall be called by the
Client application. usb_device_write() routine (which handles write() call to device
file) calls usb_tx_data(). It accepts three parameters:
epnum – number of endpoint, through which data will be transferred (obtained
from minor number);
start – pointer to data buffer, that will be transferred;
length – number of bytes to transfer (transfer length).
This function initializes the fields of global structure ep.buffer.
It sets the field ep[epnum].buffer.start to the beginning of the data buffer to be sent,
ep[epnum].buffer.length – to the length of buffer, and ep[epnum].buffer.position
to 0 (no data sent yet).
Then, it determines the number of bytes that can be placed into FIFO buffer, and copies
that amount of data from the source buffer to the FIFO. Then it modifies the
ep[epnum].buffer.position field (ep[epnum].buffer.position will be set to the
number of bytes written). usb_tx_data() then returns control.
For more detailed information about usb_tx_data() refer to Chapter 5.1.1.
The USB module sends this data to the Host in packets. If the Host successfully receives
a packet, it sends an acknowledge to the device. Following this, the USB module
generates EOP (end of packet) interrupt. Using this interrupt, a new portion of data can be
placed into the FIFO buffer. The usb_in_service() handler is used for this purpose.
usb_in_service()
checks
if
there
are
data
to
send
(examines
ep[epnum].buffer.position and ep[epnum].buffer.length). If there are data to
send, it determines the amount of data that can be placed into the FIFO buffer.
usb_in_service() copies that amount of data to the FIFO buffer and increments the
ep[epnum].buffer.position field by the number of written bytes.
For more detailed information about usb_in_service() refer to Chapter 5.1.2.
When write() returns control, the Client application may process another portion of data
or execute an algorithm. This activity will be interrupted from time-to-time by EOP/EOT
interrupts, and usb_in_service() will then be called. When the Client application
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-1
completes execution of its algorithms and is ready to send another data buffer to USB, it
may call the USB_EP_BUSY ioctl command (to test if desired endpoint is free) or
USB_EP_WAIT (to wait while desired endpoint is busy). For more detailed information
about these functions refer to Chapter 9.
The stages of data transferring from Device to Host are shown in Fig 5-1.
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-2
ep[epnum].buffer.start = 0
ep[epnum].buffer.position = 0
ep[epnum].buffer.length = 0
Initial state:
Call to usb_tx_data():
ep[epnum].buffer.start +
ep[epnum].buffer.length
ep[epnum].buffer.start
ep[epnum].buffer.position = 0
Data Buffer (to be sent)
usb_tx_data() places data to FIFO buffer:
ep[epnum].buffer.start
ep[epnum].buffer.start +
ep[epnum].buffer.length
ep[epnum].buffer.start +
ep[epnum].buffer.position
Data already
placed to FIFO
Data Buffer (to be sent)
EOP interrupt occurred,
usb_in_service() is called
and places data to FIFO:
ep[epnum].buffer.start +
ep[epnum].buffer.length
ep[epnum].buffer.start +
ep[epnum].buffer.position
ep[epnum].buffer.start
Data already
placed to FIFO
Sent Data
Data Buffer (to be sent)
EOP interrupt occurred,
usb_in_service() is called
and places data to FIFO:
ep[epnum].buffer.start + ep[epnum].buffer.start +
ep[epnum].buffer.position ep[epnum].buffer.length
ep[epnum].buffer.start
Data already
placed to FIFO
Sent Data
.
.
Data Buffer
(to be sent)
.
Fig 5-1. Stages of data transfer by the Driver.
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-3
5.1.1.
Initiating a Data Transfer.
The usb_tx_data() function is used to initiate each data transfer from Device to Host.
The algorithm of this function is shown in Fig 5-2.
Start
yes
epnum = 0?
no
yes
Are transfers
allowed for this
endpoint?
yes
no
Exit
yes
epnum < 8?
no
yes
Is ep free?
Exit
no
Exit
yes
Is there data
to send?
no
yes
Is this an
IN endpoint?
Exit
no
yes
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
Exit
5-4
Is endpoint
halted?
yes
no
Save the current
IMR
Exit
Disable EOP, EOT, RESET,
DEV_CFG interrupts
Set up EP buffer
structure
Determine number of bytes to
place into FIFO
Place data to FIFO
buffer
Modify position
Is it all the data
to be sent?
yes
no
Finish transfer
Restore saved IMR
Exit
Fig 5-2. Algorithm of usb_tx_data() function.
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-5
usb_tx_data() accepts three parameters (see Chapter 5.1). Firstly it checks whether the
device has been reset for data transfers on a non-zero endpoint. Since endpoint number
zero transfers are permitted even if the device is not configured. For more detailed
information refer to Chapter 8 and Chapter 9. Then usb_tx_data() tests whether the
given endpoint is busy:
/* See if the EP is currently busy */
if (ep[epnum].buffer.start || (epnum && MCF5272_RD_USB_EPDPR(imm,
epnum)))
return 1;
It checks the ep[epnum].buffer.start field (it should not point to any buffer) and
checks that the FIFO buffer is empty (for non-zero endpoints, because EP0DPR monitors
OUT FIFO only).
Then it makes sure there is data to send (examines parameters start and length).
Finally, it ensures that the desired endpoint is an IN endpoint and the endpoint is not
halted.
EOP/EOT interrupts should be disabled in order to prevent damage of the
ep[epnum].buffer structure by the usb_in_service() handler. RESET and DEV_CFG
interrupts must also be disabled in order to properly terminate the transfer.
usb_tx_data() sets up the ep buffer structure:
ep[epnum].buffer.start = start;
ep[epnum].buffer.length = length;
ep[epnum].buffer.position = 0;
Then, the amount of data that can be placed into the FIFO buffer is determined:
free_space = ep[epnum].fifo_length;
length parameter (amount of data to be sent) can be less than the size of the FIFO
buffer for epnum, therefore additional modifications are needed:
/* If the amount of data to be sent less than free_space, modify
free_space */
if ((int16) free_space > length)
{
free_space = length;
}
Now, usb_tx_data() starts to write data to the FIFO buffer four bytes at a time (while it
is possible) and the rest of data - by one byte.
If this is all the data that has to be sent, usb_tx_data() finishes the transfer (refer to
Chapter 5.1.3). It does not clear ep[epnum].buffer structure. The usb_tx_data()
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-6
function placed data for at least one packet, so the EOP interrupt will occur, and
usb_in_service() will continue or finish the transfer properly. The saved interrupt
mask register must be restored. The function returns control.
5.1.2.
Continuation of a Data Transfer.
If the Host successfully receives a data packet it sends acknowledge to device and the
USB module generates EOP interrupt. At this moment there is a free space in FIFO
buffer for at least one data packet. Thus, placing a new portion of data to FIFO module
will continue the transfer.
usb_in_service() is responsible for continuation of the transfer. Its algorithm is shown
in Fig 5-3.
This function accepts two parameters:
epnum – number of endpoint, for which interrupt has occurred;
event – the kind of interrupt(s) occurred.
First, usb_in_service() tests event for EOP interrupt. If an interrupt occurred, the
function saves IMR and disables RESET and DEV_CFG interrupts. If there is data to
send, it determines the amount of data that can be placed into the FIFO buffer.
The data present register for endpoint number zero monitors only the OUT FIFO, so it
cannot be used to determine the free space in the FIFO buffer for that endpoint. Thus, if
epnum is zero, not more than one packet will be placed into the FIFO. Free space for the
rest of the endpoints can be calculated by subtracting the amount of data in the FIFO
buffer from the length of the FIFO buffer for that endpoint:
if (epnum == 0)
free_space = ep[0].packet_size;
else
free_space
=
(uint16)(ep[epnum].fifo_length
MCF5272_RD_USB_EPDPR(imm, epnum));
-
If the amount of data to be sent less than the free space in the FIFO buffer, the variable
free_space must be modified:
if
(free_space
ep[epnum].buffer.position))
free_space
ep[epnum].buffer.position);
>
=
(ep[epnum].buffer.length
(ep[epnum].buffer.length
-
Then usb_in_service() writes data to FIFO using a four byte format (while it is
possible) and the rest of data – in lots of one byte.
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-7
If this was all the data to be sent, usb_in_service() finishes the transfer. The saved
interrupt mask register must be restored.
Finally, usb_in_service() tests event for EOT interrupt. If that interrupt occurred, the
function finishes the transfer (refer to Chapter 5.1.3).
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-8
Start
Does EOP
interrupt occur?
no
yes
Disable RESET and DEV_CFG interrupts
(save current IMR)
Is there data to
send?
no
yes
Determine amount of data that
can be placed to FIFO
Place data to FIFO
Is it all the data
to be sent?
yes
Finish transfer
no
Restore saved IMR
Is there EOT
interrupt?
no
yes
Finish transfer
Exit
Fig 5-3. Algorithm of usb_in_service() function.
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-9
5.1.3.
Completion of Data IN Transfer.
The Driver sends data to the Host in maximum size packets (while it is possible). The rest
of data are sent in one short packet. The Driver handles the end of transfer in different
ways depending upon the exact situation. Table 5-1 summarizes the conditions and the
device’s actions according those conditions.
N
1
2
3
Table 5-1. Conditions and device’s actions to finish the transfer
Condition
Device finishes the transfer in following
way
Driver clears EPNCTL[IN_DONE] bit to
The length of transferred buffer send one short length data packet. EOT
was not a multiple of the interrupt will occur. Driver clears the
maximum size of packet.
ep[epnum].buffer structure and sets up
EPNCTL[IN_DONE] bit in EOT interrupt
handler.
Host received all the data it Clears the ep[epnum].buffer structure
expected. The length of transferred after the last packet was successfully sent to
buffer was a multiple of the the Host.
maximum size of packet.
Host did not receive all the data it In this case, device sends zero length packet
expected. The length of transferred to Host to indicate the end of transfer.
buffer was a multiple of the Driver clears EPNCTL[IN_DONE] bit.
maximum size of packet.
EOT interrupt will occur. Driver clears the
ep[epnum].buffer structure and sets up
EPNCTL[IN_DONE] bit in EOT interrupt
handler.
If the length of a transferred buffer was less than or equal to the size of the FIFO buffer
for the used endpoint, the usb_tx_data() function completes the transfer. If the last
packet is maximum size, it will be sent by the USB module automatically. If the last
packet is short, the IN_DONE bit must be cleared and as a result, the USB module will
send to the bus all the data it has (will not wait to form a maximum size packet). In both
cases, usb_in_service() handler will be called and will complete the transfer.
if ( i == ep[epnum].buffer.length )
{
/* This is all of the data to be sent */
if ((i % ep[epnum].packet_size) != 0)
/*Sent short packet - Clear the IN-BUSY bit */
MCF5272_WR_USB_EPCTL(imm, epnum, MCF5272_RD_USB_EPCTL(imm,
epnum)
& ~MCF5272_USB_EPNCTL_IN_BUSY);
}
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-10
usb_in_service() finishes the transfer in two different places: in the handler of the
EOP event and in the handler of the EOT event:
a)
If all the data is placed in the FIFO buffer and the amount of that data was a
multiple of the maximum size of packet, an EOP interrupt will occur,
usb_in_service() completes the transfer in the EOP event handler.
b)
If all the data is placed in the FIFO buffer but the size of data was not a multiple
of the maximum size of packet, the last packet (short) may stay in the FIFO
buffer. In this case the EPNCTL[IN_DONE] bit must be cleared to send the short
packet. An EOT interrupt will occur; usb_in_service() completes the transfer
in the EOT event handler.
usb_in_service() checks in the EOP handler if all the data was written into the FIFO.
If it was, usb_in_service() tests if the length of the transfer is a multiple of the
maximum size of packet, and clears the EPNCTL[IN_DONE] bit to send the last short
packet if the length of the buffer is not a multiple of the maximum packet size:
if (i == ep[epnum].buffer.length)
{
remainder = i % ep[epnum].packet_size;
/* This all of the data to be sent */
if ((remainder != 0) || ((remainder == 0)
ep[epnum].sendZLP))
{
/* All done -> Clear the IN-BUSY bit */
MCF5272_WR_USB_EPCTL(imm, epnum,
MCF5272_RD_USB_EPCTL(imm, epnum)
& ~MCF5272_USB_EPNCTL_IN_DONE);
}
else
if (MCF5272_RD_USB_EPDPR(imm, epnum) == 0)
{
if ((epnum == 0) && (NewC))
{
usb_vendreq_done(SUCCESS);
&&
free(NewC);
NewC = NULL;
}
ep[epnum].bytes_transferred = i;
ep[epnum].buffer.start = 0;
ep[epnum].buffer.length = 0;
ep[epnum].buffer.position = 0;
/* Wake up usb_ep_wait function if
it sleeps */
wake_up_interruptible(&ep_wait_queue);
}
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-11
ep[epnum].sendZLP = FALSE;
}
EOT will occur in such a case and its handler completes the transfer.
If the length of a transferred buffer was a multiple of the maximum size of packet, one of
two variants is possible: either the Host received all the data it expected or not. Field
sendZLP is used to distinguish these cases. The Client application knows the amount of
data requested by the Host. If that amount is larger than the Client application is going to
send, there is a possibility to send the last packet with the maximum size. To properly
handle the end of transfer in this case, the Client application must call the
USB_SET_SEND_ZLP ioctl for the required endpoint. The function sets up the sendZLP
field to TRUE. The Driver tests this field and only if the last packet is maximum size,
does it send a zero length packet.
The Client application does not need to calculate the remainder of a division to find the
size of the last packet before calling write(), since the Driver makes the calculation by
itself. The only thing the Client application must do is to compare the size of the
requested data from the Host, with the amount of data that the Client application is going
to send before each transfer. If the last is smaller, sendZLP must be setup to TRUE.
If the Client application is able to send all the requested data, it does not need to call the
USB_SET_SEND_ZLP ioctl (sendZLP field is cleared by the Driver after the last transfer).
The EOP handler completes the transfer in this case (see the source code above). For
more information refer to Chapter 8.17.
EOT interrupt occurs if a short length or zero length packet was sent. It completes the
transfer and sets the EPNCTL[IN_DONE] bit to send data for next transfers by maximum
size packets (previously that bit was cleared).
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-12
5.2. Host-to-Device Data Transfer.
Assuming that the OUT transfer starts from the moment when function read() is called,
if there is data in the FIFO buffer but the Client buffer is not allocated yet, the transfer
will not be started. The usb_device_read() function handles the read() call to the
device file and calls usb_rx_data(). EOP interrupts will occur (while the FIFO buffer is
able to accept data) and the usb_out_service() function will properly handle this
situation. But for the Client program, the transfer is not yet started.
usb_rx_data() accepts three parameters:
epnum – number of endpoint, through which data will be transferred (obtained
from minor number);
start – pointer to the buffer, where data will copied from FIFO buffer;
length – number of bytes that will be received.
This function initializes the fields of global structure ep.buffer. It sets the field
ep[epnum].buffer.start to the beginning of data buffer where it will place the data,
ep[epnum].buffer.length
–
to
the
size
of
expected
data,
and
ep[epnum].buffer.position to 0 (no data read yet).
Then, the function determines the number of bytes in the FIFO buffer, and copies that
amount of data from the FIFO to the destination buffer. Then it modifies the
ep[epnum].buffer.position field (ep[epnum].buffer.position will be set to the
number of copied bytes). usb_rx_data() returns control. For more detailed information
about usb_rx_data() refer to Chapter 5.2.1.
The Host sends data in packets. If the USB module successfully receives a packet, it
generates an EOP (end of packet) interrupt. Using this interrupt, a new portion of data can
be read from the FIFO buffer. The usb_out_service() handler is used for this purpose.
It determines the amount of data in the FIFO buffer and copies the data to a destination
buffer (ep[epnum].buffer.start points to it). For more detailed information about
usb_out_service() refer to Chapter 5.2.2.
When read() returns control, the Client application may process another portion of data
or execute some algorithm. This activity will be interrupted from time-to-time by EOP
interrupts, and usb_out_service() will be called. When the Client application finishes
execution of its algorithms and is ready to receive other data from the Host, it may call
the USB_EP_BUSY ioctl command (to test if the desired endpoint is free) or USB_EP_WAIT
(to wait while the desired endpoint is busy). For more detailed information about these
functions refer to Chapter 9. The different stages of data transfer from Host to Device are
shown in Fig 5-4.
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-13
ep[epnum].buffer.start = 0
ep[epnum].buffer.position = 0
ep[epnum].buffer.length = 0
Initial state:
Call to usb_rx_data():
ep[epnum].buffer.start +
ep[epnum].buffer.length
ep[epnum].buffer.start
ep[epnum].buffer.position = 0
Destination Data Buffer (empty)
usb_rx_data() reads data
from FIFO buffer:
ep[epnum].buffer.start
ep[epnum].buffer.start +
ep[epnum].buffer.position
ep[epnum].buffer.start +
ep[epnum].buffer.length
Data already
placed from FIFO
Free space
EOP interrupt occurred,
usb_out_service() is called
and reads from FIFO:
ep[epnum].buffer.start +
ep[epnum].buffer.length
ep[epnum].buffer.start +
ep[epnum].buffer.position
ep[epnum].buffer.start
Received Data
Data already
read from FIFO
Free space
EOP interrupt occurred,
usb_out_service() is called
and reads from FIFO:
ep[epnum].buffer.start + ep[epnum].buffer.start +
ep[epnum].buffer.position ep[epnum].buffer.length
ep[epnum].buffer.start
Data already
read from FIFO
Received Data
.
.
Free space
.
Fig 5-4. The stages of receiving data by Driver
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-14
5.2.1.
Initiating a Data Transfer.
The usb_rx_data() function is called by usb_device_read() (which handles read()
call from the Client application) and is used to start receiving data from the Host. The
algorithm of this function is shown in Fig 5-5.
Start
yes
epnum = 0?
no
yes
Are transfers
allowed for this
endpoint?
yes
Does Client
know about new
configuration?
no
Exit
no
Exit
yes
Is ep free?
no
Exit
yes
no
Is there buffer
allocated?
yes
Is this an
OUT endpoint?
Exit
no
yes
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
Exit
5-15
Is endpoint
halted?
yes
no
Save the current
IMR
Exit
Disable EOP, EOT, RESET,
DEV_CFG interrupts
Set up EP buffer
structure
Determine number of bytes
in FIFO buffer
Read data
from FIFO buffer
Modify position
Is it all the data
to be received?
yes
no
Finish transfer
Restore saved IMR
Exit
Fig 5-5. Algorithm of usb_rx_data() function.
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-16
usb_rx_data() accepts three parameters (see Chapter 5.2). First, it checks whether the
device is reset for data transfers with a non-zero endpoint. For endpoint number zero
transfers are permitted even if the device is not configured. Following this procedure,
usb_rx_data() tests that the given endpoint is not busy:
/* See if the EP is currently busy */
if (ep[epnum].buffer.start)
return 1;
It checks the ep[epnum].buffer.start field - which should not point to any buffer.
Then it makes sure there is target data buffer (examines parameters start and length).
Finally, the function ensures that the desired endpoint is an OUT endpoint and that the
endpoint is not halted.
EOP/EOT interrupts should be disabled in order to prevent damage to the
ep[epnum].buffer structure by the usb_out_service() handler. RESET and
DEV_CFG interrupts must also be disabled in order to properly terminate the transfer.
usb_rx_data() sets up the ep buffer structure:
ep[epnum].buffer.start = start;
ep[epnum].buffer.length = length;
ep[epnum].buffer.position = 0;
Then, determines the amount of data in the FIFO buffer:
/* Read the Data Present register */
fifo_data = MCF5272_RD_USB_EPDPR(imm, epnum);
The length parameter (the amount of data to be received) can be less than the amount of
data in the FIFO buffer for epnum, thus additional modifications are needed:
if (fifo_data > length)
{
fifo_data = length;
}
Now, usb_rx_data() starts to read data from the FIFO buffer four bytes at a time (while
this is possible) and the rest of data - one byte at a time.
If this is all the data to be received, usb_rx_data() finishes the transfer (refer to Chapter
3.2.3). The saved interrupt mask register must be restored. The function returns control.
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-17
5.2.2.
Continuation of a Data Transfer.
If the USB module successfully receives a data packet it generates an EOP interrupt. At
this moment there is data in the FIFO buffer. Thus, reading a new portion of data from
the FIFO module will continue the transfer.
usb_out_service() is responsible for the continuation of the transfer. Its algorithm is
shown in Fig 5-6.
This function accepts two parameters:
epnum – number of endpoint, for the interrupt that occurred;
event – the kind of interrupt(s) that occurred.
First, usb_out_service() tests event for an EOP interrupt. If this interrupt occurred,
the function saves IMR and disables RESET and DEV_CFG interrupts. Then it
determines the amount of data in the FIFO buffer:
/* Read the Data Present register */
fifo_data = MCF5272_RD_USB_EPDPR(imm, epnum);
If data is received on the endpoint but no buffer is allocated, the USB module will be
accepting the data from the Host while there is free space in the FIFO buffer. Following
this occurrence, data transmission will be stopped, until such time as the Client
application allocates a target buffer.
If a buffer is allocated for a given endpoint, the Driver starts to read data from the FIFO
buffer four bytes at a time (while this is possible) and the rest of data one byte at a time.
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-18
Start
Does EOP
interrupt occured?
no
yes
Disable RESET and DEV_CFG interrupts
(save current IMR)
Determine the amount of data
in FIFO buffer
Terminate transfer if overflow
condition is occurred
Is buffer allocated for
given endpoint?
no
yes
Read data from FIFO buffer
yes
Is it all the data
to be received?
Finish transfer
no
Restore saved IMR
Exit
Fig 5-6. Algorithm of usb_out_service() function.
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-19
5.2.3.
Completion of a Data OUT Transfer.
For OUT transfers, both functions usb_rx_data() and usb_out_service() may
complete the transfer.
If usb_rx_data() reads all the required data from the FIFO buffer, it clears the
ep[epnum].buffer structure (because other OUT EOP interrupts may not occur).
If all the data are received in the EOP handler, usb_out_service() checks if the
received data is a command:
if (ep[epnum].buffer.position == ep[epnum].buffer.length)
{
if ((epnum == 0) && (NewC))
{
/* We have got a new command and can wake up
fetch_command routine */
wake_up_interruptible(&fetch_command_queue);
/* and notify Client about it */
if
(usb_async_queue)
kill_fasync(&usb_async_queue,
SIGIO, POLL_IN);
}
.
.
.
The Driver notifies the Client application about the reception of a vendor specific request
using asynchronous notification (kill_fasync()).
M
Control, Bulk, Interrupt Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
5-20
6. Isochronous Data Transfer.
(for CBI & Isochronous Driver only)
This chapter describes how the Driver controls Isochronous IN/OUT transfers. It
describes how to open Isochronous IN and OUT data streams and how to close them
correctly. Also, the chapter describes how the Driver performs per-frame monitoring of
Host-side software and device-side Client application when they are working in real-time.
6.1. Requests queue and buffer headers.
For Isochronous transfers the Driver can put I/O requests (read() and write()) in the
queue. This is used to ensure a continuous data transfer. When one data buffer is
transmitted, the Client application must provide the next one as soon as possible,
otherwise some frames will be skipped. Since uClinux is a multitasking OS, it is possible
that the Client application will not receive control in time. So queuing the requests by the
Driver allows the Client application to get some additional time, to take control and
prepare the next buffer(s). Assuming that the request is a call to read() or write() Driver
function, the Client puts several requests in the queue (for example write() requests)
and then controls the size of the queue (using the USB_EP_WAIT ioctl). The following
fragment of code may be taken as an example:
write(usb_ep1_file, &buffers[0], 5);
write(usb_ep1_file, &buffers[1], 5);
/* Wait until 1 request left in a queue */
ioctl(usb_ep1_file, USB_EP_WAIT, 1);
USB_EP_WAIT ioctl takes one parameter – the number of requests that should stay in the
queue. This means that USB_EP_WAIT will return control only if the number of requests in
the queue is less than or equal to the number passed in this parameter.
When the Driver receives the first request, it initiates a transfer (fills the buffer structure)
and returns control. When it receives the second request and if the endpoint is still busy
(ep[n].buffer.start not NULL) it puts this request in the queue. The field
number_of_requests of the iso_ep structure contains the number of requests in the
queue, first_io_request contains the index of the first request in an array of requests,
last_io_request contains the index of the last request.
if (ep[epnum].buffer.start)
{
if (++iso_ep[epnum].number_of_requests >= MAX_IO_REQUESTS)
{
iso_ep[epnum].number_of_requests--;
return -EBUSY;
}
if (++iso_ep[epnum].last_io_request >= MAX_IO_REQUESTS)
M
Isochronous Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
6-1
iso_ep[epnum].last_io_request = 0;
iso_ep[epnum].requests_queue[iso_ep[epnum].last_io_request].start
start;
=
iso_ep[epnum].requests_queue[iso_ep[epnum].last_io_request].length
length;
=
………
Request parameters are stored in two fields – start (address of buffer) and length
(length of buffer in packets), which were passed to the Driver during the read/write call.
When the Driver completes the transfer of the current buffer, it extracts the next request
from the queue (address and length of new buffer) and starts processing it immediately.
The usb_get_request() function performs this operation.
Another important thing to note is that each buffer for an Isochronous transfer must have
a header. The header contains the sizes of each packet that will be received or
transmitted. After completion of the transfer, the header contains the actual size of data
that were read or written for each packet. The definition of the buffer for an Isochronous
transfer may look like the following:
typedef struct {
uint32 packet_length[20];
uint8 databuf[1800];
} audio_buffer;
This buffer contains 20 packets of ninety bytes each. Before calling the read() or
write() function, the Client application must first fill the packet_length field with the
appropriate length of packets.
6.2. Device-to-Host Data Transfer.
This subsection describes the concepts of Isochronous IN transfer, tells how the Driver
opens a data stream, continues it, etc. The following two sections describe how the Driver
monitors whether the Host software is working in real-time. It also describes how the
Driver sustains sample rate if the Host s/w misses frames.
Some remarks concerning terminology must be made. “Isochronous data IN stream”
implies an uninterruptible transmission of data to the Host. It includes an infinite (while
Device is powered) number of calls to the write() function. Sending a buffer, passed to
each write() is a “transfer”. Each transfer consists of limited number of packets (some
packets may be short – in order to setup the required sample rate). Data on isochronous
endpoints is generally streaming data. Therefore it can be assumed, that all transfers on
each isochronous endpoint belongs to the corresponding stream, that was started much
earlier on and never finishes.
M
Isochronous Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
6-2
To start write “to the stream”, the Client program must call the write() function every
time it wants to transfer a data buffer. This function initializes the ep[epnum].buffer
structure and places data to the FIFO buffer. When this function returns control to the
Client program, no data is sent yet – the earliest an IN token can be received is in the next
frame, hence the first packet will only be sent in the next frame.
The mechanism of sending a data buffer with an isochronous endpoint is mostly similar
to CBI transfers, but there are some distinctions.
1. Data is sent in packets (which is also common with CBI). However isochronous
packets are guaranteed to be sent once per USB frame and they are never resent.
2. Isochronous endpoints support packet sizes of up to 1023 bytes. Which means that
the FIFO size can be less than twice the packet size. Therefore to send each
packet, a FIFO level interrupt must be used. However it is recommended to use a
FIFO buffer size greater than the packet size if possible.
3. When the Client program calls write(), the length parameter must contain the
number of packets that have to be written and not the size of buffer. The Driver
determines the size of buffer using the information from the buffer header.
In each frame the Driver places only one packet into the FIFO (or initial bytes of the
packet if it is larger than the FIFO buffer, and when FIFO-level interrupt occurs, the
Driver places the rest of the current packet into the FIFO).
When the last packet of a transfer is sent to the Host (Driver received EOP interrupt and
FIFO is empty), the Driver wakes up the usb_ep_wait() function (if it was sleeping) and
checks if there are any requests in the queue. If the queue is not empty, the Driver
extracts the next request and reinitializes ep[epnum].buffer and iso_ep[epnum]
structures. Otherwise it frees the ep[epnum].buffer structure. The Client program may
track the end of transfer either by calling USB_EP_WAIT or USB_EP_BUSY ioctl. Then
the Client program may call write() with the next buffer.
In order to work in real-time, the Client program must add requests to the Driver’s queue
(by calling the write() function) or call write() as soon as possible after the endpoint
becomes free, in every case before the next SOF interrupt occurs.
Two remarks are necessary in respect of sending data to the Host.
1. The Driver sends buffers to the Host in maximum size packets (while this is
possible). If some packets of the buffer are short, the Driver sends short packets –
it does not fill the FIFO with data from the next buffer.
2. If write() is called in the current frame, data placed in FIFO buffer, can be sent
to the Host not earlier than in the next frame. This function can be called only
after occurrences of the SOF interrupt. And delay between the SOF interrupt and
receipt of an IN token, is less than the time needed for calling write() and
reaching the point in this function in which it starts placing data into the FIFO
buffer.
M
Isochronous Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
6-3
6.3. Monitoring the Host Software During IN
Transfers.
There is a wide class of audio devices, which steadily produce (source devices) a fixed
amount of data. Devices such as a microphone serve as an excellent example. The ADC
of a microphone produces a fixed amount of samples per some period of time. Hence, the
Device has to send all of this data during a given time period (or at least, the buffer must
be freed by the end of that period).
Assuming the example that the Device tries to send a buffer of 5 packets to the Host. The
buffer must be freed after 5 milliseconds since the ADC produces new data for the next 5
packets that must be sent during the next 5 milliseconds. If Host does not issue IN tokens
(because of problems with real-time which can arise sometimes, for example), the
transfer buffer will require more than the 5 ms allowed. Hence buffer overlapping may
occur in such cases.
The Driver is able to address this problem by moving the internal pointer in the buffer
(like it sends data to the Host), even if the Host does not issue an IN token. In effect the
Driver guarantees that the buffer will be freed in a given time, thus assuring deterministic
behavior of the system. Moreover, when the Host resumes sending the tokens, it will
receive not old data (that ought to have been sent in the previous frames), but actual data.
If the Client application wants the Driver to perform transfer monitoring, it must call the
usb_set_start_frame_number() function. The Driver starts analyzing transfer from a
given frame, the number of which was passed as a parameter to that function. It must be
the number of a frame in which the first data packet is to be sent to the Host. All the
transfers after this frame will then be monitored. When the last transfer is completed, data
monitoring must be stopped (in order to properly start new one, or properly continue data
transfer without monitoring).
To stop monitoring, the Client program must call the usb_set_final_frame_number()
function, passing the number of the frame in which data monitoring must be stopped. It
must be in a frame following the frame in which the last data packet was sent to the Host
(or at least, not earlier) – the SOF interrupt handler of the next frame checks missed EOP
interrupts in previous frame. In such a case, the Driver can correctly handle the situation,
when the last packet was not sent to the Host.
The Driver monitors whether the Host s/w is working in real time while accepting data
from the Device, using the following mechanism. A data packet, i.e. EOP interrupt,
occurs once per USB frame. SOF interrupt also occurs once per frame (it is a start of
frame interrupt). If the Host s/w misses some frames (does not send IN tokens to Device),
EOP interrupt will not occur during those frames.
The Driver increments counter in usb_isochronous_transfer_service() function:
M
Isochronous Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
6-4
if
(iso_ep[epnum].
transfer_monitoring_started
==
TRUE)
{
iso_ep[epnum].sent_packet_watch ++;
/* It must be 1, now */
…
and clear it in usb_in_service() handler, if EOP interrupt occurred:
iso_ep[epnum].sent_packet_watch = 0;
…
When the next SOF interrupt occurs, usb_isochronous_transfer_service() tests
iso_ep[epnum]. sent_packet_watch field to determine whether EOP interrupt
occurred during the previous frame:
if (iso_ep[epnum].sent_packet_watch > 1)
{
/* Remove unsent packet from FIFO buffer */
MCF5272_WR_USB_EPCFG(imm, epnum, MCF5272_RD_USB_EPCFG(imm, epnum));
/* Reset the counter */
iso_ep[epnum].sent_packet_watch = 0;
/* Set up corresponding status for Client program */
iso_ep[epnum].status |= NOT_SENT_PACKET;
…
If a data packet was not sent to the Host, the FIFO buffer must be cleared in order to send
the next portion of data (not unsent packet!) in the next frame. In such a case, the Device
is still being synchronized with the USB clock. After that it assigns NOT_SENT_PACKET
status to the transfer, and this status will be passed to usb_tx_done() function after
completion of the buffer transfer.
As the next step, the Driver moves internal pointers on to the next packet. There are three
cases here, all of which must be handled differently. Assuming that the Client application
sends data to the Host in buffers using five packets.
Case 1. Any packet, except for the last and the next to last, was not sent to Host (assume,
it was packet 2).
ep[epnum].buffer.position
Packet 1
SOF1 EOP1
M
Packet 2
SOF2 -
iso_ep[epnum].packet_position
Packet 3
Packet 4
Packet 5
SOF3 EOP3
SOF4 EOP4
SOF5 EOP5
Isochronous Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
6-5
When SOF3 interrupt occurs, usb_isochronous_transfer_service() determines that
packet 2 was not sent to the Host (EOP2 interrupt did not occur). It removes all data from
the FIFO buffer (there is data from packet 2 there only). In fact, packet 3 must now be
placed to the FIFO, however the token for the third packet is missed by this time by the
Device (similar situations are described in section 6.1, remark 2). Thus, data from packet
4 must be placed into the FIFO instead, and that packet will be sent to the Host in the
fourth frame.
So,
the
usb_isochronous_transfer_service()
ep[epnum].buffer.position to the beginning of fourth packet:
function
points
ep[epnum].buffer.position = iso_ep[epnum].packet_position +
iso_ep[epnum].packet_length[iso_ep[epnum].frame_counter];
and points iso_ep[epnum].packet_position to the end of fourth packet:
iso_ep[epnum].packet_position = ep[epnum].buffer.position +
iso_ep[epnum].packet_length[iso_ep[epnum].frame_counter];
Following this, the function places packet 4 into the FIFO. If the packet is larger than the
FIFO, the copying will be continued by usb_in_service() after raising a FIFO level
interrupt.
So, if the Host misses one frame, it does not receive the data that had to be sent in that
frame, and it does not receive data in the next frame (even if it issued IN token). In the
next frame Host may receive a few bytes of garbage – bytes that were sent before starting
to clear the FIFO. Thus, EOP3 interrupt may occur, but it is a spurious interrupt –
iso_ep[epnum].packet_position should not be modified in usb_in_service(). To
distinguish between spurious and normal EOP, the endpoint data present register must be
tested. In the case of a spurious interrupt the register contains non-zero value (the next
packet is already written to the FIFO by usb_isochronous_transfer_service()), and
is otherwise cleared.
Case 2. Next to last packet was not sent to the Host (packet 4).
ep[epnum].buffer.position
Packet 1
SOF1 EOP1
M
Packet 2
Packet 3
SOF2 EOP2
SOF3 EOP3
iso_ep[epnum].packet_position
Packet 4
SOF4 -
Packet 5
SOF5 EOP5
Isochronous Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
6-6
When SOF5 interrupt occurs, usb_isochronous_transfer_service() determines that
packet 4 was not sent to the Host (EOP4 interrupt did not occur). It removes all data from
the FIFO buffer (there is data from packet 4 there only). In fact, packet 5 must now be
placed into the FIFO, but the token for the fifth packet is missed by this time by the
Device (which is the situation like that one described in section 6.1, remark 2). Thus, the
transfer of this buffer must be completed.
The function assigns a DEFAULT value to the internal state field. It means, that
usb_tx_data() must start transferring the next buffer from the first packet.
iso_ep[epnum].state = DEFAULT;
If requests queue is not empty, usb_isochronous_transfer_service() extracts request
from the queue and starts writing to the FIFO from the first packet of the next buffer.
Otherwise usb_isochronous_transfer_service() completes the current transfer:
/* Extract next request from the queue */
if (usb_get_request(epnum))
iso_ep[epnum].packet_position
iso_ep[epnum].packet_length[0];
else
{
ep[epnum].buffer.start = 0;
ep[epnum].buffer.length = 0;
ep[epnum].buffer.position = 0;
=
iso_ep[epnum].frame_counter = 0;
}
So, if the Host misses one frame, it does not receive data that had to be sent in that frame,
and it does not receive data in the next frame either (even if it issued IN token). Then in
the next frame the Host may receive a few bytes of garbage – bytes that were sent before
starting to clear the FIFO. Thus an EOP5 interrupt may occur, however this is a spurious
interrupt. If this interrupt occurs, it will occur immediately following the SOF5 interrupt.
Even if EOP5 occurs after the call to usb_tx_data() (write()), this situation will
also be handled (see case 1).
Case 3. Last packet was not sent to the Host (packet 5).
ep[epnum].buffer.position
Packet 3
Packet 4
SOF3 EOP3
SOF4 EOP4
M
iso_ep[epnum].packet_position
Packet 5
SOF5 -
Packet 1
SOF6 EOP1
Isochronous Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
Packet 2
SOF7 EOP2
6-7
When SOF6 interrupt occurs, usb_isochronous_transfer_service() determines that
packet 5 of the previous buffer was not sent to the Host (EOP5 interrupt did not occur,
thus the transfer of that buffer was not completed yet). It removes all data from the FIFO
buffer (there is data from packet 5 there only).
If requests queue is not empty, usb_isochronous_transfer_service() extracts the
request from the queue and starts writing to the FIFO from the second packet of the next
buffer. If the length of that next buffer is 1 packet only, it extracts one more request.
If requests queue is empty, usb_isochronous_transfer_service() completes the
current transfer.
SOF6 occurred, therefore no data will be sent is this frame (similar situation to the one
described in section 6.1, remark 2). Thus, if there were no requests in a queue,
usb_tx_data() must step over the first packet in a new buffer and start placing second
packet into the FIFO. That second packet will be sent in a seventh frame.
usb_isochronous_transfer_service() function sets appropriate status for the
usb_tx_data():
iso_ep[epnum].state = SKIP_FIRST_PACKET;
So, if the Host misses several frames, it does not receive data in these frames, and it does
not receive data in the next frame either (even if it issued IN token). In the next frame the
Host may receive a few bytes of garbage – bytes that were sent before starting to clear the
FIFO. Thus, EOP7 interrupt may occur, but it is a spurious interrupt. If this interrupt
occurs, it occurs immediately following the SOF6 interrupt. Even if EOP7 occurs after
the call to write() (usb_tx_data()), this situation will be handled properly as well
(see case 1).
If the Host misses only two frames and misses them one after the other, it does not
receive garbage bytes and the Device does not overstep the third packet (this takes place
in all cases).
M
Isochronous Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
6-8
6.4. Host-to-Device Data Transfer.
This subsection describes the concepts of isochronous OUT transfer. The following two
sections describe how the Driver monitors whether Host software and the device-side
Client application are working in real-time. It also describes how the Driver sustains
sample rate if the Host s/w misses frames.
For OUT transfers, alike for IN, the following is true:
1. Isochronous packets are guaranteed to occur once per USB frame and they are
never resent.
2. Isochronous endpoints support packet sizes up to 1023 bytes. Which means that
the FIFO size can be less than twice the packet size. Thus, during packet
reception, a FIFO level interrupt can occur. Using this interrupt, the Driver reads
the initial bytes of a packet. Then (using the FIFO level interrupt again or EOP
interrupt), it reads the rest of the packet.
Data on isochronous endpoints is generally streaming data. So it can be assumed that all
such transfers on each isochronous endpoint belongs to a corresponding stream, that was
started much earlier and will never finish. When data arrives at a USB module, the FIFO
level or/and EOP interrupts occur. At this moment the Client program should allocate a
buffer for data, by calling the usb_rx_data() or usb_rx_frame() function.
The USB Driver operates using two different methods for isochronous OUT transfer.
1. The first is similar to CBI transfers. As for this method, the Client application
must call usb_rx_data(). The Driver does not return control until all the data is
received. But this method of reading is not synchronized with USB timing. Thus,
using this method (READ_DATA), the Client program may have a problem to
determine the USB data rate.
2. The second method (READ_FRAMES) is synchronized with the USB clock. In this
mode the Client program must call the usb_rx_frame() function to get data from
a given number of frames (refer to Chapter 7 for detailed description of this
function). The Client program knows the time (it passes the frame number, i.e.
number of milliseconds, to the Driver), and the Driver fills the buffer with data
that it received from the Host during a given period. It frees up the
ep[epnum].buffer structure, when a given number of frames (not an amount of
data !) is received. (the Client application must take care of buffer’s lengths – the
safest way is to anticipate all packets to be of maximum size). By means of this
the data rate can be easily determined. If the data rate does not suit the Client
program, the application may send feedback to the Host, asking for a desired
sample rate, or implement a sample rate conversion – Client dependant. The use
of this method of reading data is strongly recommended for isochronous transfers.
Regardless of the method chosen by the program, the Driver notifies the Client
application by calling it's usb_ep_rx_done() function, passing a status of reading (see
next section), and the number of read bytes to it. Following this the Driver frees up the
ep[epnum].buffer structure. In order to work in real-time, the Client program must call
M
Isochronous Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
6-9
usb_rx_frame() or usb_rx_data() before a FIFO level or EOP interrupt for the
following packet occurs.
If usb_rx_frame() or usb_rx_data() returns control, it does not mean that all
frames/data are received. To know when the transfer is completed, the Client application
must use the usb_rx_done() notification or the usb_ep_wait() (usb_ep_is_busy())
function.
6.5. Monitoring the Host Software During OUT
Transfers.
There is a wide class of audio devices, which steadily consume (sink devices) a fixed
amount of data (e.g. headphones). The DAC of a headphone supplies a fixed amount of
digital samples during some period of time. Therefore the Device has to receive all of this
data during a given time period (or at least, a buffer in which data is placed must be freed
by the end of that period).
Let’s assume, that the Device must receive 5 packets of 16 bytes from the Host and then
output the received data to headphones during 5 ms. If the Host missed a frame (in some
frame did not send a packet), the Device needs more than 5 ms to receive the 5 packets,
but the data must be output to headphones exactly after a given period.
The Driver is able to address this problem. The Driver guarantees that the buffer will be
freed after a required time, even if the Host missed packets. If the Host did not send some
packets, the Client application will know about it from the buffer’s header, and may
interpolate missed samples or mute the output. In any case, the program may synthesize
the required amount of samples and output them to the headphones in the required time.
If the Client application requests the Driver to perform transfer monitoring, it must call
the USB_SET_START_FRAME ioctl. The Driver starts analyzing the transfer from a given
frame, the number of which was passed as a parameter to that call. It must be the number
of the frame in which the first data packet has to be received from the Host. All the
transfers after this frame will be monitored. When the last transfer is completed, data
monitoring must be stopped (in order to correctly start a new one, or to properly continue
data transfer without monitoring).
In order to stop monitoring, the Client program must call the USB_SET_START_FRAME
ioctl, passing the number of the frame in which data monitoring must be stopped. This
must be done in the frame following the one, in which the last data packet has to be sent
by the Host (or at least, not earlier). The SOF interrupt handler of the next frame checks
missed EOP interrupt in the previous frame. In such a case, the Driver can properly
handle the situation, when last packet was not received by the Device.
M
Isochronous Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
6-10
The Driver monitors Host s/w activity by incrementing the counter in the
usb_isochronous_transfer_service() function:
if
(iso_ep[epnum].
transfer_monitoring_started
==
TRUE)
{
iso_ep[epnum].sent_packet_watch ++;
/* It must be 1, now */
…
The Driver clears it in the usb_out_service(), if EOP interrupt occurred:
iso_ep[epnum].sent_packet_watch = 0;
If an EOP interrupt did not occur during the frame, the Driver sets corresponding bit in
the status (next call to usb_isochronous_transfer_service() function):
if (iso_ep[epnum].sent_packet_watch > 1)
{
/* Reset the counter */
iso_ep[epnum].sent_packet_watch = 1;
ep[epnum].buffer.position +=
iso_ep[epnum].packet_length[iso_ep[epnum].frame_counter];
iso_ep[epnum].packet_length[iso_ep[epnum].frame_counter] = 0;
…
6.6. Monitoring the Device-side Application
During OUT Transfers.
The Driver also handles the situation when the Client program isn’t working in a real
time. If the FIFO level or EOP interrupt occurred but no buffer is allocated, the Driver
sets the appropriate status (in usb_out_service() function):
if ((ep[epnum].ttype == ISOCHRONOUS) &&
((ep[epnum].buffer.start == 0) || (iso_ep[epnum].state
== MISS_PACKET)))
{
/* Clear FIFO buffer */
MCF5272_WR_USB_EPCFG(imm,
epnum,
MCF5272_RD_USB_EPCFG(imm, epnum));
if (event & MCF5272_USB_EPNISR_FIFO_LVL)
iso_ep[epnum].state = MISS_PACKET;
if (event & MCF5272_USB_EPNISR_EOP)
iso_ep[epnum].state = DEFAULT;
M
Isochronous Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
6-11
/* It is nothing to read anymore */
fifo_data = 0;
}
If a FIFO level interrupt occurs, the Driver clears the FIFO buffer and sets the state field
to MISS_PACKET. This done where the Client program may call the read() function
before FIFO level and EOP interrupts. Moreover, the first sample in the FIFO buffer can
be damaged after previous clearing. Thus, the whole packet must be read out and after
EOP has occurred, state field set to DEFAULT.
M
Isochronous Data Transfer.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
6-12
7. Vendor Request Handling.
For most of the standard device requests, the MCF5272 USB module handles them
automatically. GET_DESCRIPTOR (string descriptors only) and SYNC_FRAME requests are
passed to the user (Driver) as a vendor specific request, and in those cases the Driver
handles them like any other vendor specific request. This chapter describes how the
Driver accepts different types of request (data IN, data OUT, and NO data stage) from the
Host and passes them to the Client application.
7.1. Accepting a request from the Host.
The Driver responds to requests from the host on the device’s Default Control Pipe.
These requests are made using control transfers. The request and the request’s parameters
are sent to the device in the Setup packet.
VEND_REQ interrupt is used to notify the device about accepting a request. When the
Driver detects assertion of VEND_REQ interrupt, it calls the usb_vendreq_service()
function from the interrupt handler for endpoint number zero:
usb_vendreq_service(
(uint8)(MCF5272_RD_USB_DRR1(imm) & 0xFF),
uint8)(MCF5272_RD_USB_DRR1(imm) >> 8),
(uint16)(MCF5272_RD_USB_DRR1(imm) >> 16),
(uint16)(MCF5272_RD_USB_DRR2(imm) & 0xFFFF),
(uint16)(MCF5272_RD_USB_DRR2(imm) >> 16));
Device request data registers are used to notify that a standard, class-specific, or vendorspecific request has been received and to pass the request type and its parameters.
Interrupt handler for endpoint number zero reads bmRequestType, bRequest, and
wValue parameters from register DRR1, and wIndex, wLength parameters from register
DRR2 and passes them to usb_vendreq_service().
The usb_vendreq_service() function determines the type of request (data IN
command, data OUT command, no data stage) and handles it appropriately.
The Driver notifies the Client application about the new request by sending SIGIO signal.
After that the Client shall call USB_GET_COMMAND ioctl and pass to the Driver the pointer
to DEVICE_COMMAND structure. The definition of that structure is shown below:
/* Structure for Request */
typedef struct {
uint8 bmRequestType;
uint8 bRequest;
uint16 wValue;
uint16 wIndex;
uint16 wLength;
} REQUEST;
M
Vendor Request Handling.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
7-1
/* Structure for Command Buffer for Client*/
typedef struct {
uint8 * cbuffer; /* Pointer to command block buffer */
REQUEST request; /* Request from Host*/
} DEVICE_COMMAND;
The REQUEST structure contains request parameters, the cbuffer field points to the start
of the command block. The length of the command block is equal to the
request.wLength field. The cbuffer field is used only if a request has data stage and
the direction of data transfer is from Host to Device. Otherwise, cbuffer is not
initialized. A more detailed description of request handling is given in following
subsections.
7.2. Data OUT request handling.
The direction of data transfer is determined by bmRequestType[D7] parameter [1]. If that
bit is cleared (bmRequestType < 128) and there is a data stage in a request, it is a case of
Data OUT command:
if ((bmRequestType < 128) && (wLength > 0))
{
/* Allocate memory for a new command */
/* There is a data stage in this request and direction of
data transfer is from Host to Device */
NewC
=
wLength,
GFP_ATOMIC);
(DEVICE_COMMAND
*)
kmalloc(sizeof(DEVICE_COMMAND)
+
/* Store the address where new command will be placed */
NewC -> cbuffer = (uint8 *) NewC + sizeof(DEVICE_COMMAND);
}
The Driver allocates memory for the request itself and for the command that will be
received in the data stage (the length of command is determined by wLength).
If the Driver is unable to allocate memory, it sends a STALL response to the Host by
calling the usb_vendreq_done() function:
if (NewC == NULL)
{
…
usb_vendreq_done(MALLOC_ERROR);
After allocating memory, the Driver stores request parameters into the structure NewC ->
request.
M
Vendor Request Handling.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
7-2
Finally, usb_vendreq_service() function initializes the ep[0].buffer structure to
accept a command in the data stage. When data (command block) occurs on endpoint
number zero, the usb_out_service() function will be called and receive a command.
When a command is received, the Driver sends SIGIO signal to notify the Client
application about a new command:
if ((epnum == 0) && (NewC))
{
/* We have got a new command and can wake up fetch_command
routine */
wake_up_interruptible(&fetch_command_queue);
/* and notify Client about it */
if (usb_async_queue) kill_fasync(&usb_async_queue, SIGIO,
POLL_IN);
}
To access a command, the Client application must call USB_GET_COMMAND ioctl and use
cbuffer field (defined in DEVICE_COMMAND structure). The program may check if it
supports that command, it may execute it immediately or put it into the Queue for later
execution. In any case, Client shall then call USB_COMMAND_ACCEPTED or
USB_NOT_SUPPORTED_COMMAND ioctl to indicate if it accepts a command or not. The
appropriate status will be sent in a status stage of the command transfer. The Client
program must return status as soon as possible – the time for sending status of accepting a
command in status stage is limited by USB 1.1 specification.
Having that status, the Driver calls usb_vendreq_done() function to complete a
command transfer. If status is bad, usb_vendreq_done() sends a STALL response.
7.3. Data IN request handling.
If the direction of data transfer is from Device to Host, the Driver allocates memory for
DEVICE_COMMAND structure only:
/* Direction of data transfer is from Device to Host, or no data
stage */
NewC
=
(DEVICE_COMMAND
*)
kmalloc(sizeof(DEVICE_COMMAND),
GFP_ATOMIC);
If the Driver is unable to allocate memory for any reason, it sends zero-length packet to
indicate end of transfer (no data will be provided) and STALL handshake to the Host:
if (NewC == NULL)
{
…
if ((wLength != 0) && (bmRequestType > 127))
/* The direction of data transfer is from Device to
Host,
M
Vendor Request Handling.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
7-3
send zero-length packet to indicate no data will be
provided */
MCF5272_WR_USB_EP0CTL(imm, MCF5272_RD_USB_EP0CTL(imm)
& (~ MCF5272_USB_EP0CTL_IN_DONE));
usb_vendreq_done(MALLOC_ERROR);
After allocating memory, the Driver stores request parameters into the structure NewC ->
request.
Then, the Driver sends SIGIO signal to notify the Client application about a new
command:
if
(usb_async_queue)
kill_fasync(&usb_async_queue,
SIGIO,
POLL_IN);
/* We have got a new command and can wake up fetch_command
routine */
wake_up_interruptible(&fetch_command_queue);
To access a command, the Client application must call the USB_GET_COMMAND ioctl.
The Client application must decide either it accepts a command or not. If it does not
accept a command, it must call USB_NOT_SUPPORTED_COMMAND ioctl. As a result, the
Driver will send zero-length packet and a single STALL handshake to the Host indicating
that no data will be provided and the command failed:
case USB_NOT_SUPPORTED_COMMAND:
if (NewC)
{
if
((NewC->request.bmRequestType
>
127)
&&
(NewC>request.wLength != 0))
MCF5272_WR_USB_EP0CTL(imm, MCF5272_RD_USB_EP0CTL(imm)
& (~ MCF5272_USB_EP0CTL_IN_DONE));
usb_vendreq_done(NOT_SUPPORTED_COMMAND);
kfree(NewC);
NewC = NULL;
return SUCCESS;
}
If the Client program accepts a command, it may answer with data immediately (call
write() function) from the SIGIO signal handler (endpoint number zero is free now), or
put it to the Queue for later execution. In any case, the Client application must call
write() function on endpoint 0, to transfer data upon request. Also the Client program
must do it as soon as possible – the time for sending a command in data stage from
Device to Host is limited by USB 1.1 specification. Sending data will invoke the calling
the usb_in_service() function, which completes command transfer:
M
Vendor Request Handling.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
7-4
…
if ((epnum == 0) && NewC)
{
usb_vendreq_done(SUCCESS);
…
7.4. No data stage request handling.
If there is no data stage in a request, the Driver allocates memory for DEVICE_COMMAND
structure only:
/* Direction of data transfer is from Device to Host, or no data
stage */
NewC
=
(DEVICE_COMMAND
*)
kmalloc(sizeof(DEVICE_COMMAND),
GFP_ATOMIC);
If the Driver is unable to allocate memory, it sends STALL response to the Host by
calling usb_vendreq_done() function:
if (NewC == NULL)
{
…
usb_vendreq_done(MALLOC_ERROR);
After allocating memory, the Driver stores request parameters into the structure NewC ->
request.
Then the Driver sends SIGIO signal to notify the Client application about a new
command. To access the command, the Client application must call USB_GET_COMMAND
ioctl. The Client application may accept (USB_COMMAND_ACCEPTED ioctl) or reject
(USB_NOT_SUPPORTED_COMMAND ioctl) a command, execute it immediately or put it into
the Queue for later execution.
M
Vendor Request Handling.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
7-5
8. Miscellaneous Operations.
This chapter describes how the Driver handles port-reset, change configuration events
and how it notifies the Client application.
8.1. Port Reset Handling.
When a reset event occurs, the Driver calls usb_bus_state_chg_service() function
from the interrupt handler for endpoint number zero, passing the RESET value as a
parameter into it. The reset event handler clears the ep[epnum].buffer structure for all
endpoints, sets the state of each endpoint to USB_DEVICE_RESET, and deletes a
command if NewC variable points to it.
After that, the Driver sets SIGIO signal to notify the Client application about the reset
event. The Client application shall call USB_GET_COMMAND ioctl, which will return
USB_DEVICE_RESET.
A reset event may occur at any time – during execution of usb_tx_data(),
usb_rx_data(), usb_in_service(), or usb_out_service(). To ensure each
routine will be completed properly, RESET interrupt must be disabled before starting to
work with buffers, and restored after data copying is completed. Otherwise, the RESET
event handler may be called during data copying. In this case, it clears the pointer to an
intermediate buffer, and then the interrupted function will read/write from/to zero
address.
The reset event handler clears the ep[epnum].buffer structure:
for (i=0; i< NUM_ENDPOINTS; i++)
{
ep[i].buffer.start = 0;
ep[i].buffer.length = 0;
ep[i].buffer.position = 0;
ep[i].state = USB_DEVICE_RESET;
}
The global structure must be set up to its default value (no buffers allocated). This
prevents usb_in_service() and usb_out_service() from copying data (a way to
terminate transfers that are in progress).
The reset event handler sets the state field of each endpoint to the USB_DEVICE_RESET
value. This prevents the Client application from starting new transfers on an unconfigured
device. It does not extend to endpoint number zero – according to [1], transfers on
endpoint number zero are permitted for an unconfigured device.
M
Miscellaneous Operations.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
8-1
Functions write() (usb_tx_data()), read() (usb_rx_data()), usb_ep_wait(),
and usb_ep_is_busy() examine the state field if they are called for a non zero
endpoint. If the device is reset but not yet configured, they return the USB_DEVICE_RESET
value (read and write return -1).
8.2. Change of Configuration Handling.
A DEV_CFG interrupt may occur at any time – during execution of usb_tx_data(),
usb_rx_data(), usb_in_service(), or usb_out_service(). To ensure each routine
will be completed properly, that interrupt must be disabled before starting working with
buffers, and restored after data copying is finished. Otherwise the set configuration event
handler may be called during data copying, which clears the pointer to an intermediate
buffer, and then interrupted function will read/write from/to zero address.
To handle the set configuration event (dev_cfg interrupt), the Driver calls the
usb_devcfg_service() function. This function clears the ep[epnum].buffer structure
for all endpoints. This prevents usb_in_service() and usb_out_service() from
operating with data (a way to terminate current data transfers). Then, that function sets
ep[epnum].state field to USB_CONFIGURED. So, new transfers will be permitted for all
endpoints from then on.
Next, the Driver sets SIGIO signal to notify the Client application that the new
?onfiguration / interface / alternate setting is set up. The Client application shall call
USB_GET_COMMAND ioctl, which will return USB_CONFIGURATION_CHG. Then
USB_GET_CURRENT_CONFIG ioctl may be called to get the number of new configuration
and alternate setting. This call takes the pointer to the following structure as parameter:
struct {
uint32 cur_config_num;
uint32 altsetting;
} CONFIG_STATUS;
8.3. Example of events handling in Client
application.
The following code example demonstrates how to handle the Change of Configuration,
Reset and new command receiving events in the Client application.
/* Get event from the Driver */
event = ioctl(usb_dev_file, USB_GET_COMMAND, &dc);
/* If new command has arrived - process it */
if (event & USB_NEW_COMMAND)
{
/* Test if this command is a request for string descriptor
*/
M
Miscellaneous Operations.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
8-2
if ((dc.request.bmRequestType == 0x80) &&
(dc.request.bRequest == GET_DESCRIPTOR) &&
((dc.request.wValue >> 8) == STRING))
{
get_string_descriptor(dc.request.wValue & 0xFF,
dc.request.wIndex,
dc.request.wLength);
return;
}
…
…
…
}
if (event & USB_CONFIGURATION_CHG)
{
…
/* Host has changed configuration of device (or set new alt
setting number) */
}
if (event & USB_DEVICE_RESET)
{
/* Port RESET has occured */
/* Reset signal may be processed here */
}
M
Miscellaneous Operations.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
8-3
9. USB Device Driver Function
Specification.
This chapter describes functions implemented in the USB device Driver.
Function arguments for each routine are described as in, inout. An in argument
means that the parameter value is an input only to the function. An inout argument
means that a parameter is an input to the function, but the same parameter is also an
output from the function. Inout parameters are typically input pointer variables in which
the caller passes the address of a pre-allocated data structure to a function. The function
stores its result within that data structure. The actual value of the inout pointer parameter
is not changed.
9.1. usb_bus_state_chg_service.
Call(s):
void usb_bus_state_chg_service(uint32 event);
Arguments:
Table 9-1. usb_bus_state_chg_service arguments
in
Occurred event such as RESET, SUSPEND, etc.
event
Description: This function handles RESUME, SUSPEND, and RESET interrupts. It is
called from the interrupt handler for endpoint number zero
(usb_endpoint0_isr() function).
Returns:
No value returns.
Code example:
if (event & MCF5272_USB_EP0ISR_RESET)
{
usb_bus_state_chg_service(RESET);
…
}
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-1
9.2. usb_devcfg_service.
Call(s):
void usb_devcfg_service (void);
Arguments:
No arguments.
Description: This function handles DEV_CFG interrupt. It is called from the interrupt
handler for endpoint number zero (usb_endpoint0_isr() function) when
the Host sets or changes the configuration.
Returns:
No value returns.
Code example:
if (event & MCF5272_USB_EP0ISR_DEV_CFG)
{
usb_devcfg_service();
…
}
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-2
9.3. usb_endpoint0_isr.
Call(s):
void usb_endpoint0_isr (int irq, void *dev_id, struct pt_regs *regs);
Arguments:
irq
dev_id
regs
Table
in
in
in
9-2. usb_endpoint0_isr arguments
Number of interrupt that occurred
Device id (not used)
Pointer to registers structure (not used)
Description: This function handles all interrupts that occur for endpoint number zero. It
is called from the uClinux interrupt handler.
Returns:
No value returns.
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-3
9.4. usb_endpoint_isr.
Call(s):
void usb_endpoint_isr (int irq, void *dev_id, struct pt_regs *regs);
Arguments:
irq
dev_id
regs
Table
in
in
in
9-3. usb_endpoint_isr arguments
Number of interrupt that occurred
Device id (not used)
Pointer to registers structure (not used)
Description: This function handles all interrupts for all endpoints available in the
current configuration, except of endpoint number zero. It is called from the
uClinux interrupt handler.
Returns:
No value returns.
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-4
9.5. usb_ep_is_busy, USB_EP_BUSY ioctl
command.
Call(s):
uint32 usb_ep_is_busy(uint32 epnum);
uint32 ioctl(int fd, USB_EP_BUSY)
Arguments:
Epnum
Table 9-4. usb_ep_is_busy arguments
in
Number tested for busy endpoint.
Description: usb_ep_is_busy() is called from USB_EP_BUSY ioctl command
handler. It tests an endpoint for busy state. The endpoint remains busy
while a non-zero value is assigned to ep[epnum].buffer.start field.
Returns:
Table 9-5. usb_ep_is_busy returned values
-USB_DEVICE_RESET
Device is reset
-USB_EP_IS_BUSY
Endpoint is busy
-USB_EP_IS_FREE
Endpoint is free
Code example:
uint32
ep_status;
int usb_ep2;
…
ep_status = ioctl(usb_ep2, USB_EP_BUSY);
if (ep_status == USB_EP_IS_FREE)
{
/* Endpoint is free. New transfer can be started */
…
}
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-5
9.6. USB_EP_STALL ioctl command.
Call(s):
void ioctl(int fd, USB_EP_STALL)
Arguments:
No arguments.
Description: This call halts a non-zero endpoint. It causes the endpoint to return STALL
handshake when polled by either the IN or OUT token by the USB host
controller.
Returns:
No value returns.
Code example:
ioctl(usb_ep2, USB_EP_STALL);
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-6
9.7. usb_ep_wait, USB_EP_WAIT ioctl command.
Call(s):
uint32 usb_ep_wait (uint32 epnum);
uint32 ioctl(int fd, USB_EP_WAIT)
Arguments:
epnum
Table 9-6. usb_ep_wait arguments
in
Number tested for busy endpoint.
Description: this call does not return control while endpoint is busy or while the number
of requests in the queue is more than specified in the parameter (for
Isochronous transfers only). It calls usb_ep_wait() (see Chapter 9.10).
Returns:
Table 9-7. usb_ep_wait returned values
-USB_DEVICE_RESET
Device is reset or was reset but Client
application was not notified about it
Positive value (CBI transfers The number of bytes transferred
only)
-USB_EP_IS_FREE (ISO
Endpoint is free
transfer only)
Code example:
uint16
status;
int usb_ep3;
…
ioctl(usb_ep3, USB_EP_WAIT, INTERRUPT);
write(usb_ep3, (uint8 *)(&status), 2);
…
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-7
9.8. usb_fetch_command, USB_GET_COMMAND
ioctl command.
Call(s):
uint32 usb_fetch_command(DEVICE_COMMAND * Client_item);
uint32 ioctl(int fd, USB_EP_BUSY, DEVICE_COMMAND * Client_item)
Arguments:
Table 9-8. usb_fetch_command arguments
Client_item
inout Pointer to the DEVICE_COMMAND structure allocated
by Client application. Client_item points to address
were Driver must place a command.
Description: usb_fetch_command() is called from the USB_GET_COMMAND ioctl
command handler. It copies the command to the address, contained in the
Client_item parameter. The only one field of the structure must be
initialized by the Client application before the address of that structure will
be passed to usb_fetch_command() as a parameter – cbuffer (pointer to
the command buffer). The Client application must allocate memory for the
DEVICE_COMMAND structure. Then allocate memory for the command
buffer. Then, store the address of the command buffer to the cbuffer field
of the DEVICE_COMMAND structure. Finally, pass an address of the
DEVICE_COMMAND structure to the usb_fetch_command() function. This
function sleeps while there is no new command.
Returns:
Return value is a mask of following bits:
Table 9-9. usb_fetch_command returned values
USB_DEVICE_RESET
Device is not configured or was reset
USB_NEW_COMMAND
The New Command is received.
USB_CONFIGURATION_CHG
DEV_CFG
event
has
occurred
(configuration changed)
Code example:
DEVICE_COMMAND command;
uint8 cb[COMMAND_BUFFER_LENGTH];
…
command.cbuffer = cb;
if (ioctl(usb_ep0, USB_GET_COMMAND, &command) & USB_NEW_COMMAND)
{
/* Process new command */
…
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-8
9.9. usb_fifo_init.
Call(s):
void usb_fifo_init(void);
Arguments:
No arguments.
Description: This function initializes the FIFO for current configuration. It calculates
the start address and the length of the FIFO buffer for each endpoint and
stores these values into the corresponding configuration register.
Returns:
No value returns.
Code example:
…
usb_fifo_init();
…
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-9
9.10.
USB_GET_CURRENT_CONFIG ioctl
command.
Call(s):
uint32 ioctl(int fd, USB_GET_CURRENT_CONFIG, CONFIG_STATUS *
cconfig);
Arguments:
Pointer to CONFIG_STATUS structure
Returns:
This function returns the number of the current configuration, number of
interface and number of alternate setting for every active interface.
Code example:
…
ioctl(usb_dev_file, USB_GET_CURRENT_CONFIG, &current_config);
…
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-10
9.11.
USB_GET_FRAME_NUMBER ioctl
command.
Implemented for CBI & Isochronous Driver only.
Call(s):
uint16 ioctl(int fd, USB_GET_FRAME_NUMBER);
Arguments:
no arguments.
Returns: Function returns the contents of FNR (Frame Number Register). This value is
in a range from 0 to 2047.
Code example:
…
fr_num = ioctl(usb_dev_file, USB_GET_FRAME_NUMBER);
…
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-11
9.12.
usb_get_desc.
Call(s):
uint8* usb_get_desc(int8 config, int8 iface, int8 setting, int8 ep);
Arguments:
Table 9-10. usb_get_desc arguments
in
Number of configuration
in
Number of interface
in
Number of alternate settings
in
Endpoint number
config
iface
setting
ep
Description: This function returns the pointer to the required descriptor. If config
parameter is equal to –1, it returns pointer to the device descriptor. If
iface and setting are equal to –1 but config contains the number of
configuration, it returns the pointer to the configuration descriptor of the
configuration having number config. If ep is equal to –1, but all previous
parameters are properly initialized, the function returns a pointer to the
corresponding interface descriptor for a given configuration. If all
parameters are initialized by a non –1 value, usb_get_desc() returns a
pointer to the endpoint descriptor for the given configuration, interface and
alternate setting. The ep parameter is offset and not actually the physical
endpoint number.
Returns:
Pointer to required descriptor.
Code example:
USB_CONFIG_DESC *pCfgDesc;
…
/* Get pointer to active Configuration descriptor */
pCfgDesc = (USB_CONFIG_DESC *)usb_get_desc(new_config, -1, -1, 1);
…
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-12
9.13.
usb_get_request.
Call(s):
uint32 usb_get_request (uint32 epnum);
Arguments:
epnum – the number of endpoint.
Description: This function extracts the request (new buffer’s start address and length)
from the Driver’s requests queue for the specified endpoint.
Returns:
TRUE
FALSE
Table 9-11. usb_get_request return values
in
Function successfully completed
in
Queue is empty
Code example:
…
usb_get_request(epnum);
…
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-13
9.14.
usb_init, USB_INIT ioctl command.
Call(s):
void usb_init(DESC_INFO * descriptor_info);
void ioctl(int fd, USB_INIT, DESC_INFO * descriptor_info);
Arguments:
Table 9-12. usb_init arguments
descriptor_info in
Pointer to the structure that contains pointer to device
descriptor and size of device descriptor
Description: usb_init() is called from the USB_INIT ioctl command handler. It
initializes the USB device Driver. It stores the initial values to global
variables, initializes interrupts, loads descriptors to configuration memory
and initializes the FIFO buffer.
Returns:
No value returns.
Code example:
DESC_INFO device_desc;
…
device_desc.pDescriptor = (uint8 *) &Descriptors;
device_desc.DescSize = usb_get_desc_size();
device_desc.pStrDescriptor = (uint8 *) &string_desc;
ioctl(usb_ep0, USB_INIT, &device_desc);
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-14
9.15.
usb_in_service.
Call(s):
void usb_in_service(uint32 epnum, uint32 event);
Arguments:
epnum
event
Table 9-13. usb_in_service arguments
in Number of endpoint
in Events occurred for epnum endpoint
Description: This function handles FIFO_LVL, EOP and EOT interrupts for all IN
endpoints in the current configuration.
Returns:
No value returns.
Code example:
if (event & ( MCF5272_USB_EPNISR_EOT
| MCF5272_USB_EPNISR_EOP
| MCF5272_USB_EPNISR_FIFO_LVL))
{
/* IN Endpoint */
if
(MCF5272_RD_USB_EPISR(imm,
MCF5272_USB_EPNISR_DIR)
usb_in_service(epnum, event);
M
epnum)
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
&
9-15
9.16.
usb_isochronous_transfer_service.
Implemented for CBI & Isochronous Driver only.
Call(s):
void usb_isochronous_transfer_service(void);
Arguments:
No arguments.
Description: This function is used to properly start and stop an IN/OUT isochronous
data stream. It also monitors the Host s/w and device side Client
application, as to whether they are working in real time.
Returns:
No value returns.
Code example:
if (event & MCF5272_USB_EP0ISR_SOF)
{
/* Clear this interrupt bit */
MCF5272_WR_USB_EP0ISR(imm, MCF5272_USB_EP0ISR_SOF);
usb_isochronous_transfer_service();
}
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-16
9.17.
usb_isr_init.
Call(s):
void usb_isr_init(void);
Arguments:
No arguments.
Description: This function initializes interrupts for the USB module.
Returns:
No value returns.
Code example:
…
usb_isr_init();
…
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-17
9.18.
usb_make_power_of_two.
Call(s):
void usb_make_power_of_two(uint32 *size);
Arguments:
Table 9-14. usb_make_power_of_two arguments
inout Pointer to the value that must be power of two
size
Description: This function makes a power of two of the value pointed by the size
parameter. If the pointed value is not a power of two, the function
increases it to the nearest power of two. If the result is larger than 256, 256
is assigned to the result value.
Returns:
No value returns.
Code example:
/* Make sure FIFO size is a power of 2; if not, make it so */
for (i = 0; i < nIN; i++)
usb_make_power_of_two( &(pIN[i]->fifo_length) );
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-18
9.19.
usb_out_service.
Call(s):
void usb_out_service(uint32 epnum, uint32 event);
Arguments:
epnum
event
Table 9-15. usb_out_service arguments
in Number of endpoint
in Events occurred for epnum endpoint
Description: This function handles FIFO_LVL, EOP and EOT interrupts for all OUT
endpoints in the current configuration.
Returns:
No value returns.
Code example:
if (event & ( MCF5272_USB_EPNISR_EOT
| MCF5272_USB_EPNISR_EOP
| MCF5272_USB_EPNISR_FIFO_LVL))
{
/* IN Endpoint */
if
(MCF5272_RD_USB_EPISR(imm,
MCF5272_USB_EPNISR_DIR)
usb_in_service(epnum, event);
epnum)
&
/* OUT Endpoint */
else
usb_out_service(epnum,event);
}
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-19
9.20.
usb_rx_data.
Call(s):
uint32 usb_rx_data(uint32 epnum, uint8 *start, uint32 length);
Arguments:
epnum
start
length
Table 9-16. usb_rx_data arguments
in
Number of endpoint through which data will be received
from Host
inout Pointer to buffer where Driver will place received data
from Host
in
Number of bytes to receive
Description: This function is called from the usb_device_read() (the handler of
read() call). It initializes ep[epnum].buffer structure with values start
and length. Copies the contents of the FIFO buffer for endpoint epnum to
the destination buffer pointed by start. If it was all expected data, it
clears the ep[epnum].buffer structure.
Returns:
Table 9-17. usb_rx_data returned values
-EIO
Device is not configured or was reset but
Client application was not notified, or EP is
halted
-EASSEC
Device was reconfigured but Client application
was not notified about it
-EFAULT
Parameters passed to function are not properly
initialized
-EBUSY
Given endpoint is not ready to receive new
data
Number of bytes read
The function completed successfully
Code example:
usb_rx_data(MINOR(GET_RDEV(file)), (uint8 *) buffer, (uint32) length)
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-20
9.21.
USB_SEND_ZLP ioctl command.
Call(s):
void ioctl(int fd, USB_SET_SEND_ZLP);
Arguments:
No arguments.
Description: This command sets sendZLP variable to TRUE for the appropriate endpoint.
Returns:
No value returns.
Code example:
…
ioctl(usb_ep0, USB_SET_SEND_ZLP);
…
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-21
9.22.
USB_SET_FINAL_FRAME ioctl command.
Implemented for CBI & Isochronous Driver only.
Call(s):
void ioctl(int fd, USB_SET_FINAL_FRAME, uint32 frame_num);
Arguments:
frame_num - Number of frame in which stream will be closed.
Description:
This function sets a frame, in which a data stream will be closed. When the data
stream is closed, the Driver does not monitor the Host s/w activity and device-side
application. It also permits to properly start (synchronously with the Host) a new
data stream.
Returns:
No value returns.
Code example:
…
ioctl(usb_dev_file, USB_SET_FINAL_FRAME, st_frame);
…
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-22
9.23.
USB_SET_START_FRAME ioctl
command.
Implemented for CBI & Isochronous Driver only.
Call(s):
void ioctl(int fd, USB_SET_START_FRAME, uint32 frame_num);
Arguments:
frame_num - Number of frame in which stream will be started.
Description:
This function sets a frame, in which a data stream will be started. It permits data
transfer to start synchronously with the Host.
Returns:
No value returns.
Code example:
…
ioctl(usb_dev_file, USB_SET_START_FRAME, st_frame);
…
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-23
9.24.
usb_sort_ep_array.
Call(s):
void usb_sort_ep_array(USB_EP_STATE *list[], int n);
Arguments:
Table 9-18. usb_sort_ep_array arguments
inout Pointer to the array of USB_EP_STATE elements
in
Number of elements in the array pointed by list
List
N
Description: This function sorts element in the array pointed by list in descending
order.
Returns:
No value returns.
Code example:
/* Sort the endpoints by FIFO length (decending) */
usb_sort_ep_array(pIN, nIN);
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-24
9.25.
usb_tx_data.
Call(s):
uint32 usb_tx_data(uint32 epnum, uint8 *start, uint32 length);
Arguments:
epnum
start
length
Table 9-19. usb_tx_data arguments
in
Number of endpoint through which data will be
transferred to Host
inout Pointer to buffer from where Driver will place data to
FIFO buffer
in
Number of bytes to send
Description: This function is called from the usb_device_write() (the handler of
write() call). It initializes ep[epnum].buffer structure with values
start and length. Copies the contents of the source buffer to the FIFO
buffer.
Returns:
Table 9-20. usb_tx_data returned values
-EIO
Device is not configured or was reset but
Client application was not notified, or EP is
halted
-EASSEC
Device was reconfigured but Client application
was not notified
-EFAULT
Parameters passed to function are not properly
initialized
-EBUSY
Given endpoint is not ready to send new data
Number of bytes written
The function completed successfully
Code example:
usb_tx_data(MINOR(GET_RDEV(file)),
length);
M
(uint8
*)
buffer,
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
(uint32)
9-25
9.26.
usb_vendreq_done.
Call(s):
void usb_vendreq_done(uint32 error);
Arguments:
Table 9-21. usb_vendreq_done arguments
in
Status of command completion
error
Description: This function sets EP0CTL[CMD_OVER] bit if error is zero and
EP0CTL[CMD_OVER], EP0CTL[CMD_ERR] bits if error contains a
non-zero value.
Returns:
No value returns.
Code example:
void
usb_ep_rx_done(uint32 status)
{
usb_vendreq_done(status);
…
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-26
9.27.
usb_vendreq_service.
Call(s):
void usb_vendreq_service(uint8
bmRequestType, uint8
wValue,
uint16 wIndex, uint16 wLength);
bRequest, uint16
Arguments:
Table
bmRequestType
bRequest
wValue
wIndex
wLength
9-22.
in
in
in
in
in
usb_vendreq_service arguments
Standard request parameters.
For more information refer to USB 1.1 specification
(Chapter 9.3)
Description: This function receives a request from the Host and allocates memory for
the request.
Returns:
No value returns.
Code example:
usb_vendreq_service(
(uint8)(MCF5272_RD_USB_DRR1(imm) & 0xFF),
(uint8)(MCF5272_RD_USB_DRR1(imm) >> 8),
(uint16)(MCF5272_RD_USB_DRR1(imm) >> 16),
(uint16)(MCF5272_RD_USB_DRR2(imm) & 0xFFFF),
(uint16)(MCF5272_RD_USB_DRR2(imm) >> 16));
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-27
9.28.
Interface functions.
init_usb / init_module
Description: The uClinux kernel invokes these functions when the Driver is loaded into
memory. These functions are the entry points of the Driver. Their task is to
register character device within uClinux and to setup interrupt handlers.
init_module() is used when the Driver acts like a module, init_usb()
is used when the Driver is compiled with the kernel [3].
cleanup_module
Description: uClinux kernel invokes this function just before the module is unloaded [3].
usb_device_ioctl
Description: This function handles ioctl() calls [3].
usb_device_fasync
Description: This function handles changing status of asynchronous notification [3].
usb_device_open
Description: This function handles open() calls [3]. It allows to open each endpoint
only ones.
usb_device_release
Description: This function handles close() calls [3].
usb_device_read
Description: This function handles read() calls [3]. It calls the usb_rx_data()
routine.
usb_device_write
Description: This function handles write() calls [3]. It calls the usb_tx_data()
routine.
M
USB Device Driver Function Specification.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
9-28
10. Appendix 1: File Transfer Application.
10.1.
Introduction.
This appendix describes a Device-side USB File Transfer Application to be used only for
demonstration purposes. The program illustrates some useful techniques (see section
10.2) and gives an example of working with the USB Device Driver.
10.1.1. Important Notes.
The Client application (descriptors and program) is designed to mostly support the CBI
Transport specification. From this the following is implied:
a) Endpoints are used according the CBI Transport specification (see section 10.2.1).
b) Descriptors are defined according the CBI Transport specification.
c) The Interrupt data block is defined according the CBI Transport specification.
d) The Host uses 'Accept Device-Specific Command' (ADSC) request for a Control
endpoint (endpoint number 0), to send a command block to the Device, as defined
by the CBI Transport specification.
However the Client application does not support any standard command set (such as UFI,
RBC, etc.) and so a simple UFTP command set was designed and used to achieve this
goal. The UFTP command set represents a very close fit for the file transport task. It
works on a file level and not on a level of blocks of data. Hence, the Client application
does not need to construct a file from blocks (numbers of which it receives from the
Host) of data, as in the case with UFI, RBC and other standard command sets. The
program gets the name of a file using the UFTP protocol and requests the OS to do the
routine work (access required sector, block, etc.) to access the required data. In this way
the Client application is simplified, making for transparent communication with the
Driver.
10.1.2. Capabilities of File Transfer Application.
Some useful techniques are highlighted below, which the program uses during file
transfers:
•
Simultaneous data transfer and data processing. The Client application processes
data (reads/writes data from/to the file) during transfer (reception) of the previous
(next) portion of data. It uses two intermediate buffers – the first to transfer
(receive) the data, and the second – to read/write the next portion of data. When
the first buffer becomes empty (full), the buffers switch over.
10.1.3. Related Files.
The following files are relevant to the Client Program:
• cbi.h – Client application types and global constant definitions;
• cbi.c – main program, executes commands from the Host, hold files;
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-1
•
cbi_desc.c – contains Device, configuration, interface, endpoint, and string
descriptors.
•
uftp_def.h – operation code and status values definitions for UFTP protocol.
The Client application requires the following files:
• usb.h – Driver’s interface definition (the same as USB Driver uses).
• descriptors.h – types definition for device, configuration, interface, endpoint,
and string descriptors (the same as USB Driver uses).
10.2.
UFTP Protocol Description.
This section describes USB usage by the UFTP protocol and specifies the structure of
commands that the Host sends to the Device.
10.2.1. USB Usage.
The UFTP Device and Host, support USB requests and use the USB for the transport of
command blocks, data, and status information, as defined by the CBI Transport
specification, but including the following restrictions:
• A UFTP Device implements an Interrupt endpoint and uses that interrupt endpoint
to indicate a possibility of command execution.
•
The Host uses a Control endpoint (endpoint number 0) to send a command block
to the Device.
•
A UFTP Device implements a Bulk In endpoint, to transfer data to the Host; and a
Bulk Out endpoint to receive data from the Host.
10.2.2. Status Values.
The following status values are defined by the UFTP protocol:
Table 10.1 Status values defined by UFTP protocol.
Status
Value
Description
UFTP_SUCCESS
0000h The command can be completed
successfully
UFTP_FILE_DOES_NOT_EXIST
0011h Required file does not exist on Device
UFTP_MEMORY_ALLOCATION_FAIL
0021h Not enough memory for intermediate
buffers allocation
UFTP_NOT_ENOUGH_SPACE_FOR_FILE
0041h Not enough memory for a new file
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-2
10.2.3. UFTP Command Descriptions.
Commands that are used in the UFTP protocol do not have a fixed-length structure. Only
the first field is common for all commands – Operation Code. The rest of the fields
depend upon the command.
10.2.3.1.
UFTP_READ command: 01h.
The Host sends the UFTP_READ command to get a required file from the Device.
Table 10.2 UFTP_READ command.
Byte
Description of value
0
Operation code (01h)
1
Length of file name
2
Name of file
3
(not NULL-terminated string)
…
length_of_file_name - 1
The command specifies a file, which the Device must send to the Host. It has two
parameters – length of file name and name of file. The length of file name field is used to
properly fetch the name of the file from the command. The name of the file is not a
NULL-terminated string.
UFTP_READ data: Upon receiving this command, the Device sends status to the Host,
and if that status is UFTP_SUCCESS, it sends the contents of the given file to the Host
(on Bulk In endpoint).
10.2.3.2.
UFTP_WRITE command: 02h.
The Host sends the UFTP_READ command to send a required file to the Device.
Table 10.3 UFTP_WRITE command
Byte
Description of value
0
Operation code (02h)
1
(LSB)
Length of file
2
3
(MSB)
4
5
Length of file name
6
Name of file
…
length_of_file_name - 1
(not NULL-terminated string)
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-3
The command specifies a file, which the Device must receive from the Host. It has three
parameters – length of file, length of file name and name of file. The length of file name
field is used to properly fetch the name of the file from the command. The name of the
file is not a NULL-terminated string.
UFTP_WRITE data: Upon receiving this command, the Device sends status to the Host,
and if that status is UFTP_SUCCESS, it receives the data from the
Host (on Bulk Out endpoint).
10.2.3.3.
UFTP_GET_FILE_INFO command: 03h.
The Host sends the UFTP_GET_FILE_INFO command to get the size of a given file.
Table 10.4 UFTP_GET_FILE_INFO command.
Byte
Description of value
0
Operation code (03h)
1
Length of file name
2
Name of file
3
(not
NULL-terminated
string)
…
length_of_file_name - 1
The command specifies a file, the size of which the Device must send to the Host. It has
two parameters – length of file name and name of file. The length of the file name field is
used to properly fetch the name of the file from the command. The name of the file is not
a NULL-terminated string.
UFTP_GET_FILE_INFO data:
10.2.3.4.
Upon receiving this command, the Device sends
status to the Host and if that status is
UFTP_SUCCESS, the Device sends the length of
the given file to the Host (LSB first).
UFTP_GET_DIR command: 04h.
The Host sends the UFTP_GET_DIR command to receive the names of all files held on a
given Device.
Table 10.5 UFTP_GET_DIR command.
Byte
Description of value
0
Operation code (04h)
The command has no parameters.
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-4
UFTP_GET_DIR data:
Upon receiving this command, the Device sends status to
the Host and if that status is UFTP_SUCCESS, it sends two
buffers to the Host.
The first buffer contains information about the directory – the length of the buffer that
holds the list of files (length of second buffer), and the number of files.
Table 10.6 Buffer containing information about directory.
Byte
Description of value
0
(LSB)
Length of buffer that contains list of files
1
2
(MSB)
3
4
(LSB)
Number of files
5
6
(MSB)
7
The second buffer contains a list of files.
Table 10.7 Buffer containing list of files
Byte
Description of value
0
Length of file1 name
1
Name of file1
(not NULL-terminated string)
…
length_of_file1_name - 1
length_of_file1_name
length_of_file1_name + 1
…
length_of_file2_name - 1
…
Length of file2 name
Name of file2
(not NULL-terminated string)
…
10.2.3.5.
UFTP_SET_TRANSFER_LENGTH command: 05h.
The Host sends the UFTP_SET_TRANSFER_LENGTH command to set the length of
transfer.
Table 10.8 UFTP_SET_TRANSFER_LENGTH command.
Byte
Description of value
0
Operation code (05h)
1
(LSB)
2
Length of transfer
3
(MSB)
4
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-5
Upon receiving this command, the Device sends UFTP_SUCCESS status to the Host.
The length of the transfer is used during execution of UFTP_READ and UFTP_WRITE
commands. Files are sent between the Host and the Device by blocks. The length of each
block is equal to the length of transfer. Hence, a given command sets up the length of the
block over which the transferred file will be divided up.
10.2.3.6.
UFTP_DELETE command: 06h.
The Host sends the UFTP_DELETE command to delete a required file on the Device.
Table 10.9 UFTP_DELETE command
Byte
Description of value
0
Operation code (06h)
1
Length of file name
2
Name of file
3
(not NULL-terminated string)
…
length_of_file_name - 1
Upon
receiving
this
command,
the
Device
sends
UFTP_FILE_DOES_NOT_EXIST or UFTP_SUCCESS status to the Host.
either
The command specifies a file, which must be deleted by the Device. It has two
parameters – length of file name and name of file. The length of file name field is used to
properly fetch the name of the file from the command. The name of the file is not a
NULL-terminated string.
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-6
10.3.
Implementation of File Transfer
Application.
This section explains how the Client application executes commands from the Host.
The Client Application works with files that are located in /var (defined in cbi.h file)
directory of the uClinux file system.
10.3.1. Initializing the Driver.
To start working with the Driver, the Client application must first initialize it. Before
calling the USB_INIT ioctl (which initializes the Driver), the Client application needs to
fill the DESC_INFO structure (defined in usb.h file):
extern USB_DEVICE_DESC Descriptors;
extern USB_STRING_DESC string_desc;
…
int usb_dev_file;
int usb_ep1_file;
…
DESC_INFO device_desc;
…
usb_dev_file = open(USB_EP0_FILE_NAME, O_RDWR);
if (usb_dev_file < 0)
{
printf
("Can't
open
device
file:
USB_EP0_FILE_NAME);
exit(-1);
}
usb_ep1_file = open(USB_EP1_FILE_NAME, O_WRONLY);
if (usb_ep1_file < 0)
{
printf
("Can't
open
device
file:
USB_EP1_FILE_NAME);
close(usb_dev_file);
exit(-1);
}
%s\n",
%s\n",
The variable Descriptors is allocated in the cbi_desc.c file.
Then the Client enables asynchronous notification and sets up a handler for the SIGIO
signal (accept_event() function):
act.sa_handler = &accept_event;
act.sa_mask = 0;
act.sa_flags = 0;
sigaction(SIGIO, &act, NULL);
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-7
fcntl(usb_dev_file, F_SETOWN, getpid());
oflags = fcntl(usb_dev_file, F_GETFL);
fcntl(usb_dev_file, F_SETFL, oflags | FASYNC);
The Client application then initializes the Driver:
device_desc.pDescriptor = (uint8 *) &Descriptors;
device_desc.DescSize = usb_get_desc_size();
ioctl(usb_dev_file, USB_INIT, (&device_desc);
The variable Descriptors is defined in the cbi_desc.c file.
10.3.2. Program Execution.
The Client program consists of two important parts: usb_accept_command() function
and the main() function.
accept_event() is called every time the Driver receives a command from the Host or
when some event occurs (reset, configuration change etc.). If it is a request for a string
descriptor, the Client executes that request immediately (refer to Chapter 3.3.7). If the
Client program does not support the received command, it returns the call
USB_NOT_SUPPORTED_COMMAND ioctl. Otherwise, the Client application puts a command
into the Queue and returns the call USB_COMMAND_ACCEPTED ioctl. After initializing the
Driver, main() initializes its command buffer (the buffer where the command will be
placed):
uint8 cb[COMMAND_BUFFER_LENGTH];
DEVICE_COMMAND command;
…
/* Initialize command buffer */
command.cbuffer = cb;
It then enters an infinite loop which fetches the next command from the queue by calling
the fetch_command() function:
if (usb_fetch_command(&command) == USB_COMMAND_SUCCESS)
{
switch (command_block[0])
{
…
Finally, the main() function finds the appropriate handler for the received command and
calls it, passing the address of the buffer that contains the command:
switch ( command.cbuffer[0])
{
case UFTP_READ:
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-8
#ifdef DEBUG
printf("Command UFTP_READ has been recognized by
Client\n");
#endif
do_command_read(command.block);
break;
case UFTP_WRITE:
…
The execution of each command is described in a separate subsection.
10.3.2.1.
UFTP_READ command execution.
In the first instance, the UFTP_READ command handler tries to find a given file. If the
file does not exist, it reports an error to the Host on an interrupt endpoint. Otherwise, it
allocates intermediate buffers.
To transfer a file from Device to Host, two intermediate buffers are used (detailed
description of their purpose is described below). In order to increase the execution speed
of the program during file transfers, these buffers must both be 4-byte aligned. The first
buffer is always 4-byte aligned, regardless of whether it was dynamically allocated (in
this case malloc() will return a 4-byte aligned address) or allocated in SRAM (a start
address of SRAM module is always 4-byte aligned). To find the nearest 4-byte aligned
address for the second buffer, some calculations are necessary.
The handler calculates the remainder from the division of the transfer length (the length
of each intermediate buffer) by 4. Then the function finds the number of bytes which
need to be padded:
padded_bytes = (transfer_length & 0x3);
if (padded_bytes != 0)
padded_bytes = 4 - padded_bytes;
Thus, the address of the second intermediate buffer will be equal to the sum of the
transfer length and the number of padded bytes added to the start address of the first
buffer:
buffer2 = buffer1 + transfer_length + padded_bytes;
However, at first the first intermediate buffer (pointed by buffer1) must be allocated:
buffer1 = (uint8 *) malloc(2*transfer_length + padded_bytes);
…
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-9
As the next step, the handler sends the status to the Host on an interrupt endpoint. If there
is enough memory for the buffers, the function starts to send data to the Host.
The bufptr variable is used to point to the current intermediate buffer. The size
variable contains the number of bytes that were copied from the file to the current
intermediate buffer (fread() function returns this amount). It is set then up to
transfer_length in order to enter the loop.
A file is sent to the Host according the following algorithm:
The handler reads the required portion of the data from the file, into the current
temporary buffer, then waits while the required endpoint is busy. Then it starts
transferring data to the Host by calling the write() function. This function places in the
FIFO buffer only the initial 256 bytes and then returns control. The rest of the data (from
this transfer, not the file) will be sent using an EOP interrupt handler [4]. When write()
returns control, a new portion of data can be copied from the file, but now into the second
intermediate buffer, thus data processing (copying of the next portion of data) and
transferring data from the first buffer is occurring in parallel. A more detailed description
of this is provided below.
The handler attempts to read transfer_length bytes from the file into the intermediate
buffer pointed to by the bufptr variable:
size = fread(bufptr, 1, transfer_length, file_desc);
The function returns the number of bytes read from the file. If the number of bytes read is
less than transfer_length it indicates that the end of file is reached, and the function
must complete the operation.
Before transferring the data to the Host, the program must wait until the required
endpoint (BULK IN) becomes free:
if (usb_ep_wait(usb_ep1_file) < 0)
{
free(buffer1);
fclose(file_desc);
return;
}
usb_ep_wait() is a macro as defined below:
#define usb_ep_wait(ep_file)
(ioctl(ep_file, USB_EP_WAIT, 0))
which returns the number of bytes transferred during the last transfer or –1 in case of
error.
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-10
The program then initiates a transfer:
if (write(usb_ep1_file, bufptr, size) < 0)
{
free(buffer1);
fclose(file_desc);
return;
}
Finally, the buffers switch over,
if (bufptr == buffer1)
bufptr = buffer2;
else
bufptr = buffer1;
The same operations but with new buffers will be performed on a new iteration of the
loop (if the end of file was not reached).
The values returned from some functions are tested for negative values. If a port reset
occurs on a device or if there was an error, the Driver informs the Client application in
this manner. It also sends a SIGIO signal, invoking execution of accept_command().
The program finishes all operations and returns control to the main() function in this
case.
10.3.2.2.
UFTP_WRITE command execution.
In the first instance, the UFTP_WRITE command handler tries to find a given file. If the
file does not exist it finds the first free position in the array of pointers to the file. If there
is no free position in the array, it reports an error to the Host, on an interrupt endpoint. It
then begins to allocate intermediate buffers.
To transfer a file from Host to Device, two intermediate buffers are used (a detailed
description of their purpose is given below). In order to improve the execution speed of
the program during file transfers, these buffers must both be 4-byte aligned. The first
buffer is always 4-byte aligned regardless of whether it was dynamically allocated (in this
case malloc() will return a 4-byte aligned address) or allocated in SRAM (a start address
of an SRAM module is always 4-byte aligned). To find the nearest 4-byte aligned address
for the second buffer, some calculations are necessary.
The handler calculates the remainder from the division of the transfer length (the length
of each intermediate buffer) by 4. Then the function finds the number of bytes to be
padded:
padded_bytes = (transfer_length & 0x3);
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-11
if (padded_bytes != 0)
padded_bytes = 4 - padded_bytes;
Thus, the address of the second intermediate buffer will be equal to the sum of the
transfer length and the number of padded bytes added to the start address of the first
buffer:
buffer2 = buffer1 + transfer_length + padded_bytes;
However the first intermediate buffer (pointed by buffer1) must first be allocated. If the
length of file is less than or equal to the length of the transfer, only one write operation
from temporary buffer to file will be performed and the second buffer will not be used. In
this case, memory must be allocated for the first intermediate buffer only:
if ((int32) (flength <= transfer_length))
buffer1 = (uint8 *) malloc(flength);
else
buffer1
=
(uint8
padded_bytes);
…
*)
malloc(2
*
transfer_length
+
The function sends status to the Host. If the status is UFTP_SUCCESS, the Host can start
to transfer the file.
If a file with the given name already exists on the Device, it will be overwritten.
A file is received from the Host according the following algorithm:
The program waits while the required endpoint is busy, it then starts receiving data from
the Host by calling the read() function. This function reads data from the FIFO buffer
into the current buffer. If all the expected data for this transfer (not file) was not received,
the rest of the data will be received using EOP interrupt [4]. When read() returns
control, the writing of data from the second buffer (previously received data) to the file
can be started. Thus, receiving the data into the current buffer and writing data from the
previous buffer into the file is occurring simultaneously. A more detailed description of
this is provided below.
The function enters the loop, waiting until the required (BULK OUT) endpoint becomes
free. It then calls the read() function to start receiving the file:
if ((int32)(flength - pos - size) >= transfer_length)
size = transfer_length;
else
size = flength - pos - size;
if (read(usb_ep2_file, bufptr, size) < 0)
{
free(buffer1);
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-12
fclose(file_desc);
unlink(tmpname);
return;
}
bufptr points to the current intermediate buffer, size contains the amount of bytes to be
received.
Then, the buffers swap over:
/* Change pointer to previous buffer. */
if (bufptr == buffer1)
bufptr = buffer2;
else
bufptr = buffer1;
Now, the EOP interrupt handler copies data to the first buffer, and data from the second
buffer (pointed now by bufptr) can be written to file:
/* Write data from previous buffer into the file. */
fwrite(bufptr, 1, buf_size, file_desc);
The buf_size variable contains the number of bytes written to the previous buffer, while
size – is the number of bytes to be written into the current buffer.
The same operations but with new buffers will be performed on a new iteration of the
loop (if all the expected data was not received).
Values from some functions are tested for negative values. If a port reset occurs on a
Device or if there was an error, the Driver informs the Client application about it in this
manner. It also sends a SIGIO signal, invoking the execution of accept_command(). The
program finishes all operations and returns control to the main() function in this case.
10.3.2.3.
UFTP_GET_FILE_INFO command execution.
To begin with, the UFTP_GET_FILE_INFO command handler tries to find a given file,
following which it sends status to the Host. If the status sent was UFTP_SUCCESS, the
program sorts bytes of file length in reversed order (PC Host will read it as DWORD). It
then sends the value obtained to the Host on a BULK IN endpoint.
10.3.2.4.
UFTP_GET_DIR command execution.
Execution of the UFTP_GET_DIR command handler starts from counting the length of
the buffer (total_fname_len variable is used), needed to hold the name of files and size
of name of files. In addition, it counts the number of files (files_count variable).
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-13
After this is completed, the function reorders the values with reversed byte order (each
value independently) for the PC Host (it reads them as DWORD), and stores them into
the info_buffer. Having the size, memory can be allocated dynamically for the buffer:
/* Allocate buffer to store length of file name and file name for each
file in it */
dir_buffer = (uint8 *) malloc(total_fname_len);
The program then sends status to the Host on an interrupt endpoint. If the status was
UFTP_SUCCESS, the handler starts to fill the directory buffer with the length of file
name and name of file for each file. It then sends info_buffer to the Host on a BULK
IN endpoint.
If the buffer that contains the list of files is not empty, the program sends it to the Host on
a BULK IN endpoint.
As a further remark concerning the execution of the UFTP_GET_DIR command: the
Host has no way to obtain the list of files from the Device, if the Device is not able to
allocate the buffer. Changing the length of transfer has no affect upon this. The situation
may be considered as a limitation, but it is done consciously in order not to over
complicate the Client application. The main purpose is after all, demonstration only.
10.3.2.5.
UFTP_SET_TRANSFER_LENGTH command
execution.
The UFTP_SET_TRANSFER_LENGTH command handler sends UFTP_SUCCESS
status to the Host indicating that it started to process the command. Then it fetches a new
length of transfer from the command buffer and assigns it to the transfer_length
variable. This variable is used while transferring a file between Host and Device.
10.3.2.6.
UFTP_DELETE command execution.
Once execution of this command starts, the UFTP_DELETE command handler tries to
find a given file. If the file exists, the program deletes it. Then, the function sends status
to the Host.
10.3.2.7.
Request for string descriptor handling.
This section provides a memory layout for string descriptors and describes how the Client
application sends a given descriptor to the Host.
10.3.2.7.1.
Memory layout for string descriptors.
According to the documentation of the USB module, the request processor does not
handle requests for string descriptors automatically. GET_DESCRIPTOR requests for string
descriptors are passed as a vendor specific request. The string descriptors must be stored
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-14
in external memory and not in the configuration RAM. The memory layout for string
descriptors is shown in Fig 10-1 below.
String descriptors are stored in an array of descriptors. An element of the array is a
structure (defined in cbi.h and descriptors.h files):
/* Definitions for USB String Descriptors */
#define NUM_STRING_DESC
4
#define NUM_LANGUAGES
2
typedef struct {
uint8 bLength;
uint8 bDescriptorType;
uint8 bString[256];
} STR_DESC;
typedef STR_DESC USB_STRING_DESC [NUM_STRING_DESC * NUM_LANGUAGES + 1];
string_desc
6
3
0x09
409 language ID
0x04
407 language ID 0x07
0x04
NUM_STRING_DESC
NUM_STRING_DESC
18
3
s
t
r
i
n
g
54
3
s
t
r
i
n
g
26
3
s
t
r
i
n
g
88
3
s
t
r
i
n
g
18
3
s
t
r
i
n
g
54
3
s
t
r
i
n
g
26
3
s
t
r
i
n
g
88
3
s
t
r
i
n
g
1
2
3
4
1
2
3
4
bLength
bDescriptorType
bString[256]
Strings written on language Strings written on language
having 0x409 ID
having 0x407 ID
NUM_LANGUAGES
Fig 10-1. Memory layout for string descriptors
The Client application allocates the USB_STRING_DESC [NUM_STRING_DESC*
NUM_LANGUAGES + 1] array. The first element in the array (an element with index zero)
is a string descriptor that contains an array of two-byte LANGID codes supported by the
device (0x409 and 0x407 IDs). The next NUM_STRING_DESC descriptors are string
descriptors written using a language with 0x409 ID; the following are NUM_STRING_DESC
descriptors - with 0x407 language ID. The position of string descriptors must correspond
to the order of language IDs that are contained in a string descriptor, having index zero.
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-15
Thus, if the first language ID is 0x409 then the first four (NUM_STRING_DESC) descriptors
(having indices 1, 2, 3, and 4 in the array) must be written using a language having ID
0x409. The next four descriptors must be written using a language having ID 0x407.
Language IDs do not need to be sorted. Bytes in each Language ID are reverse ordered.
The variable string_desc points to the array containing string descriptors.
10.3.2.7.2.
Sending the string descriptor to the Host.
When the usb_accept_command() function is called, it tests the request. If it is a request
for a string descriptor, the function calls the get_string_descriptor() routine:
status = get_string_descriptor(dc -> request.wValue & 0xFF,
dc -> request.wIndex,
dc ->request.wLength);
The get_string_descriptor() function accepts three parameters:
desc_index - index of string descriptor;
languageID – language ID;
length – number of bytes to send.
According to the USB 1.1 specification, the Driver must send a short or zero length
packet to indicate the end of transfer if the descriptor is shorter than the length
parameter, or only the initial bytes of the descriptor, if it is longer.
This function finds the array index (variable i is used) of the desired language ID for a
non-zero indexed string (language ID 0x409 has index zero in a string with index zero,
language ID 0x407 has index 1 in that string). It reorders bytes in the languageID
parameter, to prepare it for comparison, since IDs in the array are stored with reversed
byte order.
If the string descriptor with the required index or given language ID is not supported, the
function calls the NOT_SUPPORTED_COMMAND ioctl.
Otherwise it starts to prepare data for the Host. If the desc_index parameter is zero, the
Driver returns a string descriptor that contains an array of two-byte LANGID codes,
supported by the Device regardless of the languageID parameter. This string descriptor
has index zero in the array. Otherwise, the string with the appropriate index and language
ID will be found.
The get_string_descriptor() function points the stdesc variable to the required
descriptor:
if (desc_index)
{
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-16
i *= NUM_STRING_DESC;
i += desc_index;
stdesc
=
&((*usb_string_descriptor)[i]);
}
else
stdesc
=
&((*usb_string_descriptor)[0]);
(uint8
*)
(uint8
*)
and gets the size of that descriptor:
size = *stdesc;
If the descriptor is longer than the number of requested bytes, it modifies the size:
if (size >= length)
size = length;
else
ioctl(usb_dev_file, USB_SET_SEND_ZLP);
If the Host requested more bytes than the length of the descriptor, the situation may arise
where the Driver must indicate an end of transfer by sending a zero length packet (this
happens when the length of the descriptor is a multiple of the maximum size of packet,
for endpoint number zero). Thus, the USB_SET_SEND_ZLP ioctl must be called in such a
case on a EP0 device file (a string will be sent on endpoint number zero). It does not
mean that a zero length packet will necessarily be sent. If the last packet is short (but not
zero length), a zero length packet will not be sent.
Then, the get_string_descriptor() function initiates the transfer of the descriptor to
the Host:
write(usb_dev_file, stdesc, size);
10.4.
USB File Transfer Application Function
Specification.
This section describes the functions implemented in the USB Client application.
Function arguments for each routine are described as in, inout. An in argument
implies that the parameter value is an input only to the function. An inout argument
implies that a parameter is an input to the function, but the same parameter is also an
output from the function. Inout parameters are typically input pointer variables in which
the caller passes the address of a pre-allocated data structure to a function. The function
stores the result within that data structure. The actual value of the inout pointer
parameter is not changed.
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-17
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-18
10.4.1. accept_event
Call(s):
void usb_accept_command(int sig);
Arguments:
sig – the number of the raised signal (always SIGIO).
Returns:
No value returns.
Description:
The function tests each command to see if the program supports it, and processes
USB events (reset signal, change_configuration). If the received command is supported,
the function puts this command into the Queue. If it is a request for a string descriptor,
the function calls get_string_descriptor().
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-19
10.4.2. do_command_delete.
Call(s):
void do_command_delete(uint8 * combuf);
Arguments:
Table 10-10. do_command_delete arguments.
Combuf
in
Pointer to the buffer that contains a command
Description: This function is a UFTP_DELETE command handler. It deletes a given
file.
Returns:
No value returns.
Code example:
case UFTP_DELETE:
#ifdef DEBUG
printf("Command
Client\n");
#endif
UFTP_DELETE
has
been
recognized
by
do_command_delete(command.block);
break;
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-20
10.4.3. do_command_get_dir.
Call(s):
void do_command_get_dir(void);
Arguments:
No arguments.
Description: This function is a UFTP_GET_DIR command handler. It sends a list of
files to the Host.
Returns:
No value returns.
Code example:
case UFTP_GET_DIR:
#ifdef DEBUG
printf("Command
Client\n");
#endif
UFTP_GET_DIR
has
been
recognized
by
do_command_get_dir();
break;
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-21
10.4.4. do_command_get_file_info.
Call(s):
void do_command_get_file_info(uint8 * combuf);
Arguments:
Table 10-11. do_command_get_file_info arguments.
Combuf
in
Pointer to the buffer that contains a command
Description: This function is a UFTP_GET_FILE_INFO command handler. It sends
size of given file to the Host.
Returns:
No value returns.
Code example:
case UFTP_GET_FILE_INFO:
#ifdef DEBUG
printf("Command
UFTP_GET_FILE_INFO
Client\n");
#endif
has
been
recognized
by
do_command_get_file_info(command.block);
break;
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-22
10.4.5. do_command_read.
Call(s):
void do_command_read(uint8 * combuf);
Arguments:
combuf
Table 10-12. do_command_read arguments.
in
Pointer to the buffer that contains a command
Description: This function is a UFTP_READ command handler. It sends a given file to
the Host.
Returns:
No value returns.
Code example:
case UFTP_READ:
#ifdef DEBUG
printf("Command
Client\n");
#endif
UFTP_READ
has
been
recognized
by
do_command_read(command.block);
break;
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-23
10.4.6. do_command_set_transfer_length.
Call(s):
void do_command_set_transfer_length(uint8 * combuf);
Arguments:
Table 10-13. do_command_set_transfer_length arguments.
combuf
in
Pointer to the buffer that contains a command
Description: This function is a UFTP_SET_TRANSFER_LENGTH command handler.
It sets a length of transfer given by the Host.
Returns:
No value returns.
Code example:
case UFTP_READ:
#ifdef DEBUG
printf("Command
Client\n");
#endif
UFTP_READ
has
been
recognized
by
do_command_read(command.block);
break;
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-24
10.4.7. do_command_write.
Call(s):
void do_command_write(uint8 * combuf);
Arguments:
combuf
Table 10-14. do_command_write arguments.
in
Pointer to the buffer that contains a command
Description: This function is a UFTP_WRITE command handler. It receives a file from
the Host.
Returns:
No value returns.
Code example:
case UFTP_WRITE:
#ifdef DEBUG
printf("Command
Client\n");
#endif
UFTP_WRITE
has
been
recognized
by
do_command_write(command.block);
break;
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-25
10.4.8. fetch_command.
Call(s):
uint32 fetch_command(uint8 * dcb);
Arguments:
Table 10-15. fetch_command arguments.
inout Pointer to the buffer where to place command
dcb
Description: This function copies a command into the given buffer and deletes the
request with a command from the Queue.
Returns:
Function returns status.
Status NO_NEW_COMMAND means that command queue is empty, so the
buffer pointed by dcb is not initialized with a command.
Status COMMAND_SUCCESS indicates, that buffer pointed by dcb contains a
new command.
Code example:
if (fetch_command(command_block) == COMMAND_SUCCESS)
{
switch (command_block[0])
…
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-26
10.4.9. get_string_descriptor.
Call(s):
uint32 get_string_descriptor(uint8 desc_index, uint16 languageID, uint16 length);
Arguments:
Table 10-16. get_string_descriptor arguments.
desc_index
in
Index of required descriptor
languageID
in
Language ID
Length
in
Number of bytes to send
Description: This function sends a string descriptor to the Host having a given index
and written with a language having a given ID.
Returns:
Function returns status.
Status NOT_SUPPORTED_COMMAND means that program does not support the
required descriptor.
Status SUCCESS indicates, that required descriptor was sent to the Host.
Code example:
if ((dc -> request.bmRequestType == 0x80) &&
(dc -> request.bRequest == GET_DESCRIPTOR) &&
((dc -> request.wValue >> 8) == STRING))
{
status = get_string_descriptor(dc -> request.wValue & 0xFF,
dc -> request.wIndex,
dc ->request.wLength);
return status;
}
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-27
10.4.10. read_file.
Call(s):
uint32 read_file(uint32 fnum, uint8* dest, int32 length, int32 position);
Arguments:
Fnum
Dest
Length
position
Table 10-17. read_file arguments.
in
Index of file from which data must be read
inout Pointer to buffer where to place read data
in
Number of bytes to be read
in
Offset in a given file. It is a position in a file from which
function must start copying the data.
Description: This function copies length bytes from a file having index fnum to the
buffer pointed by the dest parameter. A reading from file starts from the
position offset.
Assembler version is also provided.
Returns:
Number of read bytes.
Code example:
/* Copy next portion of data from file into the buffer */
size = read_file(fpos, bufptr, transfer_length, pos);
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-28
10.4.11. write_file.
Call(s):
uint32 write_file(uint32 fnum, uint8* dest, int32 length, int32 position);
Arguments:
fnum
dest
length
position
Table 10-18. write_file arguments.
in
Index of file in which data must be written
inout Pointer to buffer from where data must be read
in
Number of bytes to be written
in
Offset in a given file. This is a position in a file from
where a function must start placing the data.
Description: This function copies length bytes from the buffer pointed by dest
parameter to a file having index fnum. A writing to file starts from
position offset.
Assembler version is also provided.
Returns:
Number of written bytes.
Code example:
/* Write data from previous buffer into the file. */
write_file(fpos, bufptr, buf_size, pos);
M
Appendix 1: File Transfer Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
10-29
11. Appendix 2: Audio Application.
11.1.
Introduction.
This appendix describes a Device-side USB Audio Application. This program is used
only for demonstration purposes. It illustrates some useful techniques (see section 11.2)
and gives an example of working with the USB Device Driver.
11.1.1. Important Notes.
The Client application does not support any standard class (i.e. USB Audio class, etc.). A
simple vendor specific command set was designed and used to demonstrate Isochronous
IN/OUT data transfer together with acceptance/execution of commands from the Host
simultaneously with transfers. Also, the program demonstrates the behavior of the
Device-side USB Driver, when the Host s/w does not work in real time (while performing
the test transfers, misses frames).
11.1.2. Capabilities of the Audio Application.
The Audio application receives 16 bit mono PCM samples from the Host with 8 kHz and
44.1 kHz rates, reduces their amplitude (the multiplication factor is set by the Host using
a command), and sends processed data back to the Host.
In addition, the Client program performs test transfers (IN, OUT, and simultaneously IN
and OUT) both when the Host software works in normal mode, and when the Host
emulates real-time failure.
Some useful techniques are highlighted below, which the program utilizes during file
transfers:
•
•
Simultaneous data transfer on IN and OUT endpoints with data processing. The
Client application processes data (reduces the amplitude of each sample) while
performing IN and OUT data transfers.
Using the SRAM module for allocating intermediate buffers, which makes for a
faster execution speed of the program during IN/OUT data transfers.
11.1.3. Related Files.
The following files are relevant to the Client Program:
•
•
•
iso.h – Client application types and constant definitions;
iso.c – main program, executes commands from Host, performs data transfers;
iso_desc.c – contains device, configuration, interface, endpoint, and string
descriptors.
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-1
The Client application needs following files:
•
•
usb.h – Driver interface definition (the same as USB Driver uses).
descriptors.h – types definition for device, configuration, interface, endpoint,
and string descriptors (the same as USB Driver uses).
11.2.
Implementation of USB Audio Application
The following section explains how the Client application performs isochronous transfers
and executes commands from the Host.
11.2.1. USB Usage.
The Host uses a Control endpoint (endpoint number 0) to send commands to the Device.
The USB Audio Device implements an Isochronous In endpoint to transfer data to the
Host, and an Isochronous Out endpoint to receive data from the Host.
The USB Audio Device implements four alternate settings:
Alternate setting 0: no bandwidth.
Alternate setting 1: packet size of Isochronous IN/OUT endpoints is 16 bytes.
Alternate setting 2: packet size of Isochronous IN/OUT endpoints is 90 bytes.
Alternate setting 3: packet size of Isochronous IN/OUT endpoints is 160 bytes.
11.2.2. Initializing the Driver.
To start working with the Driver, the Client application must first initialize it. Before
calling the USB_INIT ioctl command (which initializes the Driver), the Client application
needs to open the necessary device files and fill up the DESC_INFO structure (defined in
the usb.h file):
extern USB_DEVICE_DESC Descriptors;
extern USB_STRING_DESC string_desc;
…
int usb_dev_file;
int usb_ep1_file;
int usb_ep2_file;
…
DESC_INFO device_desc;
…
usb_dev_file = open(USB_EP0_FILE_NAME, O_RDWR);
if (usb_dev_file < 0)
{
printf ("Can't open device file: %s\n", USB_EP0_FILE_NAME);
exit(-1);
}
usb_ep1_file = open(USB_EP1_FILE_NAME, O_WRONLY);
if (usb_ep1_file < 0)
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-2
{
printf ("Can't open device file: %s\n", USB_EP1_FILE_NAME);
close(usb_dev_file);
exit(-1);
}
The Client then enables asynchronous notification and sets up a handler for the SIGIO
signal (accept_event() function):
act.sa_handler = &accept_event;
act.sa_mask = 0;
act.sa_flags = 0;
sigaction(SIGIO, &act, NULL);
fcntl(usb_dev_file, F_SETOWN, getpid());
oflags = fcntl(usb_dev_file, F_GETFL);
fcntl(usb_dev_file, F_SETFL, oflags | FASYNC);
Following this, the Client application initializes the Driver:
device_desc.pDescriptor = (uint8 *) &Descriptors;
device_desc.DescSize = usb_get_desc_size();
The variable Descriptors is defined in the iso_desc.c file.
ioctl(usb_dev_file, USB_INIT, (&device_desc);
11.2.3. Program Execution Flow.
The Client program consists of two important parts: accept_event() function and
main() function.
accept_event() is called every time the Driver receives a command from the Host or
some
event
occurs
(reset,
configuration
change
etc.).
This
function
sets
start_main_task, start_isotest_out_stream, start_isotest_in_stream, and
start_isotest_inout_stream variables, determines the number of the frame in which
data transfers must be started, sends that number to the Host and asks the Driver to start
monitoring transfers from that frame. However it does not execute a command directly.
The Client application only handles a request for a string descriptor immediately.
The main() function polls these variables in an infinite loop. If one of the variables is set,
the program finds and executes the appropriate function to perform the corresponding
task. This is one of the best ways to recognize a new command from the Host and execute
it. The main advantage of this method (in comparison with executing the command
directly in the accept_event() function) is described below.
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-3
The program may permanently execute some task, such as process and transfer data.
During the execution of this task by the Device, the Host manipulates the Device: for
example gets/sets attributes of bass control, mixer control, volume control etc. This
method permits the handling of these request in real-time (by interrupting the main
process). Moreover this is achieved without the need for any additional code in the main
task handler, to see whether the Device received a new command from the Host or not.
(Handling the request for a string descriptor may serve as an example. The Client
program sends a string to the Host simultaneously with the execution of the main task data processing and transferring in both directions. However the main_task() function
knows nothing about this – no extra code is written in the main_task() function to
recognize a request for a string descriptor.)
Another case is when the execution of some tasks takes long time. The time needed to
reply with status in a status stage of command transfer, is limited by the USB 1.1
specification (this is the case while the execution of a previous command is in progress
when a new command is received). The Driver invokes the execution of
accept_event() by sending a SIGIO signal to the application from the interrupt handler
for endpoint number zero. This function must send status in a status stage of command
transfer to the Host, as soon as possible (using USB_COMMAND_ACCEPTED or
USB_NOT_SUPPORTED_COMMAND ioctl call).
11.2.4. USB_AUDIO_START command execution.
When the Device accepts a USB_AUDIO_START command, it must start a loop-back task.
The program determines the number of the start frame and sends that number to the Host.
Also, the USB_SET_START_FRAME ioctl is called for isochronous IN and OUT endpoints,
to inform the Driver from which frame it must start the data stream. accept_event()
then sets the start_main_task variable and calls the main_task() function, if this
variable is set.
To implement a loop-back task with data processing, the program uses four buffers,
allowing requests to be queued. The buffer contains a header (sizes of each packet) and
20 packets of data. The program performs the loop-back task with 8 kHz and 44.1 kHz
sample rates. For the 8 kHz rate the size of each buffer must be 400 bytes (80 bytes
header + 320 bytes data (20 packets in a buffer * 16 bytes packet size (16 bit, mono))),
for the 44.1 kHz rate the buffer size must be 1844 bytes (80 bytes header + 1764 bytes
data (18 packets * 90 bytes packet size + 2 packets * 72 bytes packet size)). For the 44.1
kHz sample rate the size of every 10th packet is 72.
The buffer definition looks like the following:
typedef struct {
uint32 packet_length[20];
uint8 databuf[1800];
} audio_buffer;
……
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-4
audio_buffer * rx_data;
audio_buffer * tx_data;
rx_data = (audio_buffer *) databuf;
tx_data
=
(audio_buffer*)((uint8*)rx_data
sizeof(audio_buffer)*2);
+
rx_data and tx_data point to the array of two audio_buffers.
To send/receive data with a required rate, the Device uses different packet sizes (the same
configuration, the same interface, but different alternate settings). The Host chooses the
desired rate by setting an appropriate alternate setting. The Driver catches it on DEV_CFG
interrupt and notifies the Client by sending SIGIO and invoking accept_event(). This
function reads the current configuration from the Driver by calling the
USB_GET_CURRENT_CONFIG ioctl. Then the Client program sets the packet_size variable
in the function, depending on the required rate (alternate setting):
if (current_config.altsetting == 1)
packet_size = 16;
if (current_config.altsetting == 2)
packet_size = 90;
The Client program initializes the buffer headers and puts four requests into a queue (two
for IN transfer and two for OUT transfer):
for (i=0; i<2; i++)
{
/* Init buffer headers */
init_audio_headers(&rx_data[i]);
init_audio_headers(&tx_data[i]);
/* Start IN and OUT data stream. Enqueue I/O requests */
write(usb_ep1_file, (uint8*)(&tx_data[i]), 20);
read(usb_ep2_file, (uint8*)(&rx_data[i]), 20);
}
The header of the buffer contains the size of each packet that must be sent/received. Also,
after transfer completion, it contains the actual size of the data that were transferred. Each
read() request, asks the Driver to read 20 packets of data into the corresponding buffer.
Each write() request asks the Driver to write 20 packets of data from the corresponding
buffer.
When read() or write() returns control, no transmission/reception is started. Driver
has initialized internal structures, placed a first packet into the FIFO buffer (for IN
transfer), added requests to its internal queue and returned control. Moreover, it did not
even start data monitoring. When the Driver receives a frame, having a number that was
passed to itr via the USB_SET_START_FRAME ioctl call, it starts monitoring of transfers and
actual data transmission/reception must be started by the Host (if it starts sending earlier,
it is a fault of the Host).
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-5
Then the application enters a loop and waits until data transfers are be completed, at least
for one request (i.e. one of the buffers was sent/received and the second is in progress),
by calling the USB_EP_WAIT ioctl.
/* Wait until there is only 1 request left in the Driver’s
queue */
ioctl(usb_ep2_file, USB_EP_WAIT, 1);
ioctl(usb_ep1_file, USB_EP_WAIT, 1);
When one buffer is transferred, ioctl returns control. Now the program may change
buffers and process the received data. Then it adds new read/write requests to Driver’s
queue. The program performs this task iteratively, while the stop_main_task variable is
cleared (see the following section).
11.2.5. USB_AUDIO_STOP command execution.
On receiving the USB_AUDIO_STOP command from the Host, the Device must stop the
loop-back task. accept_event() sets the stop_main_task variable to stop the loop in
the main_task() function and calls the USB_SET_FINAL_FRAME ioctl, passing the current
frame number + 40 as a parameter into it:
final_frame_number = (uint32)ioctl(usb_dev_file, USB_GET_FRAME_NUMBER) +
40;
if (final_frame_number > 2047)
final_frame_number -= 2048;
/* Tell to Driver from what number of frame data IN/OUT streams
stop. */
ioctl(usb_ep1_file, USB_SET_FINAL_FRAME, final_frame_number);
ioctl(usb_ep2_file, USB_SET_FINAL_FRAME, final_frame_number);
When the Host sends a command, it will still be sending data for the next 60 frames. But
the main_task() function completes current IN and OUT transfers (which will take not
more than 40 frames).
The Host continues sending data in following 60 frames but Driver clears the FIFO on
receiving a packet and no Client notification is provided – transfer monitoring is already
stopped and no buffer is allocated. The Device, in turn, does not send data to Host. All
internal structures of the Driver are then in the default state. Control returns to the main()
function.
11.2.6. USB_AUDIO_SET_VOLUME command
execution.
When the Client program receives the USB_AUDIO_SET_VOLUME command, it sets the
volume variable. The value was sent by the Host in the data stage of the command
transfer:
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-6
volume = *(uint16 *)dc.cbuffer;
/* Swap the bytes in multiplication factor */
volume = (volume << 8) | (volume >> 8);
/* Notify Host that the command is accepted */
ioctl(usb_dev_file, USB_COMMAND_ACCEPTED);
The Host sends this value with reversed byte order, so bytes must be swapped. The
volume variable is used in the process_data() function while modifying the amplitude
of samples.
11.2.7. START_TEST_OUT_TRANSFER command
execution.
Upon reception of this command, the program determines the number of the frame, in
which a test data OUT transfer will be started, and of the frame in which it will be
completed. The Device sends the number of the start frame to the Host. The Client
application calls USB_SET_START_FRAME and USB_SET_FINAL_FRAME ioctls to check the
test transfer for missed packets. accept_event() then sets start_isotest_out_stream
variable. The main() function calls test_case1_handler().
For this test case five buffers are used. Each of them contains 20 bytes of header and 800
bytes of data (160 bytes packet size x 5 packets). test_case1_handler() initializes
headers for each of the 5 buffers:
/* Init headers of each buffer */
for (i = 0; i < 5; i ++)
init_buffer_headers(&buffers[i]);
The Driver is then requested to read 25 packets into 5 different buffers (5 packets in each
buffer). The Driver puts all requests into a queue and processes them one after the other:
for (i = 0; i < 5; i ++)
read(usb_ep2_file, (uint8*)(&buffers[i]), 5);
Following this action, the program calls the USB_EP_WAIT ioctl to wait for all transfers to
complete and then prints the results.
11.2.8. START_TEST_IN_TRANSFER command
execution.
Upon receiving this command, the program determines the number of the frame, in which
test data IN transfers will be started, and the frame in which it will be completed. The
Device sends to the Host the number of the start frame. The Client application calls
USB_SET_START_FRAME and USB_SET_FINAL_FRAME ioctls to check the test transfer for
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-7
missed packets. accept_event() then sets the start_isotest_in_stream variable.
The main() function calls test_case2_handler().
For this test case, five buffers are used. Each of them contains 20 bytes of header and 800
bytes of data (160 bytes packet size x 5 packets). test_case2_handler() initializes the
data in buffers by calling buffer_init() and headers for each of the 5 buffers:
/* Initialize source data buffer */
buffer_init(buffers);
/* Init headers of each buffer */
for (i = 0; i < 5; i ++)
init_buffer_headers(&buffers[i]);
The Driver is then requested to write the 25 packets from 5 different buffers (5 packets in
each buffer) to the Host. The Driver puts all the requests into a queue and processes them
one after the other:
for (i = 0; i < 5; i ++)
write(usb_ep1_file, (uint8*)(&buffers[i]), 5);
The program then calls the USB_EP_WAIT ioctl to wait for all transfers to be completed.
The results can be seen in TestSuite (Host side s/w).
11.2.9. START_TEST_INOUT_TRANSFER command
execution.
Upon receiving this command, the program determines the number of the frame in which
test data IN and data OUT transfers will be started, and of the frame in which these
transfers will be completed. The Device sends to the Host the number of the start frame.
The Client application calls USB_SET_START_FRAME and USB_SET_FINAL_FRAME ioctls
for both endpoints to check the test transfer for missed packets. accept_event()then sets
the start_isotest_inout_stream variable. The main() function calls
test_case3_handler().
test_case3_handler() uses 4 buffers of 5 packets. It initializes headers for them and
calls read() and write() to start transfers:
for (i=0; i<2; i++)
{
/* Init buffer headers */
init_buffer_headers(&rx_data[i]);
init_buffer_headers(&tx_data[i]);
/* Start IN and OUT data stream. Enqueue I/O requests */
write(usb_ep1_file, (uint8*)(&tx_data[i]), 5);
read(usb_ep2_file, (uint8*)(&rx_data[i]), 5);
}
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-8
The Driver starts processing the first request (in each direction) and puts the second in the
queue. The program waits for the completion of the first transfer and then switches
buffers:
/* Wait until there is only 1 request left in each queue */
ioctl(usb_ep1_file, USB_EP_WAIT, 1);
ioctl(usb_ep2_file, USB_EP_WAIT, 1);
if (swtch)
{
/* Change buffers */
tmp = rx_data;
rx_data = tx_data;
tx_data = tmp;
swtch = 0;
}
else
swtch = 1;
The following requests are then added:
/* Add requests (send/receive next buffers) */
write(usb_ep1_file, (uint8*)(&tx_data[swtch]), 5);
read(usb_ep2_file, (uint8*)(&rx_data[swtch]), 5);
The Client program executes 5 OUT transfers of 5 packets (frames) from the Host and
sends 5 buffers of 5 packets to the Host simultaneously. The program then calls the
USB_EP_WAIT ioctl for each endpoint to wait for all transfers to be completed. The results
can be seen in TestSuite (Host side s/w).
11.2.10. Request for string descriptor handling.
When the Client program receives a request for a string descriptor, accept_event()
starts handling it immediately by calling the get_string_descriptor() function. This
section gives the memory layout for string descriptors and describes how the Client
application sends a given descriptor to the Host.
11.2.10.1.
Memory layout for string descriptors
According to the documentation of the USB module, the request processor does not
handle requests for string descriptors automatically. GET_DESCRIPTOR requests for string
descriptors are passed as a vendor specific request. The string descriptors must be stored
in external memory and not in the configuration RAM.
The memory layout for string descriptors is shown on the Fig 11-1.
String descriptors are stored in the array of descriptors. An element of this array is a
structure (defined in iso.h and descriptors.h files):
typedef struct {
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-9
uint8 bLength;
uint8 bDescriptorType;
uint8 bString[256];
} STR_DESC;
/* Definitions for USB String Descriptors */
#define NUM_STRING_DESC
4
#define NUM_LANGUAGES
2
typedef STR_DESC USB_STRING_DESC [NUM_STRING_DESC * NUM_LANGUAGES + 1];
The
Client
application allocates the USB_STRING_DESC [NUM_STRING_DESC*
NUM_LANGUAGES + 1] array. The first element in the array (an element with index zero)
is a string descriptor that contains an array of two-byte LANGID codes supported by the
device (0x409 and 0x407 IDs). The next NUM_STRING_DESC descriptors are string
descriptors written using a language with 0x409 ID, the following NUM_STRING_DESC
descriptors - are with 0x407 language ID. The position of string descriptors must
correspond to the order of language IDs that are contained in the string descriptor, having
index zero. Thus, if the first language ID is 0x409 then the first four (NUM_STRING_DESC)
descriptors (having indices 1, 2, 3, and 4 in the array) must be written using a language
having ID 0x409. The next four descriptors must be written using a language having ID
0x407. It is not necessary to sort Language. Bytes in each Language ID are reverse
ordered.
string_desc
6
3
0x09
409 language ID
0x04
407 language ID 0x07
0x04
NUM_STRING_DESC
NUM_STRING_DESC
18
3
s
t
r
i
n
g
54
3
s
t
r
i
n
g
26
3
s
t
r
i
n
g
88
3
s
t
r
i
n
g
18
3
s
t
r
i
n
g
54
3
s
t
r
i
n
g
26
3
s
t
r
i
n
g
88
3
s
t
r
i
n
g
1
2
3
4
1
2
3
4
bLength
bDescriptorType
bString[256]
Strings written on language Strings written on language
having 0x409 ID
having 0x407 ID
NUM_LANGUAGES
Fig 11-1. Memory layout for string descriptors.
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-10
The string_desc variable points to the array that contains string descriptors.
11.2.10.2.
Sending the string descriptor to the Host
When the accept_event() function is called, it tests the request. If it is a request for a
string descriptor, the function calls the get_string_descriptor() routine:
get_string_descriptor (dc.request.wValue & 0xFF,
dc.request.wIndex,
dc.request.wLength);
The get_string_descriptor() function accepts three parameters:
desc_index - index of string descriptor;
languageID – language ID;
length – number of bytes to send.
According to the USB 1.1 specification, the Driver must send a short or zero length
packet to indicate the end of a transfer if the descriptor is shorter than the length
parameter, or only the initial bytes of the descriptor, if the descriptor is longer.
The function finds the index in an array (variable i is used) of the desired language ID for
a non-zero indexed string (language ID 0x409 has index zero in a string with index zero,
language ID 0x407 has index 1 in the same string). It reorders bytes in the languageID
parameter to prepare it for comparison, since IDs in the array are stored with reversed
byte order.
If the string descriptor with the required index or given language ID is not supported, the
function calls NOT_SUPPORTED_COMMAND ioctl. Otherwise it starts to prepare data for the
Host. If the desc_index parameter is zero, the Driver returns a string descriptor that
contains an array of two-byte LANGID codes supported by the device regardless of the
languageID parameter. This string descriptor has index zero in the array. Otherwise, the
string with the appropriate index and language ID will be found.
get_string_descriptor() function points stdesc variable to the required descriptor:
if (desc_index)
{
i *= NUM_STRING_DESC;
i += desc_index;
stdesc = (uint8 *) &((*usb_string_descriptor)[i]);
}
else
stdesc = (uint8 *) &((*usb_string_descriptor)[0]);
and gets the size of that descriptor:
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-11
size = *stdesc;
If the descriptor is longer than the number of requested bytes, it modifies the size:
if (size >= length)
size = length;
else
ioctl(usb_dev_file, USB_SET_SEND_ZLP);
If the Host requested more bytes than the length of the descriptor, a situation may arise
where the Driver must indicate an end of transfer, by sending a zero length packet (this
happens when the length of a descriptor is a multiple of the maximum size of packet for
endpoint number zero). Hence the USB_SET_SEND_ZLP ioctl must be called in such a case
on EP0 device file (a string will be sent on endpoint number zero). This does not mean
that a zero length packet will necessarily be sent. If the last packet is short (but not zero
length), a zero length packet will not be sent.
Then, the get_string_descriptor() function initiates a transfer of the descriptor to the
Host:
write(usb_dev_file, stdesc, size);
11.3.
USB Audio Application Function
Specification
This section describes the functions implemented in the USB Client program.
Function arguments for each routine are described as in, inout. An in argument
implies that the parameter value is an input only to the function. An inout argument
implies that a parameter is an input to the function, but the same parameter is also an
output from the function. Inout parameters are typically input pointer variables in which
the caller passes the address of a pre-allocated data structure to a function. The function
stores the result within that data structure. The actual value of the inout pointer
parameter is not changed.
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-12
11.3.1. accept_event
Call(s):
void usb_accept_command(int sig);
Arguments:
sig – the number of the raised signal (always SIGIO).
Returns:
No value returns.
Description:
Function tests each command to see if the program supports it and processes USB
events (reset signal, change_configuration). If the received command is supported, the
function sets the appropriate variable to perform the corresponding task. If it is a request
for a string descriptor, the function calls get_string_descriptor().
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-13
11.3.2. buffer_init
Call(s):
void buffer_init1 (iso_test_buffer * buffer);
Arguments:
Table 11-1. buffer_init arguments
inout Pointer to array of 5 iso_test_buffers
buffer
Returns:
No value returns.
Description:
This function initializes the first 160 bytes of buffer[i].databuf memory with
“100” , next 160 bytes - with“101” value, etc, processing all 5 buffers.
Code example:
buffer_init(buffers);
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-14
11.3.3. clear_buffer
Call(s):
void clear_buffer(void);
Arguments:
No arguments.
Returns:
No value returns.
Description:
This function fills all memory allocated for buffers with zeroes.
Code example:
clear_buffer();
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-15
11.3.4. get_string_descriptor
Call(s):
uint32 get_string_descriptor(uint8 desc_index, uint16 languageID, uint16 length);
Arguments:
Table 11-2.
desc_index
in
languageID
in
length
in
get_string_descriptor arguments
Index of required descriptor
Language ID
Number of bytes to send
Description: This function sends a string descriptor to the Host, having a given index
and written using a language having a given ID.
Returns:
Function returns status.
Status NOT_SUPPORTED_COMMAND indicates that program does not support
requested descriptor.
Status SUCCESS indicates, that required descriptor was sent to Host.
Code example:
if ((dc.request.bmRequestType == 0x80) &&
(dc.request.bRequest == GET_DESCRIPTOR) &&
((dc.request.wValue >> 8) == STRING))
{
status = get_string_descriptor(dc.request.wValue & 0xFF,
dc.request.wIndex,
dc.request.wLength);
}
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-16
11.3.5. init_audio_headers
Call(s):
void init_audio_headers(audio_buffer* buffer);
Arguments:
buffer
Table 11-3. init_audio_headers arguments
inout Pointer to audio buffer for which packet_length field
must be initialized.
Returns:
No value returns.
Description:
This function initializes packet_length fields of the audio buffer with
appropriate length of packets (16 bytes for 8 KHz sample rate and 90 and 72 bytes for
44.1 KHz sample rate).
Code example:
init_audio_headers();
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-17
11.3.6. init_buffer_headers
Call(s):
void init_audio_headers((iso_test_buffer* buffer);
Arguments:
buffer
Table 11-4. init_buffer_headers arguments
inout Pointer to test buffer for which packet_length field
must be initialized.
Returns:
No value returns.
Description:
This function initializes packet_length fields of test buffer with appropriate
length of packets (160 bytes).
Code example:
init_buffer_headers();
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-18
11.3.7. main_task
Call(s):
void main_task(void);
Arguments:
No arguments.
Returns:
No value returns.
Description:
This function performs loop-back task.
Code example:
main_task();
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-19
11.3.8. print_buffer_contents
Call(s):
void print_buffer_contents(iso_test_buffer* buffer);
Arguments:
buffer
Table 11-5. print_buffer_contents arguments
in
Pointer to array of 5 test buffers which should be printed
Returns:
No value returns.
Description:
This function prints the contents of received buffers.
Code example:
print_buffer_contents(buffers);
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-20
11.3.9. print_transfer_status
Call(s):
void print_transfer_status(uint32 in_print, uint32 out_print);
Arguments:
buffer
Table 11-6. print_transfer_status arguments
in
Pointer to array of 5 test buffers
Returns:
No value returns.
Description:
This function prints the contents of headers of each buffer. Header holds
information of test transfer completion.
Code example:
print_transfer_status(buffers);
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-21
11.3.10. process_data
Call(s):
void process_data (audio_buffer * buffer);
Arguments:
Table 11-7. process_data arguments
inout Pointer to the buffer to be processed
buffer
Returns:
No value returns.
Description:
This function reduces amplitude of each sample in the buffer by multiplying it by
volume value.
Code example:
process_data(&tx_data[swtch]);
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-22
11.3.11. test_case1_handler
Call(s):
void test_case1_handler(void);
Arguments:
No arguments.
Returns:
No value returns.
Description:
This function performs 5 test OUT transfers, each takes 5 frames; prints out the
received data and transfers status information.
Code example:
test_case1_handler();
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-23
11.3.12. test_case2_handler
Call(s):
void test_case2_handler(void);
Arguments:
No arguments.
Returns:
No value returns.
Description:
This function performs 5 test IN transfers of 5 packets.
Code example:
test_case2_handler();
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-24
11.3.13. test_case3_handler
Call(s):
void test_case3_handler(void);
Arguments:
No arguments.
Returns:
No value returns.
Description:
This function performs 5 test IN transfers of 5 packets and simultaneously 5 test
OUT transfer, each takes 5 frames.
Code example:
test_case3_handler();
M
Appendix 2: Audio Application.
PRELIMINARY—SUBJECT TO CHANGE WITHOUT NOTICE
11-25