The Linux 2.4 Parallel Port Subsystem | ||
---|---|---|
<<< Previous | User-level device drivers | Next >>> |
The ppdev interface is largely the same as that of other character special devices, in that it supports open, close, read, write, and ioctl. The constants for the ioctl commands are in include/linux/ppdev.h.
The device node /dev/parport0 represents any device that is connected to parport0, the first parallel port in the system. Each time the device node is opened, it represents (to the process doing the opening) a different device. It can be opened more than once, but only one instance can actually be in control of the parallel port at any time. A process that has opened /dev/parport0 shares the parallel port in the same way as any other device driver. A user-land driver may be sharing the parallel port with in-kernel device drivers as well as other user-land drivers.
Most of the control is done, naturally enough, via the ioctl call. Using ioctl, the user-land driver can control both the ppdev driver in the kernel and the physical parallel port itself. The ioctl call takes as parameters a file descriptor (the one returned from opening the device node), a command, and optionally (a pointer to) some data.
PPCLAIM
Claims access to the port. As a user-land device driver
writer, you will need to do this before you are able to
actually change the state of the parallel port in any way.
Note that some operations only affect the
ppdev driver and not the port, such as
PPSETMODE
; they can be performed while
access to the port is not claimed.
PPEXCL
Instructs the kernel driver to forbid any sharing of the port
with other drivers, i.e. it requests exclusivity. The
PPEXCL
command is only valid when the
port is not already claimed for use, and it may mean that the
next PPCLAIM
ioctl
will fail: some other driver may already have registered
itself on that port.
Most device drivers don't need exclusive access to the port. It's only provided in case it is really needed, for example for devices where access to the port is required for extensive periods of time (many seconds).
Note that the PPEXCL
ioctl doesn't actually claim the port
there and then---action is deferred until the
PPCLAIM
ioctl is
performed.
PPRELEASE
Releases the port. Releasing the port undoes the effect of claiming the port. It allows other device drivers to talk to their devices (assuming that there are any).
PPYIELD
Yields the port to another driver. This ioctl is a kind of short-hand for releasing the port and immediately reclaiming it. It gives other drivers a chance to talk to their devices, but afterwards claims the port back. An example of using this would be in a user-land printer driver: once a few characters have been written we could give the port to another device driver for a while, but if we still have characters to send to the printer we would want the port back as soon as possible.
It is important not to claim the parallel port for too long,
as other device drivers will have no time to service their
devices. If your device does not allow for parallel port
sharing at all, it is better to claim the parallel port
exclusively (see PPEXCL
).
PPNEGOT
Performs IEEE 1284 negotiation into a particular mode. Briefly, negotiation is the method by which the host and the peripheral decide on a protocol to use when transferring data.
An IEEE 1284 compliant device will start out in compatibility mode, and then the host can negotiate to another mode (such as ECP).
The ioctl parameter should be a pointer to an int; values for this are in incluce/linux/parport.h and include:
IEEE1284_MODE_COMPAT
IEEE1284_MODE_NIBBLE
IEEE1284_MODE_BYTE
IEEE1284_MODE_EPP
IEEE1284_MODE_ECP
The PPNEGOT
ioctl
actually does two things: it performs the on-the-wire
negotiation, and it sets the behaviour of subsequent
read/write calls so
that they use that mode (but see
PPSETMODE
).
PPSETMODE
Sets which IEEE 1284 protocol to use for the read and write calls.
The ioctl parameter should be a pointer to an int.
PPGETMODE
Retrieves the current IEEE 1284 mode to use for read and write.
PPGETTIME
Retrieves the time-out value. The read
and write calls will time out if the
peripheral doesn't respond quickly enough. The
PPGETTIME
ioctl
retrieves the length of time that the peripheral is allowed to
have before giving up.
The ioctl parameter should be a pointer
to a struct timeval
.
PPSETTIME
Sets the time-out. The ioctl parameter
should be a pointer to a struct
timeval
.
PPGETMODES
Retrieves the capabilities of the hardware (i.e. the
modes
field of the
parport
structure).
PPSETFLAGS
Sets flags on the ppdev device which can affect future I/O operations. Available flags are:
PP_FASTWRITE
PP_FASTREAD
PP_W91284PIC
PPWCONTROL
Sets the control lines. The ioctl parameter is a pointer to an unsigned char, the bitwise OR of the control line values in include/linux/parport.h.
PPRCONTROL
Returns the last value written to the control register, in the form of an unsigned char: each bit corresponds to a control line (although some are unused). The ioctl parameter should be a pointer to an unsigned char.
This doesn't actually touch the hardware; the last value written is remembered in software. This is because some parallel port hardware does not offer read access to the control register.
The control lines bits are defined in include/linux/parport.h:
PARPORT_CONTROL_STROBE
PARPORT_CONTROL_AUTOFD
PARPORT_CONTROL_SELECT
PARPORT_CONTROL_INIT
PPFCONTROL
Frobs the control lines. Since a common operation is to
change one of the control signals while leaving the others
alone, it would be quite inefficient for the user-land driver
to have to use PPRCONTROL
, make the
change, and then use PPWCONTROL
. Of
course, each driver could remember what state the control
lines are supposed to be in (they are never changed by
anything else), but in order to provide
PPRCONTROL
, ppdev
must remember the state of the control lines anyway.
The PPFCONTROL
ioctl
is for "frobbing" control lines, and is like
PPWCONTROL
but acts on a restricted set
of control lines. The ioctl parameter is
a pointer to a struct
ppdev_frob_struct
:
struct ppdev_frob_struct { unsigned char mask; unsigned char val; }; |
The mask
and
val
fields are bitwise ORs of
control line names (such as in
PPWCONTROL
). The operation performed by
PPFCONTROL
is:
new_ctr = (old_ctr & ~mask) | val; |
In other words, the signals named in
mask
are set to the values in
val
.
PPRSTATUS
Returns an unsigned char containing bits set for
each status line that is set (for instance,
PARPORT_STATUS_BUSY
). The
ioctl parameter should be a pointer to an
unsigned char.
PPDATADIR
Controls the data line drivers. Normally the computer's parallel port will drive the data lines, but for byte-wide transfers from the peripheral to the host it is useful to turn off those drivers and let the peripheral drive the signals. (If the drivers on the computer's parallel port are left on when this happens, the port might be damaged.)
This is only needed in conjunction with
PPWDATA
or
PPRDATA
.
The ioctl parameter is a pointer to an int. If the int is zero, the drivers are turned on (forward direction); if non-zero, the drivers are turned off (reverse direction).
PPWDATA
Sets the data lines (if in forward mode). The ioctl parameter is a pointer to an unsigned char.
PPRDATA
Reads the data lines (if in reverse mode). The ioctl parameter is a pointer to an unsigned char.
PPCLRIRQ
Clears the interrupt count. The ppdev
driver keeps a count of interrupts as they are triggered.
PPCLRIRQ
stores this count in an
int, a pointer to which is passed in as the
ioctl parameter.
In addition, the interrupt count is reset to zero.
PPWCTLONIRQ
Set a trigger response. Afterwards when an interrupt is
triggered, the interrupt handler will set the control lines as
requested. The ioctl parameter is a
pointer to an unsigned char, which is interpreted
in the same way as for PPWCONTROL
.
The reason for this ioctl is simply
speed. Without this ioctl, responding to
an interrupt would start in the interrupt handler, switch
context to the user-land driver via poll
or select, and then switch context back
to the kernel in order to handle
PPWCONTROL
. Doing the whole lot in the
interrupt handler is a lot faster.
Transferring data using read and
write is straightforward. The data is
transferring using the current IEEE 1284 mode (see the
PPSETMODE
ioctl). For
modes which can only transfer data in one direction, only the
appropriate function will work, of course.
The ppdev driver provides user-land device drivers with the ability to wait for interrupts, and this is done using poll (and select, which is implemented in terms of poll).
When a user-land device driver wants to wait for an interrupt, it sleeps with poll. When the interrupt arrives, ppdev wakes it up (with a "read" event, although strictly speaking there is nothing to actually read).
<<< Previous | Home | Next >>> |
User-level or kernel-level driver? | Up | Examples |