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

📄 ch57.htm

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


<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
<SCRIPT>
<!--
function displayWindow(url, width, height) {
        var Win = window.open(url,"displayWindow",'width=' + width +
',height=' + height + ',resizable=1,scrollbars=yes');
}
//-->
</SCRIPT>
</HEAD>

 -->










<font face="Arial,Helvetica" size="-1" color="#006666">

<b>Linux</b></font><p>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">











 

























<UL>



	<LI><A HREF="#Heading1">- 57 -</A>



	<UL>



		<LI><A HREF="#Heading2">Device Drivers</A>



		<UL>



			<LI><A HREF="#Heading3">Device Drivers</A>



			<LI><A HREF="#Heading4">Interrupts</A>



			<LI><A HREF="#Heading5">Anatomy of a Linux Device Driver</A>



			<UL>



				<LI><A HREF="#Heading6">Headers</A>



				<LI><A HREF="#Heading7">Opening the Device</A>



				<LI><A HREF="#Heading8">Closing the Device</A>



				<LI><A HREF="#Heading9">Strategy Functions</A>



				<LI><A HREF="#Heading10">Write Functions</A>



				<LI><A HREF="#Heading11">Read Functions</A>



				<LI><A HREF="#Heading12">start and ioctl Routines</A>



			</UL>



			<LI><A HREF="#Heading13">Using a New Device Driver</A>



			<LI><A HREF="#Heading14">CAUTION</A>



			<LI><A HREF="#Heading15">Summary</A>



		</UL>



	</UL>



</UL>







<P>



<HR SIZE="4">







<H2 ALIGN="CENTER"><A NAME="Heading1<FONT COLOR="#000077">- 57 -</FONT></H2>



<H2 ALIGN="CENTER"><A NAME="Heading2<FONT COLOR="#000077">Device Drivers</FONT></H2>



<P><I>by Tim Parker</I></P>



<P>IN THIS CHAPTER</P>







<UL>



	<LI>Device Drivers 



	<P>



	<LI>Interrupts 



	<P>



	<LI>Anatomy of a Linux Device Driver 



	<P>



	<LI>Using a New Device Driver  



</UL>







<P>This chapter will look at:







<UL>



	<LI>What a device driver is



	<P>



	<LI>How Linux uses device drivers



	<P>



	<LI>Interrupts and device drivers



	<P>



	<LI>How a device driver is written



</UL>







<P>Device drivers provide an interface between the operating system and the peripherals



attached to the machine. A typical device driver consists of a number of functions



that accept I/O requests from the operating system and instruct the device to perform



those requests. In this manner, a uniform interface between devices and the operating



system kernel is provided.</P>



<P>We can't cover everything there is to know about device drivers in a single chapter.



Indeed, several sizable books have been written on the subject. Since device drivers



are not written by casual users, but mostly by talented programmers, the information



supplied here is mainly an introduction to the subject.</P>



<P>The code snippets in this chapter were taken from a set of simple device drivers



written in C. They are portable and designed for a UNIX system, but they also execute



properly under Linux. Use them only as a guide, if you decide you want to write device



drivers. Obtain one of the specialty books on the subject if you get serious about



programming device drivers.



<H3 ALIGN="CENTER"><A NAME="Heading3<FONT COLOR="#000077">Device Drivers</FONT></H3>



<P>Linux uses a device driver for every device attached to the system. The basic



device driver instructions are part of the kernel or loaded during the boot process.



By using a device driver, the devices appear to the operating system as files that



can addressed, redirected, or piped as normal files.</P>



<P>Each device attached to the Linux system is described in a device driver program



file, and some parameters about the device are described in a device file which is



usually stored in the <TT>/dev</TT> directory. When you add a new peripheral to the



system, a device driver must either be attached to the Linux operating system to



control the device, or you must write or supply a device driver. You also need a



device file in the <TT>/dev</TT> directory for each device. Otherwise, the device



can't be used.</P>



<P>Each device file has an assigned device number that uniquely identifies the device



to the operating system. Linux device numbers consist of two parts. The major number



identifies what general type the device driver handles, while the minor number can



specify a particular unit for that general type of device. For example, multiple



hard disk drives will use the same device driver (the same major number), but each



has unique minor numbers to identify the specific drives to the operating system.</P>



<P>There are two major types of device drivers: character mode and block mode. Any



UNIX device uses one or both of the driver types. Block mode drivers are the most



common type. They deal with I/O in blocks of data to and from the kernel's buffer



cache (which copies to memory the data from the cache). Originally designed for use



with disk drives, block mode is used with virtually all mass storage devices, such



as disk drives, high-capacity tape drives, magneto- optical drives, synchronous modems,



and some high-speed printers.</P>



<P>Character mode devices differ from block mode devices in two significant ways.



I/O can be processed directly to and from the process's memory space, without using



the kernel's cache. In addition, I/O requests are usually passed directly to the



character mode device. Terminals and printers are obvious character mode devices,



as are asynchronous modems and some tape drives.</P>



<P>Block mode devices perform a &quot;strategy&quot; function that reads or writes



a block of data to the device. A series of special device control functions called



<TT>ioctl()</TT> functions are available with character mode devices. In order to



use these <TT>ioctl()</TT> functions, block mode devices will sometimes use character



mode. An example is a tape drive that can use either a character or block mode driver,



depending on the type of data being written.</P>



<P>Regardless of the type of device driver, the driver itself performs a series of



basic tasks whenever a request is made of the device. First, the device is checked



to ensure that it is ready and available for use. If so, it is &quot;opened&quot;



to allow the calling process access. <TT>Read</TT> or <TT>write</TT> commands are



usually executed, and then the device is &quot;closed&quot; to allow other processes



access to the device.



<H3 ALIGN="CENTER"><A NAME="Heading4<FONT COLOR="#000077">Interrupts</FONT></H3>



<P>Interrupts are signals from the devices to the operating system to indicate that



attention is required. Interrupts are generated whenever an I/O is processed and



the device is ready for another process. The interrupts used by Linux are similar



to those used by DOS, so if you are familiar with DOS interrupts, you know most of



the story already.</P>



<P>Upon receipt of an interrupt, the operating system suspends whatever it was executing



and processes the interrupt. In most cases, interrupts are handled by the device



driver. Interrupts must be checked to ensure that they are valid and will not affect



operation of a process underway, except to suspend it momentarily.</P>



<P>A problem with handling interrupts is that the interrupt should not suspend the



Linux kernel's operation or that of the device drivers themselves, except under controlled



conditions. Interrupts that are not properly handled or carefully checked can cause



suspension of a device driver that was processing the I/O that the interrupt requested.</P>



<P>The processing of an interrupt is usually suspended during the stages where critical



operation would be affected. The areas of device driver code that should not allow



an interrupt to stop their processing are termed non-stoppable or critical code.



Typically, interrupt suspension during critical code segments is performed by raising



the CPU priority equal to or greater than the interrupt priority level. After critical



code execution, the CPU priority level is lowered again.</P>



<P>Interrupt priority is usually manipulated with four functions: <TT>spl5()</TT>,



<TT>spl6()</TT>, <TT>spl7()</TT>, and <TT>splx()</TT>. Calling one of the first three



will cause interrupts not to be acknowledged during processing. <TT>spl5()</TT> disables



disk drives, printer, and keyboard interrupts. <TT>spl6()</TT> disables the system



clock, while <TT>spl7()</TT> disables all interrupts, including serial devices. These



three functions always return a code indicating the previous value of the interrupt



level. <TT>splx()</TT> is used to restore interrupts to their previous values.</P>



<P>Therefore, before processing critical code, embedding the command<FONT COLOR="#0066FF"></FONT>



<PRE><FONT COLOR="#0066FF">old_level = spl5();



</FONT></PRE>



<P>in the device driver source disables interrupts until the following command is



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



<PRE><FONT COLOR="#0066FF">splx(old_level);



</FONT></PRE>



<P>Multiple level changes are combined into device drivers as in the following example:<FONT



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



<PRE><FONT COLOR="#0066FF">int level_a, level_b;



level_a = spl5();



/* do any code that can't be  */



/* interrupted by disk drives */



level_b = spl7();



/* do all code that can't be  */



/* interrupted by anything    */



splx(level_b);



/* any final code that's not  */



/* interrupted by disk drives */



splx(level_a);



</FONT></PRE>



<P>This seemingly awkward method of bouncing between levels is necessary to avoid



freezing the device driver and kernel, which prevents the system from operating normally.



The protection mechanisms must be invoked only for as short a time as necessary.</P>



<P>It is usually unwise to use the <TT>spl6()</TT> and <TT>spl7()</TT> functions.



<TT>spl6()</TT> can cause the system clock to lose time in some cases, and <TT>spl7()</TT>



causes loss of characters in serial I/O, unless they are used for very short time



spans. Even then, it is usually sufficient to use <TT>spl5()</TT> for all interrupts



in critical code.



<H3 ALIGN="CENTER"><A NAME="Heading5<FONT COLOR="#000077">Anatomy of a Linux



Device Driver</FONT></H3>



<P>Device driver code is similar to normal code in its structure. In Linux, drivers



are generally written in C, although assembler and C++ are still occasionally used.



<H4 ALIGN="CENTER"><A NAME="Heading6<FONT COLOR="#000077">Headers</FONT></H4>



<P>A typical device driver has a header that consists of <TT>include</TT> statements



for system functions, device register addresses, content definitions, and driver



global variable definitions. Most device drivers use a standard list of <TT>include</TT>



files, such as this: 



<TABLE BORDER="0">



	<TR ALIGN="LEFT" rowspan="1">



		<TD ALIGN="LEFT"><TT>param.h</TT> </TD>



		<TD ALIGN="LEFT">Kernel parameters </TD>



	</TR>



	<TR ALIGN="LEFT" rowspan="1">



		<TD ALIGN="LEFT"><TT>dir.h</TT> </TD>



		<TD ALIGN="LEFT">Directory parameters </TD>



	</TR>



	<TR ALIGN="LEFT" rowspan="1">



		<TD ALIGN="LEFT"><TT>user.h</TT> </TD>



		<TD ALIGN="LEFT">User area definitions </TD>



	</TR>



	<TR ALIGN="LEFT" rowspan="1">



		<TD ALIGN="LEFT"><TT>tty.h</TT> </TD>



		<TD ALIGN="LEFT">Terminal and <TT>clist</TT> definitions </TD>



	</TR>



	<TR ALIGN="LEFT" rowspan="1">



		<TD ALIGN="LEFT">











			<BLOCKQUOTE>



			<P><TT>buf.h</TT>







			</BLOCKQUOTE>







			<P>



		</TD>



		<TD ALIGN="LEFT">











			<BLOCKQUOTE>



			<P>Buffer header information







			</BLOCKQUOTE>







			<P>



		</TD>



	</TR>



</TABLE>



The <TT>tty.h</TT> file is used for character mode drivers, while <TT>buf.h</TT>



is used by all block mode devices.</P>



<P>Device registers are defined in the device driver header and are based on the



device. For a character mode device, these registers commonly refer to port addresses,



such as I/O address, status bits, and control bits. Toggle commands for the device



are defined as their device codes.</P>







<P>An example of device register's initialization is shown in the device driver for



a standard screen terminal (UART) device:<FONT COLOR="#0066FF"></FONT>



<PRE><FONT COLOR="#0066FF">/* define the registers */



#define RRDATA      0x01      /* receive */



#define RTDATA      0x02      /* transmit */



#define RSTATUS     0x03      /* status */



#define RCONTRL     0x04      /* control */



...etc







/* define the status registers */



#define SRRDY       0x01      /* received data ready */



#define STRDY       0x02      /* transmitter ready */



#define SPERR       0x08      /* parity error */



#define SCTS        0x40      /* clear to send status */



...etc



</FONT></PRE>



<P>The functions the device driver must perform are dependent on the nature of the



device. All devices have an <TT>open()</TT> and <TT>close()</TT> routine that allows



the device to perform I/O.

⌨️ 快捷键说明

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