linux下并口相关(x86)
一 用户空间操作并口
linux的x86平台下在用户空间操作并口有两种方式,要么写成驱动,用户应用程序通过IO设备模块实现对并口读写,还有就是直接在用户空间I/O内存地址
PC25针并口. 接口定义如下:
针 |
方向 |
2,3,4,5,6,7,8,9 |
双向 |
1,14,16,17 |
输出 |
10,11,12,13,15 |
输入 |
18,19,20,21,22,23,24,25 |
地 |
并口编程基础
这些引脚,其实就是I/O口。而且是可以控制的,以软件的方式,非常简单。
在计算机内部,对应的寄存器如下:
0x378是8bit数据寄存器的地址,对应并口针的顺序是9,8,7,6,5,4,3,2
0x379是状态寄存器,11,10,12,13,15 - - -
0x37A是控制寄存器,- - - 并口中断允许位,17,16,14,1
也就是说PC的0x378可以用来做输入和输出,0x37A的低4位可以用来输出,0x379是输入。这样就可以用并口模拟很多时许,比如IIC、JTAG、SPI等等。
例子:用户空间直接I/O 0x378地址的方法如下
- #include <stdio.h>
- #include <sys/io.h>
- int main()
- {
- /*向系统申请0x000~0x3FF的地址空间控制权*/
- ioperm(0x000,0x3FF,1);
- /*向0x378地址写入全1*/
- outb(0xff,0x378);
- /*向系统交回0x000~0x3FF的地址空间控制权*/
- ioperm(0x000,0x3FF,0);
- return 0;
- }
二 Linux Parport programming
Hardware view of the parport
Pin | Function | Dir |
---|---|---|
1 | /STROBE | W |
2..9 | DATA Bit0..7 | R/W |
10 | ACK | R |
11 | /BUSY | R |
12 | PAPEROUT | R |
13 | SELECT | R |
14 | /AUTOFD | W |
15 | ERROR | R |
16 | INIT | W |
17 | /SELECT | W |
18..25 | GND | - |
Access via raw IO (not recommended)
This is ONLY for ISA-Bus based 8255-compatible devices.
Unless you are developing a device driver in kernel space, do NOT use it.
Access via /dev/lpX
A huge advantage is also, that this (in contrary of the raw i/o method) will also work with USB-to-Parport converters.
Full access via /dev/parportX
Prefer this method if you are going to read and write data to any homebrew electronics (because here, e.g. the STOBE line is NOT set automatically and the driver also does NOT wait for an ACK pulse).
List of ioctl()s for /dev/parportX access
Setting direction for Databits
三 Writing a parport Device Driver
What parport does
One purpose of parport is to allow multiple device drivers to use the same parallel port. It does this by sitting in-between the port hardware and the parallel port device drivers. When a driver wants to talk to its parallel port device, it calls a function to "claim" the port, and "releases" the port when it is done.
Another thing that parport does is provide a layer of abstraction from the hardware, so that device drivers can be architecture-independent in that they don't need to know which style of parallel port they are using (those currently supported are PC-style, Archimedes, and Sun Ultra/AX architecture).
Interface to parport
Finding a port
To obtain a pointer to a linked list of parport structures, use the parport_enumerate function. This returns a pointer to a struct parport, in which the member next points to the next one in the list, or is NULL at the end of the list.
This structure looks like (from linux/include/linux/parport.h):
/* A parallel port */ struct parport { unsigned long base; /* base address */ unsigned int size; /* IO extent */ char *name; int irq; /* interrupt (or -1 for none) */ int dma; unsigned int modes; struct pardevice *devices; struct pardevice *cad; /* port owner */ struct pardevice *lurker; struct parport *next; unsigned int flags; struct parport_dir pdir; struct parport_device_info probe_info; struct parport_operations *ops; void *private_data; /* for lowlevel driver */ };
Device registration
The next thing to do is to register a device on each port that you want to use. This is done with the parport_register_device function, which returns a pointer to astruct pardevice, which you will need in order to use the port.
This structure looks like (again, from linux/include/linux/parport.h):
/* A parallel port device */ struct pardevice { char *name; struct parport *port; int (*preempt)(void *); void (*wakeup)(void *); void *private; void (*irq_func)(int, void *, struct pt_regs *); int flags; struct pardevice *next; struct pardevice *prev; struct parport_state *state; /* saved status over preemption */ };
There are two types of driver that can be registered: "transient" and "lurking". A lurking driver is one that wants to have the port whenever no-one else has it. PLIP is an example of this. A transient driver is one that only needs to use the parallel port occasionally, and for short periods of time (the printer driver and Zip driver are good examples).
Claiming the port
To claim the port, use parport_claim, passing it a pointer to the struct pardevice obtained at device registration. If parport_claim returns zero, the port is yours, otherwise you will have to try again later.
A good way of doing this is to register a "wakeup" function: when a device driver releases the port, other device drivers that are registered on that port have their "wakeup" functions called, and the first one to claim the port gets it. If the parport claim fails, you can go to sleep; when the parport is free again, your wakeup function can wake you up again. For example, declare a global wait queue for each possible port that a device could be on:
static struct wait_queue * wait_q[MAX_MY_DEVICES];
The wakeup function looks like:
void my_wakeup (void * my_stuff) { /* this is our chance to grab the parport */ struct wait_queue ** wait_q_pointer = (struct wait_queue **) my_stuff; if (!waitqueue_active (wait_q_pointer)) return; /* parport has messed up if we get here */ /* claim the parport */ if (parport_claim (wait_q_pointer))) return; /* Shouldn't happen */ wake_up(wait_q_pointer); }
Then, in the initialisation code, do something like:
struct pardevice * pd[MAX_MY_DEVICES]; int my_driver_init (void) { struct parport * pp = parport_enumerate (); int count = 0; while (pp) { /* for each port */ /* set up the wait queue */ init_waitqueue (&wait_q[count]); /* register a device */ pd[count] = parport_register_device (pp, "Me", /* preemption function */ my_preempt, /* wakeup function */ my_wakeup, /* interrupt function */ my_interrupt, /* this driver is transient */ PARPORT_DEV_TRAN, /* private data */ &wait_q[count]); /* try initialising the device */ if (init_my_device (count) == ERROR) /* failed, so unregister */ parport_unregister_device (pd[count]); else if (++count == MAX_MY_DEVICES) /* can't handle any more devices */ break; }
Then a typical thing to do to obtain access to the port would be:
if (parport_claim (pd[n])) /* someone else had it */ sleep_on (&wait_q[n]); /* will wake up when wakeup */ /* function called */ /* (do stuff with the port here) */ /* finished with the port now */ parport_release (pd[n]);
Using the port
Operations on the parallel port can be carried out using functions provided by the parport interface:
struct parport_operations { void (*write_data)(struct parport *, unsigned int); unsigned int (*read_data)(struct parport *); void (*write_control)(struct parport *, unsigned int); unsigned int (*read_control)(struct parport *); unsigned int (*frob_control)(struct parport *, unsigned int mask, unsigned int val); void (*write_econtrol)(struct parport *, unsigned int); unsigned int (*read_econtrol)(struct parport *); unsigned int (*frob_econtrol)(struct parport *, unsigned int mask, unsigned int val); void (*write_status)(struct parport *, unsigned int); unsigned int (*read_status)(struct parport *); void (*write_fifo)(struct parport *, unsigned int); unsigned int (*read_fifo)(struct parport *); void (*change_mode)(struct parport *, int); void (*release_resources)(struct parport *); int (*claim_resources)(struct parport *); unsigned int (*epp_write_block)(struct parport *, void *, unsigned int); unsigned int (*epp_read_block)(struct parport *, void *, unsigned int); unsigned int (*ecp_write_block)(struct parport *, void *, unsigned int, void (*fn)(struct parport *, void *, unsigned int), void *); unsigned int (*ecp_read_block)(struct parport *, void *, unsigned int, void (*fn)(struct parport *, void *, unsigned int), void *); void (*save_state)(struct parport *, struct parport_state *); void (*restore_state)(struct parport *, struct parport_state *); void (*enable_irq)(struct parport *); void (*disable_irq)(struct parport *); int (*examine_irq)(struct parport *); void (*inc_use_count)(void); void (*dec_use_count)(void); };
However, for generic operations, the following macros should be used (architecture-specific parport implementations may redefine them to avoid function call overheads):
/* Generic operations vector through the dispatch table. */ #define parport_write_data(p,x) (p)->ops->write_data(p,x) #define parport_read_data(p) (p)->ops->read_data(p) #define parport_write_control(p,x) (p)->ops->write_control(p,x) #define parport_read_control(p) (p)->ops->read_control(p) #define parport_frob_control(p,m,v) (p)->ops->frob_control(p,m,v) #define parport_write_econtrol(p,x) (p)->ops->write_econtrol(p,x) #define parport_read_econtrol(p) (p)->ops->read_econtrol(p) #define parport_frob_econtrol(p,m,v) (p)->ops->frob_econtrol(p,m,v) #define parport_write_status(p,v) (p)->ops->write_status(p,v) #define parport_read_status(p) (p)->ops->read_status(p) #define parport_write_fifo(p,v) (p)->ops->write_fifo(p,v) #define parport_read_fifo(p) (p)->ops->read_fifo(p) #define parport_change_mode(p,m) (p)->ops->change_mode(p,m) #define parport_release_resources(p) (p)->ops->release_resources(p) #define parport_claim_resources(p) (p)->ops->claim_resources(p)
Releasing the port
When you have finished the sequence of operations on the port that you wanted to do, use release_parport to let any other devices that there may be have a go.
Unregistering the device
If you decide that you don't want to use the port after all (perhaps the device that you wanted to talk to isn't there), use parport_unregister_device.
Something to bear in mind: interrupts
Parallel port devices cannot share interrupts. The parport code shares a parallel port among different devices by means of scheduling - only one device has access to the port at any one time. If a device (a printer, say) is going to generate an interrupt, it could do it when some other driver (like the Zip driver) has the port rather than the printer driver. That would lead to the interrupt being missed altogether. For this reason, drivers should poll their devices unless there are no other drivers using that port. To see how to do this, you might like to take a look at the printer driver.
四 linux/Documentation/parport.txt
最后更新:2017-04-03 20:19:09