forked from KolibriOS/kolibrios
184 lines
8.1 KiB
Plaintext
184 lines
8.1 KiB
Plaintext
|
When the kernel detects a connected USB device, it configures the device in
|
||
|
terms of USB protocol - SET_ADDRESS + SET_CONFIGURATION, the first
|
||
|
configuration is always selected. The kernel also reads device descriptor to
|
||
|
print some information, reads and parses configuration descriptor. For every
|
||
|
interface the kernel looks for class code of this interface and loads the
|
||
|
corresponding COFF driver. Currently the correspondence is hardcoded into
|
||
|
the kernel code and looks as follows: 3 = usbhid.obj, 8 = usbstor.obj,
|
||
|
9 is handled by the kernel itself, other = usbother.obj.
|
||
|
|
||
|
The driver must be standard driver in COFF format, exporting procedure
|
||
|
named "START" and a variable named "version". Loader calls "START" procedure
|
||
|
as stdcall with one parameter DRV_ENTRY = 1; if initialization is successful,
|
||
|
the "START" procedure is also called by shutdown code with one parameter
|
||
|
DRV_EXIT = -1.
|
||
|
|
||
|
The driver must register itself as a USB driver in "START" procedure.
|
||
|
This is done by call to exported function RegUSBDriver and passing the returned
|
||
|
value as result of "START" procedure.
|
||
|
|
||
|
void* __stdcall RegUSBDriver(
|
||
|
const char* name,
|
||
|
void* handler,
|
||
|
const USBFUNC* usbfunc
|
||
|
);
|
||
|
|
||
|
The parameter 'name' should match the name of driver, "usbhid" for usbhid.obj.
|
||
|
The parameter 'handler' is optional; if it is non-NULL, it should point to
|
||
|
the standard handler for IOCTL interface as in non-USB drivers.
|
||
|
The parameter 'usbfunc' is a pointer to the following structure:
|
||
|
|
||
|
struc USBFUNC
|
||
|
{
|
||
|
.strucsize dd ? ; size of the structure, including this field
|
||
|
.add_device dd ? ; pointer to AddDevice function in the driver
|
||
|
; required
|
||
|
.device_disconnect dd ? ; pointer to DeviceDisconnected function in the driver
|
||
|
; optional, may be NULL
|
||
|
; other functions may be added in the future
|
||
|
}
|
||
|
|
||
|
The driver should implement the function
|
||
|
|
||
|
void* __stdcall AddDevice(
|
||
|
void* pipe0,
|
||
|
void* configdescr,
|
||
|
void* interfacedescr
|
||
|
);
|
||
|
|
||
|
The parameter 'controlpipe' is a handle of the control pipe for endpoint zero
|
||
|
of the device. It can be used as the argument of USBControlTransferAsync.
|
||
|
The parameter 'configdescr' points to USB configuration descriptor
|
||
|
and all associated data, as returned by GET_DESCRIPTOR request.
|
||
|
The total length of all associated data is contained in the configuration
|
||
|
descriptor.
|
||
|
The parameter 'interfacedescr' points to USB interface descriptor corresponding
|
||
|
to the interface which is initializing. This is a pointer inside data
|
||
|
associated with the configuration descriptor.
|
||
|
Note that one device can implement many interfaces, so AddDevice may be
|
||
|
called several times with the same 'configdescr' and different 'interfacedescr'.
|
||
|
The returned value NULL means that the initialization has failed.
|
||
|
Any other value means that configuration was successful; the kernel does not
|
||
|
try to interpret the value. It can be, for example, pointer to the internal
|
||
|
data allocated with Kmalloc, or index in some internal table. Remember that
|
||
|
Kmalloc() is NOT stdcall, it destroys ebx.
|
||
|
|
||
|
The driver can implement the function
|
||
|
|
||
|
void __stdcall DeviceDisconnected(
|
||
|
void* devicedata
|
||
|
);
|
||
|
|
||
|
If this function is implemented, the kernel calls it when the device is
|
||
|
disconnected, passing the returned value of AddDevice as 'devicedata'.
|
||
|
|
||
|
The driver can use the following functions exported by the kernel.
|
||
|
|
||
|
void* __stdcall USBOpenPipe(
|
||
|
void* pipe0,
|
||
|
int endpoint,
|
||
|
int maxpacketsize,
|
||
|
int type,
|
||
|
int interval
|
||
|
);
|
||
|
|
||
|
The parameter 'pipe0' is a handle of the pipe for endpoint zero for
|
||
|
the device, as passed to AddDevice. It is used to identify the device.
|
||
|
The parameter 'endpoint' is endpoint number as defined by USB. Lower
|
||
|
4 bits form the number itself, bit 7 - highest bit of low byte -
|
||
|
is 0/1 for OUT/IN endpoints, other bits should be zero.
|
||
|
The parameter 'maxpacketsize' sets the maximum packet size for this pipe.
|
||
|
The parameter 'type' selects the type of the endpoint as defined by USB:
|
||
|
0 = control, 1 = isochronous (not supported yet), 2 = bulk, 3 = interrupt.
|
||
|
The parameter 'interval' is ignored for control and bulk endpoints.
|
||
|
For interrupt endpoints, it sets the polling interval in milliseconds.
|
||
|
The function returns a handle to the pipe or NULL on failure.
|
||
|
|
||
|
void* __stdcall USBNormalTransferAsync(
|
||
|
void* pipe,
|
||
|
void* buffer,
|
||
|
int size,
|
||
|
void* callback,
|
||
|
void* calldata,
|
||
|
int flags
|
||
|
);
|
||
|
void* __stdcall USBControlTransferAsync(
|
||
|
void* pipe,
|
||
|
void* config,
|
||
|
void* buffer,
|
||
|
int size,
|
||
|
void* callback,
|
||
|
void* calldata,
|
||
|
int flags
|
||
|
);
|
||
|
|
||
|
The first function inserts a bulk or interrupt transfer to the transfer queue
|
||
|
for given pipe. Type and direction of transfer are fixed for bulk and interrupt
|
||
|
endpoints and are set in USBOpenPipe. The second function inserts a control
|
||
|
transfer to the transfer queue for given pipe. Direction of a control transfer
|
||
|
is concluded from 'config' packet, bit 7 of byte 0 is set for IN transfers
|
||
|
and cleared for OUT transfers. These function return immediately; when data
|
||
|
are transferred, the callback function will be called.
|
||
|
|
||
|
The parameter 'pipe' is a handle returned by USBOpenPipe.
|
||
|
The parameter 'config' of USBControlTransferAsync points to 8-byte
|
||
|
configuration packet as defined by USB.
|
||
|
The parameter 'buffer' is a pointer to buffer. For IN transfers, it will be
|
||
|
filled with the data. For OUT transfers, it should contain data to be
|
||
|
transferred. It can be NULL for an empty transfer or if no additional data are
|
||
|
required for a control transfer.
|
||
|
The parameter 'size' is size of data to transfer. It can be 0 for an empty
|
||
|
transfer or if no additional data are required for a control transfer.
|
||
|
The parameter 'callback' is a pointer to a function which will be called
|
||
|
when the transfer will be done.
|
||
|
The parameter 'calldata' will be passed as is to the callback function.
|
||
|
For example, it can be NULL, it can be a pointer to device data or it can be
|
||
|
a pointer to data used to pass additional parameters between caller and
|
||
|
callback. The transfer-specific data can also be associated with 'buffer',
|
||
|
preceding (negative offsets from 'buffer') or following (offsets more than
|
||
|
or equal to 'size') the buffer itself.
|
||
|
The parameter 'flags' is the bitmask.
|
||
|
The bit 0 is ignored for OUT transfers, for IN transfers it controls whether
|
||
|
the device can transfer less data than 'size' bytes. If the bit is 0, a small
|
||
|
transfer is an error; if the bit is 1, a small transfer is OK.
|
||
|
All other bits are reserved and should be zero.
|
||
|
The returned value is NULL if an error occured and non-NULL if the transfer
|
||
|
was successfully queued. If an error will occur later, the callback function
|
||
|
will be notified.
|
||
|
|
||
|
void __stdcall CallbackFunction(
|
||
|
void* pipe,
|
||
|
int status,
|
||
|
void* buffer,
|
||
|
int length,
|
||
|
void* calldata
|
||
|
);
|
||
|
|
||
|
The parameters 'pipe', 'buffer', 'calldata' are the same as for the
|
||
|
corresponding USB*TransferAsync.
|
||
|
The parameter 'length' is the number of bytes transferred. For
|
||
|
control transfers, this includes 8 bytes from SETUP stage, so
|
||
|
0 means that SETUP stage failed and 'size'+8 means full transfer.
|
||
|
The parameter 'status' is nonzero if an error occured.
|
||
|
USB_STATUS_OK = 0 ; no error
|
||
|
USB_STATUS_CRC = 1 ; CRC error
|
||
|
USB_STATUS_BITSTUFF = 2 ; bit stuffing violation
|
||
|
USB_STATUS_TOGGLE = 3 ; data toggle mismatch
|
||
|
USB_STATUS_STALL = 4 ; device returned STALL
|
||
|
USB_STATUS_NORESPONSE = 5 ; device not responding
|
||
|
USB_STATUS_PIDCHECK = 6 ; invalid PID check bits
|
||
|
USB_STATUS_WRONGPID = 7 ; unexpected PID value
|
||
|
USB_STATUS_OVERRUN = 8 ; too many data from endpoint
|
||
|
USB_STATUS_UNDERRUN = 9 ; too few data from endpoint
|
||
|
USB_STATUS_BUFOVERRUN = 12 ; overflow of internal controller buffer
|
||
|
; possible only for isochronous transfers
|
||
|
USB_STATUS_BUFUNDERRUN = 13 ; underflow of internal controller buffer
|
||
|
; possible only for isochronous transfers
|
||
|
USB_STATUS_DISCONNECTED = 16 ; device disconnected
|
||
|
|
||
|
If several transfers are queued for the same pipe, their callback functions
|
||
|
are called in the same order as they were queued.
|
||
|
When the device is disconnected, all callback functions are called
|
||
|
with USB_STATUS_DISCONNECTED. The call to DeviceDisconnected() occurs after
|
||
|
all callbacks.
|