📄 parportbook.tmpl
字号:
</para> <para> For each port that the printer driver wants to use (see <function>lp_register</function>), it calls <function>parport_register_device</function> and stores the resulting <structname>struct pardevice</structname> pointer in the <varname>lp_table</varname>. If the user told it to do so, it then resets the printer. </para> <para> The other interesting piece of the printer driver, from the point of view of <literal>parport</literal>, is <function>lp_write</function>. In this function, the user space process has data that it wants printed, and the printer driver hands it off to the <literal>parport</literal> code to deal with. </para> <para> The <literal>parport</literal> functions it uses that we have not seen yet are <function>parport_negotiate</function>, <function>parport_set_timeout</function>, and <function>parport_write</function>. These functions are part of the IEEE 1284 implementation. </para> <para> The way the IEEE 1284 protocol works is that the host tells the peripheral what transfer mode it would like to use, and the peripheral either accepts that mode or rejects it; if the mode is rejected, the host can try again with a different mode. This is the negotation phase. Once the peripheral has accepted a particular transfer mode, data transfer can begin that mode. </para> <para> The particular transfer mode that the printer driver wants to use is named in IEEE 1284 as <quote>compatibility</quote> mode, and the function to request a particular mode is called <function>parport_negotiate</function>. </para> <funcsynopsis> <funcsynopsisinfo>#include <parport.h> </funcsynopsisinfo> <funcprototype> <funcdef>int <function>parport_negotiate</function></funcdef> <paramdef>struct parport *<parameter>port</parameter></paramdef> <paramdef>int <parameter>mode</parameter></paramdef> </funcprototype> </funcsynopsis> <para> The <parameter>modes</parameter> parameter is a symbolic constant representing an IEEE 1284 mode; in this instance, it is <constant>IEEE1284_MODE_COMPAT</constant>. (Compatibility mode is slightly different to the other modes---rather than being specifically requested, it is the default until another mode is selected.) </para> <para> Back to <function>lp_write</function> then. First, access to the parallel port is secured with <function>parport_claim_or_block</function>. At this point the driver might sleep, waiting for another driver (perhaps a Zip drive driver, for instance) to let the port go. Next, it goes to compatibility mode using <function>parport_negotiate</function>. </para> <para> The main work is done in the write-loop. In particular, the line that hands the data over to <literal>parport</literal> reads: </para><programlisting><![CDATA[ written = parport_write (port, kbuf, copy_size);]]></programlisting> <para> The <function>parport_write</function> function writes data to the peripheral using the currently selected transfer mode (compatibility mode, in this case). It returns the number of bytes successfully written: </para> <funcsynopsis> <funcsynopsisinfo>#include <parport.h> </funcsynopsisinfo> <funcprototype> <funcdef>ssize_t <function>parport_write</function></funcdef> <paramdef>struct parport *<parameter>port</parameter></paramdef> <paramdef>const void *<parameter>buf</parameter></paramdef> <paramdef>size_t <parameter>len</parameter></paramdef> </funcprototype> </funcsynopsis> <funcsynopsis> <funcprototype> <funcdef>ssize_t <function>parport_read</function></funcdef> <paramdef>struct parport *<parameter>port</parameter></paramdef> <paramdef>void *<parameter>buf</parameter></paramdef> <paramdef>size_t <parameter>len</parameter></paramdef> </funcprototype> </funcsynopsis> <para> (<function>parport_read</function> does what it sounds like, but only works for modes in which reverse transfer is possible. Of course, <function>parport_write</function> only works in modes in which forward transfer is possible, too.) </para> <para> The <parameter>buf</parameter> pointer should be to kernel space memory, and obviously the <parameter>len</parameter> parameter specifies the amount of data to transfer. </para> <para> In fact what <function>parport_write</function> does is call the appropriate block transfer function from the <structname>struct parport_operations</structname>: </para> <programlisting> <![CDATA[struct parport_operations { [...] /* Block read/write */ size_t (*epp_write_data) (struct parport *port, const void *buf, size_t len, int flags); size_t (*epp_read_data) (struct parport *port, void *buf, size_t len, int flags); size_t (*epp_write_addr) (struct parport *port, const void *buf, size_t len, int flags); size_t (*epp_read_addr) (struct parport *port, void *buf, size_t len, int flags); size_t (*ecp_write_data) (struct parport *port, const void *buf, size_t len, int flags); size_t (*ecp_read_data) (struct parport *port, void *buf, size_t len, int flags); size_t (*ecp_write_addr) (struct parport *port, const void *buf, size_t len, int flags); size_t (*compat_write_data) (struct parport *port, const void *buf, size_t len, int flags); size_t (*nibble_read_data) (struct parport *port, void *buf, size_t len, int flags); size_t (*byte_read_data) (struct parport *port, void *buf, size_t len, int flags);}; ]]></programlisting> <para> The transfer code in <literal>parport</literal> will tolerate a data transfer stall only for so long, and this timeout can be specified with <function>parport_set_timeout</function>, which returns the previous timeout: </para> <funcsynopsis> <funcsynopsisinfo>#include <parport.h> </funcsynopsisinfo> <funcprototype> <funcdef>long <function>parport_set_timeout</function></funcdef> <paramdef>struct pardevice *<parameter>dev</parameter></paramdef> <paramdef>long <parameter>inactivity</parameter></paramdef> </funcprototype> </funcsynopsis> <para> This timeout is specific to the device, and is restored on <function>parport_claim</function>. </para> <para> The next function to look at is the one that allows processes to read from <filename>/dev/lp0</filename>: <function>lp_read</function>. It's short, like <function>lp_write</function>. </para> <para> The semantics of reading from a line printer device are as follows: </para> <itemizedlist> <listitem> <para> Switch to reverse nibble mode. </para> </listitem> <listitem> <para> Try to read data from the peripheral using reverse nibble mode, until either the user-provided buffer is full or the peripheral indicates that there is no more data. </para> </listitem> <listitem> <para> If there was data, stop, and return it. </para> </listitem> <listitem> <para> Otherwise, we tried to read data and there was none. If the user opened the device node with the <constant>O_NONBLOCK</constant> flag, return. Otherwise wait until an interrupt occurs on the port (or a timeout elapses). </para> </listitem> </itemizedlist> </chapter> <chapter id="ppdev"> <title>User-level device drivers</title> <!-- ppdev --> <sect1> <title>Introduction to ppdev</title> <para> The printer is accessible through <filename>/dev/lp0</filename>; in the same way, the parallel port itself is accessible through <filename>/dev/parport0</filename>. The difference is in the level of control that you have over the wires in the parallel port cable. </para> <para> With the printer driver, a user-space program (such as the printer spooler) can send bytes in <quote>printer protocol</quote>. Briefly, this means that for each byte, the eight data lines are set up, then a <quote>strobe</quote> line tells the printer to look at the data lines, and the printer sets an <quote>acknowledgement</quote> line to say that it got the byte. The printer driver also allows the user-space program to read bytes in <quote>nibble mode</quote>, 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). </para> <para> In contrast, the <literal>ppdev</literal> driver (accessed via <filename>/dev/parport0</filename>) allows you to: </para> <itemizedlist spacing=compact> <listitem> <para> examine status lines, </para> </listitem> <listitem> <para> set control lines, </para> </listitem> <listitem> <para> set/examine data lines (and control the direction of the data lines), </para> </listitem> <listitem> <para> wait for an interrupt (triggered by one of the status lines), </para> </listitem> <listitem> <para> find out how many new interrupts have occurred, </para> </listitem> <listitem> <para> set up a response to an interrupt, </para> </listitem> <listitem> <para> use IEEE 1284 negotiation (for telling peripheral which transfer mode, to use) </para> </listitem> <listitem> <para> transfer data using a specified IEEE 1284 mode. </para> </listitem> </itemizedlist> </sect1> <sect1> <title>User-level or kernel-level driver?</title> <para> The decision of whether to choose to write a kernel-level device driver or a user-level device driver depends on several factors. One of the main ones from a practical point of view is speed: kernel-level device drivers get to run faster because they are not preemptable, unlike user-level applications. </para> <para> Another factor is ease of development. It is in general easier to write a user-level driver because (a) one wrong move does not result in a crashed machine, (b) you have access to user libraries (such as the C library), and (c) debugging is easier. </para> </sect1> <sect1> <title>Programming interface</title> <para> The <literal>ppdev</literal> interface is largely the same as that of other character special devices, in that it supports <function>open</function>, <function>close</function>, <function>read</function>, <function>write</function>, and <function>ioctl</function>. The constants for the <function>ioctl</function> commands are in <filename>include/linux/ppdev.h</filename>. </para> <sect2> <title> Starting and stopping: <function>open</function> and <function>close</function> </title> <para> The device node <filename>/dev/parport0</filename> represents any device that is connected to <filename>parport0</filename>, 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -