DeviceFS USB technical
From RISC OS
The following is a collection of notes for using the RISC OS DeviceFS USB API. At present, this is mostly relevant to those writing USB drivers as modules rather than applications and as such is of a rather low-level nature. This has mostly been gathered from posts to comp.sys.acorn.programmer, guesswork and experimentation, so there is a significant risk of errors and incompleteness.
Documentation and examples
On Castle's website, the high-level USB interface is described. This includes details on how to enumerate existing devices and launching applications when devices are plugged in. There is also an archive of example source and a C header file describing some of the data structures used to communicate with the driver.
On closing a DeviceFS stream, the close file operation will sometimes block waiting for a buffer to be emptied. For this reason, it is sensible to flush the stream's buffer before closing it. Unplugging a blocking device will cause the close SWI to return an error which is useful for getting control back.
Direct buffer access
You can bypass the usual FileSwitch interface (SWI OS_GBPB) to read or write packets by accessing the pipe's buffer directly. This is particularly useful in module code as it is much quicker and the buffer manager service routine can be called safely in IRQ/SVC modes with/without IRQs enabled which is quite convenient.
There are numerous handles associated with each open pipe:
- FileSwitch stream handle, or file handle (as returned by SWI OS_Find), needed for SWI OS_GBPB etc.
- Buffer handle, you can get this with SWI DeviceFS_CallDevice (1<<31)+3. Needed for direct reading to or writing from the buffer and for flushing it before closing connection.
- DeviceFS stream handle, can get this from SWI DeviceFS_CallDevice (1<<31)+3.
- DeviceFS driver's handle (can get this from UpCall 10). Needed to initiate a background read and to find the real length of the returned data, see below.
Initiating background reads
To initiate a background USB read, you can use SWI DeviceFS_CallDevice 3 (Wake up for TX). This will return immediately. When the transfer has occurred, the buffer will be filled with the USB data and then padded until it is full (only used with non-bulk pipes, so this may be inaccurate for other transfer types).
This padding is unfortunate as it is impossible to determine the actual amount of data transferred using the public API. This can be worked around by accessing the actual transfer size which is in the 11th word from the DeviceFS driver's handle. After you have finished reading the real data, you need to flush the padding and can then call SWI DeviceFS_CallDevice 3 to initiate the next transfer. UpCall 15 will trigger when data is received.
If you can get away without abusing this knowledge it is probably wise to do so, as it is relaying on the private internal workings of the USB Driver.
Transfer complete notification
For module drivers, the examples on the Castle website use SWI Buffer_LinkDevice to get notification of buffers filling or emptying. Attempting this on RISC OS 5.11 results in a "Unable to detach current owner of this buffer" error.
When a read operation is complete, UpCall 15 (Device data present) is triggered assuming the buffer was empty beforehand, which provides a work around.
There doesn't appear to be an equivalent UpCall for buffer exhaustion to indicate a completed write operation. It may be possible to use UpCall 9 (Buffer emptying) or UpCall 17 (Device threshold above), though I've found neither of these reliable.
Summary of problems and unknowns
- It would be nice to have a nicer method of getting the DeviceFS device driver's handle, say via SWI DeviceFS_CallDevice, upcalls are not always conveniant. Or a public API for initiating a read.
- It would be nice to get rid of the padding of short reads and the need to probe internal USB Driver workspace to get the real transfer size.
- How to get the best performance out of the system, inparticular: is it possible to queue outgoing packets? If not what is the best method of writing a sequence of them as quickly and efficiently as possible?
- SWI Buffer_LinkDevice is claimed by something else, despite the examples.
- Are users of the API allowed to set buffer or device thresholds, or are these used by the implementation.
- Can more than one transfer's worth of data can be in input or output buffers at a time.