Comtrol eCos User Manual

Page 700

Advertising
background image

Sending Data to the Host

The normal sequence of events is that the USB device driver will update the appropriate hardware registers. At
some point after that the host will attempt to fetch data by transmitting an IN token. Since a transmit operation
is now in progress the peripheral can send a packet of data, and the host will generate an ACK. At this point the
USB hardware will generate an interrupt, and the device driver will service this interrupt and arrange for a DSR
to be called. Isochronous and interrupt transfers involve just a single packet. However, bulk transfers may involve
multiple packets so the device driver has to check whether there is more data to send and set things up for the next
packet. When the device driver DSR detects a complete transfer it will inform higher-level code by invoking the
supplied completion function.

This means that the completion function will normally be invoked by a DSR and not in thread context - although
some USB device drivers may have a different implementation. Therefore the completion function is restricted in
what it can do, in particular it must not make any calls that will or may block such as locking a mutex or allocating
memory. The kernel documentation should be consulted for more details of DSR’s and interrupt handling generally.

It is possible that the completion function will be invoked before

usbs_start_tx_buffer

returns. Such an event

would be unusual because the transfer cannot happen until the next time the host tries to fetch data from this
peripheral, but it may happen if, for example, another interrupt happens and a higher priority thread is scheduled
to run. Also, if the endpoint is currently halted then the completion function will be invoked immediately with

-EAGAIN

: typically this will happen in the current thread rather than in a separate DSR. The completion function

is allowed to start another transfer immediately by calling

usbs_start_tx_buffer

again.

USB device drivers are not expected to perform any locking. It is the responsibility of higher-level code to ensure
that there is only one transmit operation for a given endpoint in progress at any one time. If there are concurrent
calls to

usbs_start_tx_buffer

then the resulting behaviour is undefined. For typical USB applications this does

not present any problems because only piece of code will access a given endpoint at any particular time.

The following code fragment illustrates a very simple use of

usbs_start_tx_buffer

to implement a blocking

transmit, using a semaphore to synchronise between the foreground thread and the DSR. For a simple example like
this no completion data is needed.

static int error_code = 0;

static cyg_sem_t completion_wait;

static void

completion_fn(void* data, int result)

{

error_code = result;

cyg_semaphore_post(&completion_wait);

}

int

blocking_transmit(usbs_tx_endpoint* ep, const unsigned char* buf, int len)

{

error_code = 0;

usbs_start_tx_buffer(ep, buf, len, &completion_fn, NULL);

cyg_semaphore_wait(&completion_wait);

return error_code;

}

There is also a utility function

usbs_start

. This can be used by code that wants to manipulate

data

endpoints

directly, specifically the

complete_fn

,

complete_data

,

buffer

and

buffer_size

fields.

usbs_start_tx

just calls a function supplied by the device driver.

596

Advertising