分享
 
 
 

Linux下的并口编程

王朝system·作者佚名  2006-01-10
窄屏简体版  字體: |||超大  

The printer is accessible through /dev/lp0;

in the same way, the parallel port itself is accessible through

/dev/parport0. The difference is in the

level of control that you have over the wires in the parallel port

cable. With the printer driver, a user-space program (such as the printer

spooler) can send bytes in "printer protocol".

Briefly, this means that for each byte, the eight data lines are

set up, then a "strobe" line tells the printer to

look at the data lines, and the printer sets an

"acknowledgement" line to say that it got the byte.

The printer driver also allows the user-space program to read

bytes in "nibble mode", which is a way of

transferring data from the peripheral to the computer half a byte

at a time (and so it's quite slow).

In contrast, the ppdev driver (accessed via

/dev/parport0) allows you to:

examine status lines,

set control lines,

set/examine data lines (and control the direction of the data

lines),

wait for an interrupt (triggered by one of the status lines),

find out how many new interrupts have occurred,

set up a response to an interrupt,

use IEEE 1284 negotiation (for telling peripheral which transfer

mode, to use)

transfer data using a specified IEEE 1284 mode.

Programming interface 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.

Starting and stopping: open and close 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.

Control: ioctl 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.

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

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

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

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

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

PPSETMODESets which IEEE 1284 protocol to use for the

read and write

calls.

The ioctl parameter should be a pointer

to an int.

PPGETMODERetrieves the current IEEE 1284 mode to use for

read and write.

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

PPSETTIMESets the time-out. The ioctl parameter

should be a pointer to a struct

timeval.

PPGETMODESRetrieves the capabilities of the hardware (i.e. the

modes field of the

parport structure).

PPSETFLAGSSets flags on the ppdev device which can

affect future I/O operations. Available flags are:

PP_FASTWRITE

PP_FASTREAD

PP_W91284PIC

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

PPRCONTROLReturns 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

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

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

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

PPWDATASets the data lines (if in forward mode). The

ioctl parameter is a pointer to an

unsigned char.

PPRDATAReads the data lines (if in reverse mode). The

ioctl parameter is a pointer to an

unsigned char.

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

PPWCTLONIRQSet 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: read and write 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.

Waiting for events: poll and select 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).

Examples Presented here are two demonstrations of how to write a simple

printer driver for ppdev. Firstly we will

use the write function, and after that we

will drive the control and data lines directly.

The first thing to do is to actually open the device.

int drive_printer (const char *name)

{

int fd;

int mode; /* We'll need this later. */

fd = open (name, O_RDWR);

if (fd == -1) {

perror ("open");

return 1;

}

Here name should be something along the lines

of "/dev/parport0". (If you don't have any

/dev/parport files, you can make them with

mknod; they are character special device nodes

with major 99.)

In order to do anything with the port we need to claim access to

it.

if (ioctl (fd, PPCLAIM)) {

perror ("PPCLAIM");

close (fd);

return 1;

}

Our printer driver will copy its input (from

stdin) to the printer, and it can do that it

one of two ways. The first way is to hand it all off to the

kernel driver, with the knowledge that the protocol that the

printer speaks is IEEE 1284's "compatibility"

mode.

/* Switch to compatibility mode. (In fact we don't need

* to do this, since we start off in compatibility mode

* anyway, but this demonstrates PPNEGOT.)

mode = IEEE1284_MODE_COMPAT;

if (ioctl (fd, PPNEGOT, &mode)) {

perror ("PPNEGOT");

close (fd);

return 1;

}

for (;;) {

char buffer[1000];

char *ptr = buffer;

size_t got;

got = read (0 /* stdin */, buffer, 1000);

if (got < 0) {

perror ("read");

close (fd);

return 1;

}

if (got == 0)

/* End of input */

break;

while (got > 0) {

int written = write_printer (fd, ptr, got);

if (written < 0) {

perror ("write");

close (fd);

return 1;

}

ptr += written;

got -= written;

}

}

The write_printer function is not pictured

above. This is because the main loop that is shown can be used

for both methods of driving the printer. Here is one

implementation of write_printer:

ssize_t write_printer (int fd, const void *ptr, size_t count)

{

return write (fd, ptr, count);

}

We hand the data to the kernel-level driver (using

write) and it handles the printer

protocol.

Now let's do it the hard way! In this particular example there is

no practical reason to do anything other than just call

write, because we know that the printer talks

an IEEE 1284 protocol. On the other hand, this particular example

does not even need a user-land driver since there is already a

kernel-level one; for the purpose of this discussion, try to

imagine that the printer speaks a protocol that is not already

implemented under Linux.

So, here is the alternative implementation of

write_printer (for brevity, error checking

has been omitted):

ssize_t write_printer (int fd, const void *ptr, size_t count)

{

ssize_t wrote = 0;

while (wrote < count) {

unsigned char status, control, data;

unsigned char mask = (PARPORT_STATUS_ERROR

| PARPORT_STATUS_BUSY);

unsigned char val = (PARPORT_STATUS_ERROR

| PARPORT_STATUS_BUSY);

struct ppdev_frob_struct frob;

struct timespec ts;

/* Wait for printer to be ready */

for (;;) {

ioctl (fd, PPRSTATUS, &status);

if ((status & mask) == val)

break;

ioctl (fd, PPRELEASE);

sleep (1);

ioctl (fd, PPCLAIM);

}

/* Set the data lines */

data = * ((char *) ptr)++;

ioctl (fd, PPWDATA, &data);

/* Delay for a bit */

ts.tv_sec = 0;

ts.tv_nsec = 1000;

nanosleep (&ts, NULL);

/* Pulse strobe */

frob.mask = PARPORT_CONTROL_STROBE;

frob.val = PARPORT_CONTROL_STROBE;

ioctl (fd, PPFCONTROL, &frob);

nanosleep (&ts, NULL);

/* End the pulse */

frob.val = 0;

ioctl (fd, PPFCONTROL, &frob);

nanosleep (&ts, NULL);

wrote++;

}

return wrote;

}

To show a bit more of the ppdev interface,

here is a small piece of code that is intended to mimic the

printer's side of printer protocol.

for (;;)

{

int irqc;

int busy = nAck | nFault;

int acking = nFault;

int ready = Busy | nAck | nFault;

char ch;

/* Set up the control lines when an interrupt happens. */

ioctl (fd, PPWCTLONIRQ, &busy);

/* Now we're ready. */

ioctl (fd, PPWCONTROL, &ready);

/* Wait for an interrupt. */

{

fd_set rfds;

FD_ZERO (&rfds);

FD_SET (fd, &rfds);

if (!select (fd + 1, &rfds, NULL, NULL, NULL))

/* Caught a signal? */

continue;

}

/* We are now marked as busy. */

/* Fetch the data. */

ioctl (fd, PPRDATA, &ch);

/* Clear the interrupt. */

ioctl (fd, PPCLRIRQ, &irqc);

if (irqc > 1)

fprintf (stderr, "Arghh! Missed %d interrupt%s!\n",

irqc - 1, irqc == 2 ? "s" : "");

/* Ack it. */

ioctl (fd, PPWCONTROL, &acking);

usleep (2);

ioctl (fd, PPWCONTROL, &busy);

putchar (ch);

}

And here is an example (with no error checking at all) to show how

to read data from the port, using ECP mode, with optional

negotiation to ECP mode first.

{

int fd, mode;

fd = open ("/dev/parport0", O_RDONLY | O_NOCTTY);

ioctl (fd, PPCLAIM);

mode = IEEE1284_MODE_ECP;

if (negotiate_first) {

ioctl (fd, PPNEGOT, &mode);

/* no need for PPSETMODE */

} else {

ioctl (fd, PPSETMODE, &mode);

}

/* Now do whatever we want with fd */

close (0);

dup2 (fd, 0);

if (!fork()) {

/* child */

execlp ("cat", "cat", NULL);

exit (1);

} else {

/* parent */

wait (NULL);

}

/* Okay, finished */

ioctl (fd, PPRELEASE);

close (fd);

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有