Sophie

Sophie

distrib > Mandriva > 9.0 > i586 > by-pkgid > 98e91bc877e03cf3582cd163550eb7e3 > files > 749

kernel-doc-html-2.4.19-16mdk.i586.rpm

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<HTML
><HEAD
><TITLE
>The printer driver</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><LINK
REL="HOME"
TITLE="The Linux 2.4 Parallel Port Subsystem"
HREF="book1.html"><LINK
REL="PREVIOUS"
TITLE="Port drivers"
HREF="c419.html"><LINK
REL="NEXT"
TITLE="User-level device drivers"
HREF="c587.html"></HEAD
><BODY
CLASS="CHAPTER"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>The Linux 2.4 Parallel Port Subsystem</TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="c419.html"
ACCESSKEY="P"
>&#60;&#60;&#60; Previous</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
></TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="c587.html"
ACCESSKEY="N"
>Next &#62;&#62;&#62;</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="CHAPTER"
><H1
><A
NAME="LP"
></A
>The printer driver</H1
><P
>   The printer driver, <TT
CLASS="LITERAL"
>lp</TT
> is a character special
   device driver and a <TT
CLASS="LITERAL"
>parport</TT
> client.  As a
   character special device driver it registers a <SPAN
CLASS="STRUCTNAME"
>struct
   file_operations</SPAN
> using
   <TT
CLASS="FUNCTION"
>register_chrdev</TT
>, with pointers filled in for
   <TT
CLASS="STRUCTFIELD"
><I
>write</I
></TT
>, <TT
CLASS="STRUCTFIELD"
><I
>ioctl</I
></TT
>,
   <TT
CLASS="STRUCTFIELD"
><I
>open</I
></TT
> and
   <TT
CLASS="STRUCTFIELD"
><I
>release</I
></TT
>.  As a client of
   <TT
CLASS="LITERAL"
>parport</TT
>, it registers a <SPAN
CLASS="STRUCTNAME"
>struct
   parport_driver</SPAN
> using
   <TT
CLASS="FUNCTION"
>parport_register_driver</TT
>, so that
   <TT
CLASS="LITERAL"
>parport</TT
> knows to call
   <TT
CLASS="FUNCTION"
>lp_attach</TT
> when a new parallel port is
   discovered (and <TT
CLASS="FUNCTION"
>lp_detach</TT
> when it goes
   away).
  </P
><P
>   The parallel port console functionality is also implemented in
   <TT
CLASS="FILENAME"
>drivers/char/lp.c</TT
>, but that won't be covered
   here (it's quite simple though).
  </P
><P
>   The initialisation of the driver is quite easy to understand (see
   <TT
CLASS="FUNCTION"
>lp_init</TT
>).  The <TT
CLASS="VARNAME"
>lp_table</TT
> is
   an array of structures that contain information about a specific
   device (the <SPAN
CLASS="STRUCTNAME"
>struct pardevice</SPAN
> associated
   with it, for example).  That array is initialised to sensible
   values first of all.
  </P
><P
>   Next, the printer driver calls <TT
CLASS="FUNCTION"
>register_chrdev</TT
>
   passing it a pointer to <TT
CLASS="VARNAME"
>lp_fops</TT
>, which contains
   function pointers for the printer driver's implementation of
   <TT
CLASS="FUNCTION"
>open</TT
>, <TT
CLASS="FUNCTION"
>write</TT
>, and so on.
   This part is the same as for any character special device
   driver.
  </P
><P
>   After successfully registering itself as a character special device
   driver, the printer driver registers itself as a
   <TT
CLASS="LITERAL"
>parport</TT
> client using
   <TT
CLASS="FUNCTION"
>parport_register_driver</TT
>.  It passes a pointer
   to this structure:
  </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><PRE
CLASS="PROGRAMLISTING"
>   
static struct parport_driver lp_driver = {
        "lp",
        lp_attach,
        lp_detach,
        NULL
};
   </PRE
></TD
></TR
></TABLE
><P
>   The <TT
CLASS="FUNCTION"
>lp_detach</TT
> function is not very interesting
   (it does nothing); the interesting bit is
   <TT
CLASS="FUNCTION"
>lp_attach</TT
>.  What goes on here depends on
   whether the user supplied any parameters.  The possibilities are:
   no parameters supplied, in which case the printer driver uses every
   port that is detected; the user supplied the parameter
   <SPAN
CLASS="QUOTE"
>"auto"</SPAN
>, in which case only ports on which the device
   ID string indicates a printer is present are used; or the user
   supplied a list of parallel port numbers to try, in which case only
   those are used.
  </P
><P
>   For each port that the printer driver wants to use (see
   <TT
CLASS="FUNCTION"
>lp_register</TT
>), it calls
   <TT
CLASS="FUNCTION"
>parport_register_device</TT
> and stores the
   resulting <SPAN
CLASS="STRUCTNAME"
>struct pardevice</SPAN
> pointer in the
   <TT
CLASS="VARNAME"
>lp_table</TT
>.  If the user told it to do so, it then
   resets the printer.
  </P
><P
>   The other interesting piece of the printer driver, from the point
   of view of <TT
CLASS="LITERAL"
>parport</TT
>, is
   <TT
CLASS="FUNCTION"
>lp_write</TT
>.  In this function, the user space
   process has data that it wants printed, and the printer driver
   hands it off to the <TT
CLASS="LITERAL"
>parport</TT
> code to deal with.
  </P
><P
>   The <TT
CLASS="LITERAL"
>parport</TT
> functions it uses that we have not
   seen yet are <TT
CLASS="FUNCTION"
>parport_negotiate</TT
>,
   <TT
CLASS="FUNCTION"
>parport_set_timeout</TT
>, and
   <TT
CLASS="FUNCTION"
>parport_write</TT
>.  These functions are part of
   the IEEE 1284 implementation.
  </P
><P
>   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.
  </P
><P
>   The particular transfer mode that the printer driver wants to use
   is named in IEEE 1284 as <SPAN
CLASS="QUOTE"
>"compatibility"</SPAN
> mode, and the
   function to request a particular mode is called
   <TT
CLASS="FUNCTION"
>parport_negotiate</TT
>.
  </P
><DIV
CLASS="FUNCSYNOPSIS"
><A
NAME="AEN506"
></A
><P
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><PRE
CLASS="FUNCSYNOPSISINFO"
>#include &#60;parport.h&#62;
   </PRE
></TD
></TR
></TABLE
><P
><CODE
><CODE
CLASS="FUNCDEF"
>int <TT
CLASS="FUNCTION"
>parport_negotiate</TT
></CODE
>(struct parport *<TT
CLASS="PARAMETER"
><I
>port</I
></TT
>, int <TT
CLASS="PARAMETER"
><I
>mode</I
></TT
>);</CODE
></P
><P
></P
></DIV
><P
>   The <TT
CLASS="PARAMETER"
><I
>modes</I
></TT
> parameter is a symbolic constant
   representing an IEEE 1284 mode; in this instance, it is
   <TT
CLASS="CONSTANT"
>IEEE1284_MODE_COMPAT</TT
>. (Compatibility mode is
   slightly different to the other modes---rather than being
   specifically requested, it is the default until another mode is
   selected.)
  </P
><P
>   Back to <TT
CLASS="FUNCTION"
>lp_write</TT
> then.  First, access to the
   parallel port is secured with
   <TT
CLASS="FUNCTION"
>parport_claim_or_block</TT
>.  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 <TT
CLASS="FUNCTION"
>parport_negotiate</TT
>.
  </P
><P
>   The main work is done in the write-loop.  In particular, the line
   that hands the data over to <TT
CLASS="LITERAL"
>parport</TT
> reads:
  </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><PRE
CLASS="PROGRAMLISTING"
>        written = parport_write (port, kbuf, copy_size);</PRE
></TD
></TR
></TABLE
><P
>   The <TT
CLASS="FUNCTION"
>parport_write</TT
> 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:
  </P
><DIV
CLASS="FUNCSYNOPSIS"
><A
NAME="AEN527"
></A
><P
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><PRE
CLASS="FUNCSYNOPSISINFO"
>#include &#60;parport.h&#62;
   </PRE
></TD
></TR
></TABLE
><P
><CODE
><CODE
CLASS="FUNCDEF"
>ssize_t <TT
CLASS="FUNCTION"
>parport_write</TT
></CODE
>(struct parport *<TT
CLASS="PARAMETER"
><I
>port</I
></TT
>, const void *<TT
CLASS="PARAMETER"
><I
>buf</I
></TT
>, size_t <TT
CLASS="PARAMETER"
><I
>len</I
></TT
>);</CODE
></P
><P
></P
></DIV
><DIV
CLASS="FUNCSYNOPSIS"
><A
NAME="AEN538"
></A
><P
></P
><P
><CODE
><CODE
CLASS="FUNCDEF"
>ssize_t <TT
CLASS="FUNCTION"
>parport_read</TT
></CODE
>(struct parport *<TT
CLASS="PARAMETER"
><I
>port</I
></TT
>, void *<TT
CLASS="PARAMETER"
><I
>buf</I
></TT
>, size_t <TT
CLASS="PARAMETER"
><I
>len</I
></TT
>);</CODE
></P
><P
></P
></DIV
><P
>   (<TT
CLASS="FUNCTION"
>parport_read</TT
> does what it sounds like, but
   only works for modes in which reverse transfer is possible.  Of
   course, <TT
CLASS="FUNCTION"
>parport_write</TT
> only works in modes in
   which forward transfer is possible, too.)
  </P
><P
>   The <TT
CLASS="PARAMETER"
><I
>buf</I
></TT
> pointer should be to kernel space
   memory, and obviously the <TT
CLASS="PARAMETER"
><I
>len</I
></TT
> parameter
   specifies the amount of data to transfer.
  </P
><P
>   In fact what <TT
CLASS="FUNCTION"
>parport_write</TT
> does is call the
   appropriate block transfer function from the <SPAN
CLASS="STRUCTNAME"
>struct
   parport_operations</SPAN
>:
  </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><PRE
CLASS="PROGRAMLISTING"
>   
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);
};
   </PRE
></TD
></TR
></TABLE
><P
>   The transfer code in <TT
CLASS="LITERAL"
>parport</TT
> will tolerate a
   data transfer stall only for so long, and this timeout can be
   specified with <TT
CLASS="FUNCTION"
>parport_set_timeout</TT
>, which
   returns the previous timeout:
  </P
><DIV
CLASS="FUNCSYNOPSIS"
><A
NAME="AEN561"
></A
><P
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><PRE
CLASS="FUNCSYNOPSISINFO"
>#include &#60;parport.h&#62;
   </PRE
></TD
></TR
></TABLE
><P
><CODE
><CODE
CLASS="FUNCDEF"
>long <TT
CLASS="FUNCTION"
>parport_set_timeout</TT
></CODE
>(struct pardevice *<TT
CLASS="PARAMETER"
><I
>dev</I
></TT
>, long <TT
CLASS="PARAMETER"
><I
>inactivity</I
></TT
>);</CODE
></P
><P
></P
></DIV
><P
>   This timeout is specific to the device, and is restored on
   <TT
CLASS="FUNCTION"
>parport_claim</TT
>.
  </P
><P
>   The next function to look at is the one that allows processes to
   read from <TT
CLASS="FILENAME"
>/dev/lp0</TT
>:
   <TT
CLASS="FUNCTION"
>lp_read</TT
>.  It's short, like
   <TT
CLASS="FUNCTION"
>lp_write</TT
>.
  </P
><P
>   The semantics of reading from a line printer device are as follows:
  </P
><P
></P
><UL
><LI
><P
>     Switch to reverse nibble mode.
    </P
></LI
><LI
><P
>     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.
    </P
></LI
><LI
><P
>     If there was data, stop, and return it.
    </P
></LI
><LI
><P
>     Otherwise, we tried to read data and there was none.  If the user
     opened the device node with the <TT
CLASS="CONSTANT"
>O_NONBLOCK</TT
>
     flag, return.  Otherwise wait until an interrupt occurs on the
     port (or a timeout elapses).
    </P
></LI
></UL
></DIV
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="c419.html"
ACCESSKEY="P"
>&#60;&#60;&#60; Previous</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="book1.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="c587.html"
ACCESSKEY="N"
>Next &#62;&#62;&#62;</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Port drivers</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
>&nbsp;</TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>User-level device drivers</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>