⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch57.htm

📁 linux-unix130.linux.and.unix.ebooks130 linux and unix ebookslinuxLearning Linux - Collection of 12 E
💻 HTM
📖 第 1 页 / 共 2 页
字号:


<CENTER>



<H4><A NAME="Heading7<FONT COLOR="#000077">Opening the Device</FONT></H4>



</CENTER>



<P>The <TT>open()</TT> routine must check to ensure a valid device has been specified,



validate the device request (permission to access the device or device not ready),



then initialize the device. The <TT>open()</TT> routine is run every time a process



uses the device.</P>



<P>The <TT>open()</TT> routine presented here is for a generic terminal device, <TT>td</TT>:<FONT



COLOR="#0066FF"></FONT>



<PRE><FONT COLOR="#0066FF">tdopen(device,flag)



int device,flag;



{



          /* definitions for local variables ignored */



          /* details and definitions ignored in code */







          /* check device number */



          if (UNMODEM(device) &gt;= NTDEVS)



          {



               seterror(ENXIO);



               return;



          }







          /* check if device in use */



          /* if so, see if superuser (suser) for override */



          tp = &amp;td_tty[UNMODEM(device)];



          address = td_address[UNMODEM(device)];



          if((tp-&gt;t_lflag &amp; XCLUDE) &amp;&amp; !suser())



          {



               seterror(EBBUSY);



               return;



          }







          /* if not open, initialize by calling ttinit() */



          if((tp-&gt;t_state &amp; (ISOPEN|WOPEN)) == 0)



          {



               ttinit(tp);



               /* initialize flags, and call tdparam() to set line */



               tdparam(device);



          }







          /* if a modem is used, check carrier status */



          /* if direct, set carrier detect flags */



          /* set interrupt priority to avoid overwrite */



          /* wait for carrier detect signal */



          /* code eliminated from example */



</FONT></PRE>



<CENTER>



<H4><FONT COLOR="#0066FF"><BR>



<A NAME="Heading8</FONT><FONT COLOR="#000077">Closing the Device</FONT></H4>



</CENTER>



<P>The <TT>close()</TT> routine is used only after the process is finished with the



device. The routine disables interrupts from the device and issues any shut-down



commands. All internal references to the device will be reset. <TT>close()</TT> routines



are not usually required in many device drivers because the device is treated as



being available throughout. Exceptions are removable media and exclusive-use devices.



Some modems require closing (<TT>close()</TT>) to allow the line to be hung up.</P>



<P>Again, the terminal device example is used for the <TT>close()</TT> routine sample:</P>



<PRE><FONT COLOR="#0066FF">tdclose(device)



{



register struct tty *tp;



tp = &amp;td_tty[UNMODEM(device)];



(*linesw[tp-&gt;t_line].l_close)(tp);



if(tp-&gt;t_cflag &amp; HUPCL)



tdmodem(device,TURNOFF);



/* turn off exclusive flag bit */



ip-&gt;t_lflag &amp; =~XCLUDE







}



</FONT></PRE>



<CENTER>



<H4><A NAME="Heading9<FONT COLOR="#000077">Strategy Functions</FONT></H4>



</CENTER>



<P>Strategy functions (block mode devices only) are issued with a parameter to the



kernel buffer header. The buffer header contains the instructions for a <TT>read</TT>



or <TT>write</TT> along with a memory location for the operation to occur to or from.



The size of the buffer is usually fixed at installation and varies from 512 to 1024



bytes. It can be examined in the file <TT>param.h</TT> as the <TT>BSIZE</TT> variable.



A device's block size may be smaller than the buffer block size, in which case, the



driver executes multiple reads or writes.</P>



<P>The <TT>strategy</TT> function can be illustrated in a sample device driver for



a hard disk. No code is supplied, but the skeleton explains the functions of the



device driver in order:<FONT COLOR="#0066FF"></FONT>



<PRE><FONT COLOR="#0066FF">int hdstrategy(bp)



register struct buf *bp;



{



/* initialize drive and partition numbers */



/* set local variables */







/* check for valid drive &amp; partition */



/* compute target cylinder */



/* disable interrupts */



/* push request into the queue */



/* check controller: if not active, start it */



/* reset interrupt level */







}



</FONT></PRE>



<CENTER>



<H4><A NAME="Heading10<FONT COLOR="#000077">Write Functions</FONT></H4>



</CENTER>



<P>Character mode devices employ a <TT>write()</TT> instruction that checks the arguments



of the instruction for validity, and then copies the data from the process memory



to the device driver buffer. When all data is copied, or the buffer is full, I/O



is initiated to the device until the buffer is empty, at which point the process



is repeated. Data is read from the process memory using a simple function (<TT>cpass</TT>)



that returns a <TT>-1</TT> when end of memory is reached. The data is written to



process memory using a complementary function (<TT>passc</TT>). The <TT>write()</TT>



routine is illustrated for the terminal device:</P>



<PRE><FONT COLOR="#0066FF">tdwrite(device)



         {



               register struct tty *tp;



               tp=&amp;td_tty[UNMODEM(device)];



               (*linesw[tp-&gt;t_line].l_write)(tp);



</FONT></PRE>







<PRE><FONT COLOR="#0066FF">          }



</FONT></PRE>



<P>Large amounts of data are handled by a process called <TT>copyio</TT> which takes



the addresses of source and destination, a byte count, and a status flag as arguments.



<CENTER>



<H4><A NAME="Heading11<FONT COLOR="#000077">Read Functions</FONT></H4>



</CENTER>



<P>The <TT>read()</TT> operation for character mode devices transfers data from the



device to the process memory. The operation is analogous to that of the <TT>write</TT>



procedure. For the terminal device, the <TT>read()</TT> code becomes</P>



<PRE><FONT COLOR="#0066FF">tdread(device)



{



register struct tty *tp;



tp=&amp;td_tty[UNMODEM(device)];



(*linesw[tp-&gt;t_line].l_read)(tp);







}



</FONT></PRE>



<P>A small buffer is used when several characters are to be copied at once by <TT>read()</TT>



or <TT>write()</TT>, rather than continually copying single characters. <TT>clist</TT>



implements a small buffer used by character mode devices as a series of linked lists



that use <TT>getc</TT> and <TT>putc</TT> to move characters on and off the buffer



respectively. A header for <TT>clist</TT> maintains a count of the contents.



<CENTER>



<H4><A NAME="Heading12<FONT COLOR="#000077">start and ioctl Routines</FONT></H4>



</CENTER>



<P>A <TT>start</TT> routine is usually used for both block and character mode devices.



It takes requests or data from device queues and sends them in order to the device.



Block mode devices queue data with the strategy routine, while character mode devices



use <TT>clist</TT>. The <TT>start</TT> routine maintains busy flags automatically



as instructions are passed to the device. When a device has finished its process,



it executes an <TT>intr</TT> routine which reinitializes the device for the next



process.</P>



<P>The character mode <TT>ioctl()</TT> routine provides a special series of instructions



to drivers. These include changes in the communications method between the driver



and the operating system, as well as device-dependent operations (tape load or rewind,



or memory allocation, for example).</P>



<P>The <TT>ioctl()</TT> function can be illustrated with the terminal device example.



The <TT>ioctl()</TT> routine, in this case, calls another function that sets the



device parameters. No code is supplied for the called function, but the skeleton



explains the process of the device driver in order:<FONT COLOR="#0066FF"></FONT>



<PRE><FONT COLOR="#0066FF">tdioctl(device,cmd,arg,mode)      int device;



int cmd;



int mode;



faddr_t arg;



{



if(ttiocom(&amp;td_tty[UNMODEM(device)],cmd,arg,mode))



tdparam(device)



}







tdparam(device)



{



/* initialize variables */



/* get address and flags for referenced line */



addr=td_addr[UNMODEM(device)];



cflag=td_tty[UNMODEM(device].t_cflag;







/* check speed: if zero hang up line */



/* set up speed change */



/* set up line control */



/* manage interrupts */



</FONT></PRE>







<PRE><FONT COLOR="#0066FF">}



</FONT></PRE>



<CENTER>



<H3><A NAME="Heading13<FONT COLOR="#000077">Using a New Device Driver</FONT></H3>



</CENTER>



<P>Drivers are added to Linux systems in a series of steps. First the interrupt handler



is identified, and then the device driver entry points (such as <TT>open</TT>) are



added to a driver entry point table. The entire driver is compiled and linked to



the kernel, and then placed in the <TT>/dev</TT> directory. (See Chapter 56, &quot;Working



with the Kernel,&quot; for more information on adding to the Linux kernel.) Finally,



the system is rebooted and the device driver tested. Obviously, changes to the driver



require the process to be repeated, so device driver debugging is an art that minimizes



the number of machine reboots!







<DL>



	<DT></DT>



</DL>











<DL>



	<DD>



<HR>



<A NAME="Heading14<FONT COLOR="#000077"><B>CAUTION: </B></FONT>Two basic don'ts



	are important for device driver programming. Don't use <TT>sleep()</TT> or <TT>seterror()</TT>



	during interrupt suspensions, and don't use floating-point operations. Interrupt



	suspensions must be minimized, but they must be used to avoid corruption of <TT>clist</TT>



	(or other buffer) data. Finally, it is important to minimize stack space.



<HR>







</DL>







<P>You can simplify debugging device drivers in many cases by using judicious <TT>printf</TT>



or <TT>getchar</TT> statements to another device, such as the console. Statements



like <TT>printf</TT> and <TT>getchar</TT> enable you to set up code that traces the



execution steps of the device driver. If you are testing the device when logged in



as root, the <TT>adb</TT> debugger can be used to allow examination of the kernel's



memory while the device driver executes. Careful use of <TT>adb</TT> allows direct



testing of minor changes in variables or addresses, but be careful as incorrect use



of <TT>adb</TT> may result in system crashes!</P>







<P>One of the most common problems with device drivers (other than faulty coding)



is the loss of interrupts or the suspension of a device while an interrupt is pending.



This causes the device to hang. A time-out routine is included in most device drivers



to prevent this. Typically, if an interrupt is expected and has not been received



within a specified amount of time, the device is checked directly to ensure the interrupt



was not missed. If an interrupt was missed, it can be simulated by code. You can



use the <TT>spl</TT> functions during debugging, which usually helps to isolate these



problems.</P>



<P>Block mode-based device drivers are generally written using interrupts. However,



more programmers are now using polling for character mode devices. Polling means



the device driver checks at frequent intervals to determine the device's status.



The device driver doesn't wait for interrupts but this does add to the CPU overhead



the process requires. Polling is not suitable for many devices, such as mass storage



systems, but for character mode devices it can be of benefit. Serial devices generally



are polled to save interrupt overhead.</P>



<P>A 19,200 baud terminal will cause approximately 1,920 interrupts per second, causing



the operating system to interrupt and enter the device driver that many times. By



replacing the interrupt routines with polling routines, the interval between CPU



demands can be decreased by an order of magnitude, using a small device buffer to



hold intermediate characters generated to or from the device. Real time devices also



benefit from polling, since the number of interrupts does not overwhelm the CPU.



If you want to use polling in your device drivers, you should read one of the books



dedicated to device driver design, as this is a complex subject.



<CENTER>



<H3><A NAME="Heading15<FONT COLOR="#000077">Summary</FONT></H3>



</CENTER>



<P>Most Linux users will never have to write a device driver, as most devices you



can buy already have a device driver available. If you acquire brand new hardware,



or have the adventurous bug, you may want to try writing a driver, though. Device



drivers are not really difficult to write (as long as you are comfortable coding



in a high-level language like C), but drivers tend to be very difficult to debug.



The device driver programmer must at all times be careful of impacting other processes



or devices. However, there is a peculiar sense of accomplishment when a device driver



executes properly.



















</td>
</tr>
</table>

<!-- begin footer information -->



</body></html>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -