New Driver API for Linux IrDA Dongles

Martin Diehl

info@mdiehl.de

$Id:$
Revision History
Revision 1.002003-06-26Revised by: Martin Diehl
first draft

Table of Contents
1. Basic Driver Operation
2. Dongle Callbacks
2.1. Dongle "open" callback
2.2. Dongle "close" callback
2.3. Dongle "reset" callback
2.4. Dongle "set_speed" callback
3. Interaction with the sir-dev config state machine

1. Basic Driver Operation

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:

owner

must be set to THIS_MODULE to make the sir-dev core handle all the module refcounting and locking issues for the driver.

type

an type id from enum IRDA_DONGLE (defined in linux/irda.h). The same values from the old dongle drivers are reused

driver_name

string to indicate driver name, for debug messages f.e.

open/close/reset/set_speed

dongle driver callbacks, see below.

While registered with the sir-dev core the dongle driver is just idle waiting for one of its callbacks to get invoked.

2. Dongle Callbacks

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_dtr_rts(sir_dev, dtr, rts)

set the modem control lines DTR and RTS of the controlling port indicated by sir_dev.

write(sir_dev, buffer, len)

write bytes directly to the controlling port indicated by sir_dev

read(sir_dev, buffer, len)

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:

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.

2.1. Dongle "open" callback

int dongle_open(struct sir_dev *dev);

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.

2.2. Dongle "close" callback

int dongle_close(struct sir_dev *dev);

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.

2.3. Dongle "reset" callback

int dongle_reset(struct sir_dev *dev);

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!

2.4. Dongle "set_speed" callback

int dongle_set_speed(struct sir_dev *dev, unsigned speed);

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

3. Interaction with the sir-dev config state machine

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