Device driver's view

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_device 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.

#include <parport.h>
   

struct parport *parport_find_number(int number);

#include <parport.h>
   

struct parport *parport_find_base(unsigned long base);

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);

void parport_close(struct pardevice *dev);

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);

void parport_unregister_device(struct pardevice *dev);

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.

#include <parport.h>
   

int parport_claim(struct pardevice *dev);

int parport_claim_or_block(struct pardevice *dev);

void parport_release(struct pardevice *dev);

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.

#include <parport.h>
   

int parport_yield(struct pardevice *dev);

int parport_yield_blocking(struct pardevice *dev);

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).