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

📄 x571.html

📁 linux驱动开发
💻 HTML
📖 第 1 页 / 共 2 页
字号:
><P   
><TT   
CLASS="VARNAME"   
>MOD_DEC_USE_COUNT</TT   
>: Decrement the use count.</P   
></LI   
><LI   
><P   
><TT   
CLASS="VARNAME"   
>MOD_IN_USE</TT   
>: Display the use count.</P   
></LI   
></UL   
><P   
>保持该计数器时刻精确是非常重要的;如果你丢失了正确的计数,你将无法卸载模块,那就只有重启了。不过这种情况在今后编写内核模块时也是无法避免的。</P   
></DIV   
><DIV   
CLASS="SECT2"   
><H2   
CLASS="SECT2"   
><A   
NAME="AEN685"   
></A   
>4.1.5. chardev.c</H2     
><P    
>下面的代码示范了一个叫做 <TT   
CLASS="FILENAME"   
>chardev </TT   
>的字符设备。你可以用 <TT   
CLASS="FILENAME"   
>cat </TT   
>输出该设备文件的内容(或用别的程序打开它)时,驱动模块会将该设备文件被读取的次数显示。目前对设备文件的写操作还不被支持(像 
    <B   
CLASS="COMMAND"   
>echo "hi" &#62;    
			/dev/hello </B   
>),但会捕捉这些操作并且告诉用户该操作不被支持。不要担心我们对读入缓冲区的数据做了什么;我们什么都没做。我们只是读入数据并输出我们已经接收了数据的信息。</P   
><DIV   
CLASS="EXAMPLE"   
><A   
NAME="AEN692"   
></A   
><P   
><B   
>Example 4-1. chardev.c</B   
></P   
><TABLE   
BORDER="0"   
BGCOLOR="#E0E0E0"   
WIDTH="100%"   
><TR   
><TD   
><FONT   
COLOR="#000000"   
><PRE   
CLASS="PROGRAMLISTING"   
>/*  chardev.c: Creates a read-only char device that says how many times
 *  you've read from the dev file
 */
	
	#if defined(CONFIG_MODVERSIONS) &amp;&amp; ! defined(MODVERSIONS)
	   #include &lt;linux/modversions.h&gt;
	   #define MODVERSIONS
	#endif
	#include &lt;linux/kernel.h&gt;
	#include &lt;linux/module.h&gt;
	#include &lt;linux/fs.h&gt;
	#include &lt;asm/uaccess.h&gt;  /* for put_user */
	
	/*  Prototypes - this would normally go in a .h file
	 */
	int init_module(void);
	void cleanup_module(void);
	static int device_open(struct inode *, struct file *);
	static int device_release(struct inode *, struct file *);
	static ssize_t device_read(struct file *, char *, size_t, loff_t *);
	static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
	
	#define SUCCESS 0
	#define DEVICE_NAME &quot;chardev&quot; /* Dev name as it appears in /proc/devices   */
	#define BUF_LEN 80            /* Max length of the message from the device */
	
	
	/* Global variables are declared as static, so are global within the file. */
	
	static int Major;            /* Major number assigned to our device driver */
	static int Device_Open = 0;  /* Is device open?  Used to prevent multiple  */
	                                access to the device                       */
	static char msg[BUF_LEN];    /* The msg the device will give when asked    */
	static char *msg_Ptr;
	
	static struct file_operations fops = {
	  .read = device_read, 
	  .write = device_write,
	  .open = device_open,
	  .release = device_release
	};
	
	
	/*                   Functions
	 */
	
	int init_module(void)
	{
	   Major = register_chrdev(0, DEVICE_NAME, &amp;fops);
	
	   if (Major &lt; 0) {
	     printk (&quot;Registering the character device failed with %d\n&quot;, Major);
	     return Major;
	   }
	
	   printk(&quot;&lt;1&gt;I was assigned major number %d.  To talk to\n&quot;, Major);
	   printk(&quot;&lt;1&gt;the driver, create a dev file with\n&quot;);
		 printk(&quot;'mknod /dev/hello c %d 0'.\n&quot;, Major);
	   printk(&quot;&lt;1&gt;Try various minor numbers.  Try to cat and echo to\n&quot;);
		 printk(&quot;the device file.\n&quot;);
	   printk(&quot;&lt;1&gt;Remove the device file and module when done.\n&quot;);
	
	   return 0;
	}
	
	
	void cleanup_module(void)
	{
	   /* Unregister the device */
	   int ret = unregister_chrdev(Major, DEVICE_NAME);
	   if (ret &lt; 0) printk(&quot;Error in unregister_chrdev: %d\n&quot;, ret);
	}  
	
	
	/*                   Methods
	 */
	
	/* Called when a process tries to open the device file, like
	 * &quot;cat /dev/mycharfile&quot;
	 */
	static int device_open(struct inode *inode, struct file *file)
	{
	   static int counter = 0;
	   if (Device_Open) return -EBUSY;
	   Device_Open++;
	   sprintf(msg,&quot;I already told you %d times Hello world!\n&quot;, counter++&quot;);
	   msg_Ptr = msg;
	   MOD_INC_USE_COUNT;
	
	  return SUCCESS;
	}
	
	
	/* Called when a process closes the device file.
	 */
	static int device_release(struct inode *inode, struct file *file)
	{
	   Device_Open --;     /* We're now ready for our next caller */
	
	   /* Decrement the usage count, or else once you opened the file, you'll
		    never get get rid of the module. */
	   MOD_DEC_USE_COUNT;
	
	   return 0;
	}
	
	
	/* Called when a process, which already opened the dev file, attempts to
	   read from it.
	*/
	static ssize_t device_read(struct file *filp,
	   char *buffer,    /* The buffer to fill with data */
	   size_t length,   /* The length of the buffer     */
	   loff_t *offset)  /* Our offset in the file       */
	{
	   /* Number of bytes actually written to the buffer */
	   int bytes_read = 0;
	
	   /* If we're at the end of the message, return 0 signifying end of file */
	   if (*msg_Ptr == 0) return 0;
	
	   /* Actually put the data into the buffer */
	   while (length &amp;&amp; *msg_Ptr)  {
	
		/* The buffer is in the user data segment, not the kernel segment;
		 * assignment won't work.  We have to use put_user which copies data from
		 * the kernel data segment to the user data segment. */
	      put_user(*(msg_Ptr++), buffer++);
	
	      length--;
	      bytes_read++;
	   }
	
	   /* Most read functions return the number of bytes put into the buffer */
	   return bytes_read;
	}
	
	
	/*  Called when a process writes to dev file: echo &quot;hi&quot; &gt; /dev/hello */
	static ssize_t device_write(struct file *filp,
	   const char *buff,
		 size_t len,
		 loff_t *off)
	{
	   printk (&quot;&lt;1&gt;Sorry, this operation isn't supported.\n&quot;);
	   return -EINVAL;
	}</PRE
></FONT   
></TD   
></TR   
></TABLE   
></DIV   
></DIV   
><DIV   
CLASS="SECT2"   
><H2   
CLASS="SECT2"   
><A   
NAME="AEN695"   
></A   
>4.1.6. 为多个版本的内核编写内核模块</H2    
><P   
>系统调用,也就是内核提供给进程的接口,基本上是保持不变的。也许会添入新的系统调用,但那些已有的不会被改动。这对于向下兼容是非常重要的。在多数情况下,设备文件是保持不变的。但内核的内部在不同版本之间还是会有区别的。</P   
><P   
>Linux内核分为稳定版本(版本号中间为偶数)和试验版本(版本号中间为奇数)。试验版本中可以试验各种各样的新而酷的主意,有些会被证实是一个错误,有些在下一版中会被完善。总之,你不能依赖这些版本中的接口(这也是我不在本文档中支持它们的原因,它们更新的太快了)。在稳定版本中,我们可以期望接口保持一致,除了那些修改代码中错误的版本。</P   
><P   
>如果你要支持多版本的内核,你需要编写为不同内核编译的代码树。可以通过比较宏    
			<TT    
CLASS="VARNAME"    
>LINUX_VERSION_CODE</TT    
> 和宏 <TT    
CLASS="VARNAME"    
>KERNEL_VERSION</TT    
> 来实现。在版本号为 <TT    
CLASS="VARNAME"    
>a.b.c</TT    
>    
    的内核中,该宏的值可以为 2^16×a+2^8×b+c 。注意在内核    
			2.0.35 
    及之前是没有该宏的。如果你确实要支持很老的内核,你必须自己定义该宏,就像:</P   
><DIV   
CLASS="EXAMPLE"   
><A   
NAME="AEN709"   
></A   
><P   
><B   
>Example 4-2. some title</B   
></P   
><TABLE   
BORDER="0"   
BGCOLOR="#E0E0E0"   
WIDTH="100%"   
><TR   
><TD   
><FONT   
COLOR="#000000"   
><PRE   
CLASS="PROGRAMLISTING"   
>    #if LINUX_KERNEL_VERSION &gt;= KERNEL_VERSION(2,2,0)
        #define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))
    #endif</PRE
></FONT   
></TD   
></TR   
></TABLE   
></DIV   
><P   
>既然它们是宏,你可以用预处理语句 <B    
CLASS="COMMAND"    
>#ifndef KERNEL_VERSION</B    
>  去检测该宏是否被定义,而不是靠内核版本识别。</P   
></DIV   
></DIV   
><H3   
CLASS="FOOTNOTES"   
>注意</H3   
><TABLE   
BORDER="0"   
CLASS="FOOTNOTES"   
WIDTH="100%"   
><TR   
><TD   
ALIGN="LEFT"   
VALIGN="TOP"   
WIDTH="5%"   
><A   
NAME="FTN.AEN632"   
HREF="x571.html#AEN632"   
><SPAN   
CLASS="footnote"   
>[1]</SPAN   
></A   
></TD   
><TD   
ALIGN="LEFT"   
VALIGN="TOP"   
WIDTH="95%"   
><P   
>这只是习惯上的。将设备文件放在你的用户目录下是没有问题的。但是当真正提供成熟的驱动模块时,请保证将设备文件放在 <TT    
CLASS="FILENAME"    
>/dev</TT    
>  下。</P   
></TD   
></TR   
></TABLE   
><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="c569.html"   
ACCESSKEY="P"   
>返回</A   
></TD   
><TD   
WIDTH="34%"   
ALIGN="center"   
VALIGN="top"   
><A   
HREF="index.html"   
ACCESSKEY="H"   
>返回首页</A   
></TD   
><TD   
WIDTH="33%"   
ALIGN="right"   
VALIGN="top"   
><A   
HREF="c714.html"   
ACCESSKEY="N"   
>继续</A   
></TD   
></TR   
><TR   
><TD   
WIDTH="33%"   
ALIGN="left"   
VALIGN="top"   
>字符设备文件</TD   
><TD   
WIDTH="34%"   
ALIGN="center"   
VALIGN="top"   
><A   
HREF="c569.html"   
ACCESSKEY="U"   
>返回本章开始</A   
></TD   
><TD   
WIDTH="33%"   
ALIGN="right"   
VALIGN="top"   
>关于 /proc 文件系统</TD   
></TR   
></TABLE   
></DIV   
></BODY   
></HTML   
>

⌨️ 快捷键说明

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