📄 parportbook.tmpl
字号:
parport_operations</structname>, which is a list of function pointers for the various operations that can be performed on a port. You can think of a <structname>struct parport</structname> as a parallel port <quote>object</quote>, if <quote>object-orientated</quote> programming is your thing. The <structname>parport</structname> structures are chained in a linked list, whose head is <varname>portlist</varname> (in <filename>drivers/parport/share.c</filename>). </para> <para> Once the port has been registered, the low-level port driver announces it. The <function>parport_announce_port</function> function walks down the list of parallel port device drivers (<structname>struct parport_driver</structname>s) calling the <function>attach</function> function of each (which may block). </para> <para> Similarly, a low-level port driver can undo the effect of registering a port with the <function>parport_unregister_port</function> function, and device drivers are notified using the <function>detach</function> callback (which may not block). </para> <para> Device drivers can undo the effect of registering themselves with the <function>parport_unregister_driver</function> function. </para> </sect1> <!-- IEEE 1284.3 API --> <sect1> <title>The IEEE 1284.3 API</title> <para> The ability to daisy-chain devices is very useful, but if every device does it in a different way it could lead to lots of complications for device driver writers. Fortunately, the IEEE are standardising it in IEEE 1284.3, which covers daisy-chain devices and port multiplexors. </para> <para> At the time of writing, IEEE 1284.3 has not been published, but the draft specifies the on-the-wire protocol for daisy-chaining and multiplexing, and also suggests a programming interface for using it. That interface (or most of it) has been implemented in the <literal>parport</literal> code in Linux. </para> <para> At initialisation of the parallel port <quote>bus</quote>, daisy-chained devices are assigned addresses starting from zero. There can only be four devices with daisy-chain addresses, plus one device on the end that doesn't know about daisy-chaining and thinks it's connected directly to a computer. </para> <para> Another way of connecting more parallel port devices is to use a multiplexor. The idea is to have a device that is connected directly to a parallel port on a computer, but has a number of parallel ports on the other side for other peripherals to connect to (two or four ports are allowed). The multiplexor switches control to different ports under software control---it is, in effect, a programmable printer switch. </para> <para> Combining the ability of daisy-chaining five devices together with the ability to multiplex one parallel port between four gives the potential to have twenty peripherals connected to the same parallel port! </para> <para> In addition, of course, a single computer can have multiple parallel ports. So, each parallel port peripheral in the system can be identified with three numbers, or co-ordinates: the parallel port, the multiplexed port, and the daisy-chain address. </para> <mediaobject> <imageobject> <imagedata fileref="parport-multi" format="eps"> </imageobject> <imageobject> <imagedata fileref="parport-multi.png" format="png"> </imageobject> </mediaobject> <para> Each device in the system is numbered at initialisation (by <function>parport_daisy_init</function>). You can convert between this device number and its co-ordinates with <function>parport_device_num</function> and <function>parport_device_coords</function>. </para> <funcsynopsis> <funcsynopsisinfo>#include <parport.h> </funcsynopsisinfo> <funcprototype> <funcdef>int <function>parport_device_num</function></funcdef> <paramdef>int <parameter>parport</parameter></paramdef> <paramdef>int <parameter>mux</parameter></paramdef> <paramdef>int <parameter>daisy</parameter></paramdef> </funcprototype> </funcsynopsis> <funcsynopsis> <funcprototype> <funcdef>int <function>parport_device_coords</function></funcdef> <paramdef>int <parameter>devnum</parameter></paramdef> <paramdef>int *<parameter>parport</parameter></paramdef> <paramdef>int *<parameter>mux</parameter></paramdef> <paramdef>int *<parameter>daisy</parameter></paramdef> </funcprototype> </funcsynopsis> <para> Any parallel port peripheral will be connected directly or indirectly to a parallel port on the system, but it won't have a daisy-chain address if it does not know about daisy-chaining, and it won't be connected through a multiplexor port if there is no multiplexor. The special co-ordinate value <constant>-1</constant> is used to indicate these cases. </para> <para> Two functions are provided for finding devices based on their IEEE 1284 Device ID: <function>parport_find_device</function> and <function>parport_find_class</function>. </para> <funcsynopsis> <funcsynopsisinfo>#include <parport.h> </funcsynopsisinfo> <funcprototype> <funcdef>int <function>parport_find_device</function></funcdef> <paramdef>const char *<parameter>mfg</parameter></paramdef> <paramdef>const char *<parameter>mdl</parameter></paramdef> <paramdef>int <parameter>from</parameter></paramdef> </funcprototype> </funcsynopsis> <funcsynopsis> <funcprototype> <funcdef>int <function>parport_find_class</function></funcdef> <paramdef>parport_device_class <parameter>cls</parameter></paramdef> <paramdef>int <parameter>from</parameter></paramdef> </funcprototype> </funcsynopsis> <para> These functions take a device number (in addition to some other things), and return another device number. They walk through the list of detected devices until they find one that matches the requirements, and then return that device number (or <constant>-1</constant> if there are no more such devices). They start their search at the device after the one in the list with the number given (at <parameter>from</parameter>+1, in other words). </para> </sect1> </chapter> <chapter id="drivers"> <title>Device driver's view</title><!-- Cover: - sharing interface, preemption, interrupts, wakeups... - IEEE 1284.3 interface - port operations - why can read data but ctr is faked, etc. --><!-- I should take a look at the kernel hackers' guide bit I wrote, --><!-- as that deals with a lot of this. The main complaint with it --><!-- was that there weren't enough examples, but 'The printer --><!-- driver' should deal with that later; might be worth mentioning --><!-- in the text. --> <para> This section is written from the point of view of the device driver programmer, who might be writing a driver for a printer or a scanner or else anything that plugs into the parallel port. It explains how to use the <literal>parport</literal> interface to find parallel ports, use them, and share them with other device drivers. </para> <para> We'll start out with a description of the various functions that can be called, and then look at a reasonably simple example of their use: the printer driver. </para> <para> The interactions between the device driver and the <literal>parport</literal> layer are as follows. First, the device driver registers its existence with <literal>parport</literal>, in order to get told about any parallel ports that have been (or will be) detected. When it gets told about a parallel port, it then tells <literal>parport</literal> that it wants to drive a device on that port. Thereafter it can claim exclusive access to the port in order to talk to its device. </para> <para> So, the first thing for the device driver to do is tell <literal>parport</literal> that it wants to know what parallel ports are on the system. To do this, it uses the <function>parport_register_device</function> function: </para> <funcsynopsis> <funcsynopsisinfo>#include <parport.h>struct parport_driver { const char *name; void (*attach) (struct parport *); void (*detach) (struct parport *); struct parport_driver *next;}; </funcsynopsisinfo> <funcprototype> <funcdef>int <function>parport_register_driver</function></funcdef> <paramdef>struct parport_driver *<parameter>driver</parameter></paramdef> </funcprototype> </funcsynopsis> <para> In other words, the device driver passes pointers to a couple of functions to <literal>parport</literal>, and <literal>parport</literal> calls <function>attach</function> for each port that's detected (and <function>detach</function> for each port that disappears---yes, this can happen). </para> <para> The next thing that happens is that the device driver tells <literal>parport</literal> that it thinks there's a device on the port that it can drive. This typically will happen in the driver's <function>attach</function> function, and is done with <function>parport_register_device</function>: </para> <funcsynopsis> <funcsynopsisinfo>#include <parport.h> </funcsynopsisinfo> <funcprototype> <funcdef>struct pardevice *<function>parport_register_device</function></funcdef> <paramdef>struct parport *<parameter>port</parameter></paramdef> <paramdef>const char *<parameter>name</parameter></paramdef> <paramdef>int <parameter>(*pf)</parameter> <funcparams>void *</funcparams></paramdef> <paramdef>void <parameter>(*kf)</parameter> <funcparams>void *</funcparams></paramdef> <paramdef>void <parameter>(*irq_func)</parameter> <funcparams>int, void *, struct pt_regs *</funcparams></paramdef> <paramdef>int <parameter>flags</parameter></paramdef> <paramdef>void *<parameter>handle</parameter></paramdef> </funcprototype> </funcsynopsis> <para> The <parameter>port</parameter> comes from the parameter supplied to the <function>attach</function> function when it is called, or alternatively can be found from the list of detected parallel ports directly with the (now deprecated) <function>parport_enumerate</function> function. A better way of doing this is with <function>parport_find_number</function> or <function>parport_find_base</function> functions, which find ports by number and by base I/O address respectively. </para> <funcsynopsis> <funcsynopsisinfo>#include <parport.h> </funcsynopsisinfo> <funcprototype> <funcdef>struct parport *<function>parport_find_number</function></funcdef> <paramdef>int <parameter>number</parameter></paramdef> </funcprototype> </funcsynopsis> <funcsynopsis> <funcsynopsisinfo>#include <parport.h> </funcsynopsisinfo> <funcprototype> <funcdef>struct parport *<function>parport_find_base</function></funcdef> <paramdef>unsigned long <parameter>base</parameter></paramdef> </funcprototype> </funcsynopsis> <para> The next three parameters, <parameter>pf</parameter>, <parameter>kf</parameter>, and <parameter>irq_func</parameter>, are more function pointers. These callback functions get called under various circumstances, and are always given the <parameter>handle</parameter> as one of their parameters. </para> <para> The preemption callback, <parameter>pf</parameter>, is called when the driver has claimed access to the port but another device driver wants access. If the driver is willing to let the port go, it should return zero and the port will be released on its behalf. There is no need to call <function>parport_release</function>. If <parameter>pf</parameter> gets called at a bad time for letting the port go, it should return non-zero and no action will be taken. It is good manners for the driver to try to release the port at the earliest opportunity after its preemption callback is called. </para> <para> The <quote>kick</quote> callback, <parameter>kf</parameter>, is called when the port can be claimed for exclusive access; that is, <function>parport_claim</function> is guaranteed to succeed inside the <quote>kick</quote> callback. If the driver wants to claim the port it should do so; otherwise, it need not take any action. </para> <para> The <parameter>irq_func</parameter> callback is called, predictably, when a parallel port interrupt is generated. But it is not the only code that hooks on the interrupt. The sequence is this: the lowlevel driver is the one that has done <function>request_irq</function>; it then does whatever hardware-specific things it needs to do to the parallel port hardware (for PC-style ports, there is nothing special to do); it then tells the IEEE 1284 code about the interrupt, which may involve reacting to an IEEE 1284 event, depending on the current IEEE 1284 phase; and finally the <parameter>irq_func</parameter> function is called. </para> <para> None of the callback functions are allowed to block. </para> <para> The <parameter>flags</parameter> are for telling <literal>parport</literal> any requirements or hints that are useful. The only useful value here (other than <constant>0</constant>, which is the usual value) is <constant>PARPORT_DEV_EXCL</constant>. The point of that flag is to request exclusive access at all times---once a driver has successfully called <function>parport_register_device</function> with that flag, no other device drivers will be able to register devices on that port (until the successful driver deregisters its device, of course). </para> <para> The <constant>PARPORT_DEV_EXCL</constant> flag is for preventing
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -