This section is written from the point of view of the device driver programmer, who might be writing a driver for a printer or a scanner or else anything that plugs into the parallel port. It explains how to use the parport interface to find parallel ports, use them, and share them with other device drivers.
We'll start out with a description of the various functions that can be called, and then look at a reasonably simple example of their use: the printer driver.
The interactions between the device driver and the parport layer are as follows. First, the device driver registers its existence with parport, in order to get told about any parallel ports that have been (or will be) detected. When it gets told about a parallel port, it then tells parport that it wants to drive a device on that port. Thereafter it can claim exclusive access to the port in order to talk to its device.
So, the first thing for the device driver to do is tell
parport that it wants to know what parallel
ports are on the system. To do this, it uses the
parport_register_driver
function:
#include <parport.h> struct parport_driver { const char *name; void (*attach) (struct parport *); void (*detach) (struct parport *); struct parport_driver *next; };
int parport_register_driver
(struct parport_driver *driver);
In other words, the device driver passes pointers to a couple of
functions to parport, and
parport calls attach
for
each port that's detected (and detach
for each
port that disappears---yes, this can happen).
The next thing that happens is that the device driver tells
parport that it thinks there's a device on the
port that it can drive. This typically will happen in the driver's
attach
function, and is done with
parport_register_device
:
#include <parport.h>
struct pardevice *parport_register_device
(struct parport *port, const char *name, int (*pf)
(void *), void (*kf)
(void *), void (*irq_func)
(int, void *, struct pt_regs *), int flags, void *handle);
The port
comes from the parameter supplied
to the attach
function when it is called, or
alternatively can be found from the list of detected parallel ports
directly with the (now deprecated)
parport_enumerate
function. A better way of
doing this is with parport_find_number
or
parport_find_base
functions, which find ports
by number and by base I/O address respectively.
The next three parameters, pf
,
kf
, and irq_func
, are
more function pointers. These callback functions get called under
various circumstances, and are always given the
handle
as one of their parameters.
The preemption callback, pf
, is called when
the driver has claimed access to the port but another device driver
wants access. If the driver is willing to let the port go, it
should return zero and the port will be released on its behalf.
There is no need to call parport_release
. If
pf
gets called at a bad time for letting the
port go, it should return non-zero and no action will be taken. It
is good manners for the driver to try to release the port at the
earliest opportunity after its preemption callback is
called.
The "kick" callback, kf
, is
called when the port can be claimed for exclusive access; that is,
parport_claim
is guaranteed to succeed inside
the "kick" callback. If the driver wants to claim the
port it should do so; otherwise, it need not take any
action.
The irq_func
callback is called,
predictably, when a parallel port interrupt is generated. But it
is not the only code that hooks on the interrupt. The sequence is
this: the lowlevel driver is the one that has done
request_irq
; it then does whatever
hardware-specific things it needs to do to the parallel port
hardware (for PC-style ports, there is nothing special to do); it
then tells the IEEE 1284 code about the interrupt, which may
involve reacting to an IEEE 1284 event, depending on the current
IEEE 1284 phase; and finally the irq_func
function is called.
None of the callback functions are allowed to block.
The flags
are for telling
parport any requirements or hints that are
useful. The only useful value here (other than
0
, which is the usual value) is
PARPORT_DEV_EXCL
. The point of that flag is
to request exclusive access at all times---once a driver has
successfully called parport_register_device
with that flag, no other device drivers will be able to register
devices on that port (until the successful driver deregisters its
device, of course).
The PARPORT_DEV_EXCL
flag is for preventing
port sharing, and so should only be used when sharing the port with
other device drivers is impossible and would lead to incorrect
behaviour. Use it sparingly!
Devices can also be registered by device drivers based on their device numbers (the same device numbers as in the previous section).
The parport_open
function is similar to
parport_register_device
, and
parport_close
is the equivalent of
parport_unregister_device
. The difference is
that parport_open
takes a device number rather
than a pointer to a struct parport
.
#include <parport.h>
struct pardevice *parport_open
(int devnum, const char *name, int (*pf)
(void *), int (*kf)
(void *), int (*irqf)
(int, void *, struct pt_regs *), int flags, void *handle);
struct pardevice *parport_register_device
(struct parport *port, const char *name, int (*pf)
(void *), int (*kf)
(void *), int (*irqf)
(int, void *, struct pt_regs *), int flags, void *handle);
The intended use of these functions is during driver initialisation while the driver looks for devices that it supports, as demonstrated by the following code fragment:
int devnum = -1; while ((devnum = parport_find_class (PARPORT_CLASS_DIGCAM, devnum)) != -1) { struct pardevice *dev = parport_open (devnum, ...); ... }
Once your device driver has registered its device and been handed a
pointer to a struct pardevice
, the next
thing you are likely to want to do is communicate with the device
you think is there. To do that you'll need to claim access to the
port.
To claim access to the port, use parport_claim
or parport_claim_or_block
. The first of these
will not block, and so can be used from interrupt context. If
parport_claim
succeeds it will return zero and
the port is available to use. It may fail (returning non-zero) if
the port is in use by another driver and that driver is not willing
to relinquish control of the port.
The other function, parport_claim_or_block
,
will block if necessary to wait for the port to be free. If it
slept, it returns 1
; if it succeeded without
needing to sleep it returns 0
. If it fails it
will return a negative error code.
When you have finished communicating with the device, you can give
up access to the port so that other drivers can communicate with
their devices. The parport_release
function
cannot fail, but it should not be called without the port claimed.
Similarly, you should not try to claim the port if you already have
it claimed.
You may find that although there are convenient points for your driver to relinquish the parallel port and allow other drivers to talk to their devices, it would be preferable to keep hold of the port. The printer driver only needs the port when there is data to print, for example, but a network driver (such as PLIP) could be sent a remote packet at any time. With PLIP, it is no huge catastrophe if a network packet is dropped, since it will likely be sent again, so it is possible for that kind of driver to share the port with other (pass-through) devices.
The parport_yield
and
parport_yield_blocking
functions are for
marking points in the driver at which other drivers may claim the
port and use their devices. Yielding the port is similar to
releasing it and reclaiming it, but is more efficient because
nothing is done if there are no other devices needing the port. In
fact, nothing is done even if there are other devices waiting but
the current device is still within its "timeslice".
The default timeslice is half a second, but it can be adjusted via
a /proc entry.
The first of these, parport_yield
, will not
block but as a result may fail. The return value for
parport_yield
is the same as for
parport_claim
. The blocking version,
parport_yield_blocking
, has the same return
code as parport_claim_or_block
.
Once the port has been claimed, the device driver can use the
functions in the struct parport_operations
pointer in the struct parport
it has a
pointer to. For example:
port->ops->write_data (port, d);
Some of these operations have "shortcuts". For
instance, parport_write_data
is equivalent to
the above, but may be a little bit faster (it's a macro that in
some cases can avoid needing to indirect through
port
and ops
).