info@mdiehl.de
Copyright © 2003 by Martin Diehl
$Id:$Revision History | ||
---|---|---|
Revision 1.00 | 2003-06-26 | Revised by: Martin Diehl |
first draft |
New style dongle drivers are represented by a struct dongle_driver (declared in sir-dev.h). One (or more, see actisys-sir) of these struct shall be defined statically in the driver. The module_init function of the driver module calls irda_register_dongle to register the driver with the sir-dev core. The corresponding irda_unregister_dongle call removes the dongle driver and must be called from module_exit. Both register/unregister calls return zero on success, or errno-code <0 in the case of a problem.
The struct dongle_driver provides the following members which must be inititalized before registering the dongle:
must be set to THIS_MODULE to make the sir-dev core handle all the module refcounting and locking issues for the driver.
an type id from enum IRDA_DONGLE (defined in linux/irda.h). The same values from the old dongle drivers are reused
string to indicate driver name, for debug messages f.e.
dongle driver callbacks, see below.
The open/close/reset/set_speed callbacks are always invoked with process context, namely from the irda kernel thread without spinlocks hold. So the driver may sleep there. Actually, the callback execution is part of some sir-core state machine which handle the sir configuration changes. For details about the state machine interaction see below.
When any of the four callback gets invoked it can be sure all pending transmission ahs been finished before so it could immediately start to reset or change speed without risk to corrupt any data still in some transmit buffer.
The callbacks expect a struct sir_dev as argument. This refers to the sir device representation corresponding to the controlling port. Most members of this struct sir_dev (declared in "sir-dev.h" are private in the sir-core. The dongle driver should not touch or make assumptions for any member there which is not described in this document.
There are three callback members in the struct sir_dev which can be used by the dongle driver operations to interact with the controlling port:
set the modem control lines DTR and RTS of the controlling port indicated by sir_dev.
write bytes directly to the controlling port indicated by sir_dev
read bytes directly from the controlling port indicated by sir_dev
Another common feature for all dongle driver callbacks is the return value. They are expected to return:
<0: failure (negative errno value)
=0: success
>0: tells the sir-dev core the dongle need more time to complete. In this case the same callback will be invoked again after a delay given by the return value in msec.
Instead of return positive values to ask for some delay the dongle driver might just call usleep to achieve the same goal. No problem in 99% of all cases with only a single dongle in the system. However, this blocks the irda-thread so if there were concurrent configuration requests for other dongles f.e. they might get some delay. Using the retval>0 method allows the irda-thread to schedule the other dongle requests meanwhile - YMMV.
Called when the dongle gets attached to the port - attached according to ioctl-notification ("irattach"), not physical attachment. When called the driver shall:
power up the dongle. In most cases dongles are powered by the modem control lines - the driver would use dev->set_dtr_rts here.
initialize the dev->qos structure to indicate the supported baudrates and min-turn-time
if required, do some additional waiting in order to get the power stabilized for the dongle
When dongle_open finally returns with retval==0 the sir-dev core will always wait for 50msec before expecting the dongle to be ready for normal operation. This default delay doesn't hurt during dongle-attach and seems to be sufficient for power settling with many dongles. This means an additional power-up delay is not needed unless the dongle would take more than 50ms to power up.
Further note, there is no need to reset the dongle in the open callback. The sir-dev core will always call dongle_reset after this 50msec general delay.
Finally, there's no need to bump module refcounts there. The module use count is already incremented before dongle_open gets called to avoid SMP races. The refcount gets decremented again either after dongle_close returns or if dongle_open fails with retval <0.
Called when the dongle gets logically detached from the controlling port, usually when killing irattach. There's not much to do in this case, usually just unpower the dongle and return success. Thereafter the sir-dev core will decrement the module refcount so the driver module might be unloaded safely.
Called whenever the dongle needs to be set to a defined state - initially after the dongle was attached or at any time later when we want to change the baudrate. When called the driver shall:
execute the dongle-specific reset protocoll to get it to a defined state
set dev->speed to indicate what the default speed of this dongle is. For most dongles this will be 9600 baud, but there are exceptions!
Called during irda speed change sequence to make the dongle operate at a new speed. This is called after the dongle was reset so the dongle is in the default state. When called the driver shall:
execute the dongle-specific protocol to switch to the requested speed
set dev->speed when done
The sequence of operations executed by the sir-core when changing speed of a dongle is:
wait until pending transmission has finished
let the dongle-driver reset the dongle (dongle->reset)
set the speed of the controlling port to the dongle's default speed
let the dongle-driver set the new speed (dongle->set_speed)
set the speed of the controlling port to the new speed
The state machine is the core of the irda-thread (in sir_kthread.c) which executes requested configuration changes. In theory for all the required delays in those operation we could just sleep there because we have process context. The only drawback is with several dongles one of them might sleep in some 300msec power-settling delay while another want just needs to change speed. Since the irda-thread would be busy then, the speed change might easily fail or at least take very long. To avoid this problem the state machine helps to execute the relatively fast dongle operations as separate individual steps without need to sleep. This is controlled by a timer and allows to execute several config changes in parallel. Of course the actual operation is handled from irda-thread, not timer-softirq, so one can still sleep there if desired.
First, each sir-dev port has its own state machine context at sir_dev->fsm. Actually there are two stacked state machine. The primary one uses fsm.state and is complete private in the sir-dev core. The secondary state machine is there to help dongle driver callbacks (all except dongle_close). For this to work the dongle driver may use the fsm.substate member.
For any config change handled by the core primary state machin the fsm.substate for the first callback invokation is set as follows:
callback: dongle_open:
initial fsm.substate value: SIRDEV_STATE_DONGLE_OPEN
callback: dongle_reset:
initial fsm.substate value: SIRDEV_STATE_DONGLE_RESET
callback: dongle_set_speed:
initial fsm.substate value: SIRDEV_STATE_DONGLE_SPEED
If the callback wants to benefit from state machine support, it can define further private substate using offset 1...0xff. I.e. the first private substate for dongle-reset would be (SIRDEV_STATE_DONGLE_RESET + 1). With the callback returning retval>0 to indicate the requested delay in msec, the sir-dev core preserves the private substate and will repeat calling the same callback until the retval is <=0.
For short delays however, it's of course better to keep things simple and just usleep or udelay without returning to the sir-dev core. For an example how to use the state machine from dongle drivers take a look at tekram-sir.c