parport 的一个目的是允许多个设备驱动程序使用同一个并行端口。 它通过位于端口硬件和并行端口设备驱动程序之间来实现这一点。 当驱动程序想要与其并行端口设备通信时,它会调用一个函数来“声明”(claim)端口,并在完成操作后“释放”(release)端口。
另一个parport的作用是从硬件提供一个抽象层,以便设备驱动程序可以实现架构独立性,即它们不需要知道它们正在使用哪种类型的并行端口(目前支持的类型包括 PC 风格、Archimedes 和 Sun Ultra/AX 架构)。
要获取指向 parport 结构体链表的指针,请使用parport_enumerate函数。 这会返回一个指向struct parport的指针,其中成员next指向列表中的下一个,或者为NULL在列表的末尾。
此结构体看起来像这样(来自 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 */ };
接下来要做的是在您想要使用的每个端口上注册一个设备。 这可以通过parport_register_device函数来完成,该函数返回一个指向struct pardevice的指针,您需要它才能使用该端口。
此结构体看起来像这样(同样来自 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 */ };
可以注册两种类型的驱动程序:“瞬态”(transient)和“潜伏”(lurking)。 “潜伏”驱动程序是指当没有其他驱动程序占用端口时,它希望拥有该端口的驱动程序。 PLIP 就是一个例子。 “瞬态”驱动程序是指只需要偶尔且在短时间内使用并行端口的驱动程序(打印机驱动程序和 Zip 驱动程序是很好的例子)。
要声明端口,请使用parport_claim,并传递给它一个指向struct pardevice在设备注册时获得的指针。 如果parport_claim返回零,则表示您拥有该端口,否则您将不得不稍后重试。
一个好的方法是注册一个“唤醒”(wakeup)函数:当设备驱动程序释放端口时,在该端口上注册的其他设备驱动程序会调用它们的“唤醒”函数,并且第一个声明端口的驱动程序将获得它。 如果 parport 声明失败,您可以进入睡眠状态; 当 parport 再次空闲时,您的唤醒函数可以再次唤醒您。 例如,为设备可能位于的每个可能的端口声明一个全局等待队列
static struct wait_queue * wait_q[MAX_MY_DEVICES];
唤醒函数看起来像这样
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); }
然后,在初始化代码中,执行类似这样的操作
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; }
然后,获得端口访问权限的典型做法是
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]);
可以使用 parport 接口提供的函数来执行并行端口上的操作
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); };
但是,对于通用操作,应使用以下宏(特定于架构的 parport 实现可能会重新定义它们以避免函数调用开销)
/* 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)
当您完成要在端口上执行的操作序列后,请使用release_parport以允许可能存在的任何其他设备尝试使用。
如果您决定最终不想使用该端口(也许您想与之通信的设备不存在),请使用parport_unregister_device.
并行端口设备无法共享中断。 parport 代码通过调度在不同的设备之间共享一个并行端口 - 任何时候只有一个设备可以访问该端口。 如果一个设备(例如打印机)要生成中断,则它可能在其他驱动程序(如 Zip 驱动程序)拥有端口而不是打印机驱动程序时执行此操作。 这将导致完全错过中断。 因此,除非没有其他驱动程序使用该端口,否则驱动程序应轮询其设备。 要了解如何执行此操作,您可以查看打印机驱动程序。