970
技術社區[雲棲]
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