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

📄 x848.html

📁 linux驱动开发
💻 HTML
📖 第 1 页 / 共 2 页
字号:
<HTML
><HEAD
><TITLE
>Talking to Device Files (writes and IOCTLs)}</TITLE
><META
NAME="GENERATOR"
CONTENT="Microsoft FrontPage 4.0"><LINK
REL="HOME"
TITLE="The Linux Kernel Module Programming Guide"
HREF="index.html"><LINK
REL="UP"
TITLE="Talking To Device Files"
HREF="c846.html"><LINK
REL="PREVIOUS"
TITLE="Talking To Device Files"
HREF="c846.html"><LINK
REL="NEXT"
TITLE="System Calls"
HREF="c929.html"></HEAD
><BODY
CLASS="SECT1"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER" style="width: 980; height: 55"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>The Linux KerneLinux内核驱动模块编程指南 (内核版本2.2, 2.4)</TH 
></TR 
><TR 
><TD 
WIDTH="10%" 
ALIGN="left" 
VALIGN="bottom" 
><A 
HREF="c846.html" 
ACCESSKEY="P" 
>返回</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
>第七章.与设备文件对话</TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="c929.html"
ACCESSKEY="N"
>继续</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="SECT1"
><H1
CLASS="SECT1"
><A
NAME="AEN848"
></A
>7.1. 与设备文件对话 (writes and IOCTLs)</H1
><P
>设备文件是用来代表相对应的硬件设备。绝大多数的硬件设备是用来进行输出和输入操作的,所以在内核中肯定有内核从进程中获得发送到设备的输出的机制。这是通过打开一个设备文件然后向其中进行写操作来实现的,如同对普通文件的写操作。在下面的的例子中,这是通过 <TT 
CLASS="FUNCTION" 
>device_write </TT
>实现的。</P
><P
>但这并不总是够用。设想你有一个通过串口连接的调制解调器(即使你使用的是内置调制解调器,对于CPU来说同样也是通过连接在串口上来实现工作的)。通常我们通过打开一个设备文件向调制解调器发送信息(将要通过通信线路传输的指令或数据)或读取信息(从通信线路中返回的响应指令或数据)。但是,我们如何设置同串口对话的速率,也就是向串口传输数据的速率这个问题仍然没有解决。</P
><P
>解决之道是在Unix系统中的函数 <TT 
CLASS="FUNCTION" 
>ioctl</TT 
>  (Input Output ConTroL的简写)。每个设备可以有自己的 <TT
CLASS="FUNCTION"
>ioctl</TT
>  命令,通过读取 <TT 
CLASS="FUNCTION" 
>ioctl</TT 
>'s 可以从进程中向内核发送信息,或写 <TT 
CLASS="FUNCTION" 
>ioctl</TT 
>'s 向进程返回信息<A
NAME="AEN870"
HREF="#FTN.AEN870"
><SPAN
CLASS="footnote"
>[1]</SPAN
></A
>,或者两者都是,或都不是。函数 <TT 
CLASS="FUNCTION" 
>ioctl</TT 
> 调用时需要三个参数:合适的设备文件的文件描述符, ioctl 
  号,和一个可以被一个任务使用来传递任何东西的 long 
  类型的参数<A
NAME="AEN875"
HREF="#FTN.AEN875"
><SPAN
CLASS="footnote"
>[2]</SPAN
></A
>。</P
><P
>&nbsp;ioctl 号是反映主设备号, ioctl 
  的种类,对应的命令和参数类型的数字。它通常是通过在头文件中宏调用 (<TT 
CLASS="VARNAME" 
>_IO</TT 
>, <TT 
CLASS="VARNAME" 
>_IOR</TT 
>, <TT 
CLASS="VARNAME" 
>_IOW</TT 
> 
	or <TT 
CLASS="VARNAME" 
>_IOWR</TT 
> --- 取决于其种类)来建立的。该头文件应该被使用 <TT
CLASS="FUNCTION"
>ioctl</TT
>  的用户程序包含(这样它们就可以生成正确的 <TT
CLASS="FUNCTION"
>ioctl</TT
>'s)和内核驱动模块包含(这样模块才能理解它)。在下面的例子中,头文件为 
  <TT
CLASS="FILENAME"
>chardev.h</TT
>  ,用户程序为 <TT
CLASS="FUNCTION"
>ioctl.c</TT
>。</P
><P
>即使你只想在自己的模块中使用 <TT 
CLASS="FUNCTION" 
>ioctl</TT 
>s ,你最好还是接收正式的 
	<TT 
CLASS="FUNCTION" 
>ioctl</TT 
>  标准,这样当你意外的使用别人的 
	<TT 
CLASS="FUNCTION" 
>ioctls</TT
>  
  ,或别人使用你的时,你会知道有错误发生。详情参见内核代码目录树下的文件 
  <TT
CLASS="FILENAME"
>Documentation/ioctl-number.txt。</TT
></P
><DIV
CLASS="EXAMPLE"
><A
NAME="AEN907"
></A
><P
><B
>Example 7-1. chardev.c</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>/*  chardev.c - Create an input/output character device
 */

#include &lt;linux/kernel.h&gt;   /* We're doing kernel work */
#include &lt;linux/module.h&gt;   /* Specifically, a module */

/* Deal with CONFIG_MODVERSIONS */
#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include &lt;linux/modversions.h&gt;
#endif        

/* For character devices */

/* The character device definitions are here */
#include &lt;linux/fs.h&gt;

/* A wrapper which does next to nothing at
 * at present, but may help for compatibility
 * with future versions of Linux */
#include &lt;linux/wrapper.h&gt;

			     
/* Our own ioctl numbers */
#include &quot;chardev.h&quot;


/* In 2.2.3 /usr/include/linux/version.h includes a 
 * macro for this, but 2.0.35 doesn't - so I add it 
 * here if necessary. */
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))
#endif



#if LINUX_VERSION_CODE &gt;= KERNEL_VERSION(2,2,0)
#include &lt;asm/uaccess.h&gt;  /* for get_user and put_user */
#endif



#define SUCCESS 0


/* Device Declarations ******************************** */


/* The name for our device, as it will appear in 
 * /proc/devices */
#define DEVICE_NAME &quot;char_dev&quot;


/* The maximum length of the message for the device */
#define BUF_LEN 80

/* Is the device open right now? Used to prevent 
 * concurent access into the same device */
static int Device_Open = 0;

/* The message the device will give when asked */
static char Message[BUF_LEN];

/* How far did the process reading the message get? 
 * Useful if the message is larger than the size of the 
 * buffer we get to fill in device_read. */
static char *Message_Ptr;


/* This function is called whenever a process attempts 
 * to open the device file */
static int device_open(struct inode *inode, 
                       struct file *file)
{
#ifdef DEBUG
  printk (&quot;device_open(%p)\n&quot;, file);
#endif

  /* We don't want to talk to two processes at the 
   * same time */
  if (Device_Open)
    return -EBUSY;

  /* If this was a process, we would have had to be 
   * more careful here, because one process might have 
   * checked Device_Open right before the other one 
   * tried to increment it. However, we're in the 
   * kernel, so we're protected against context switches.
   *
   * This is NOT the right attitude to take, because we
   * might be running on an SMP box, but we'll deal with
   * SMP in a later chapter.
   */ 

  Device_Open++;

  /* Initialize the message */
  Message_Ptr = Message;

  MOD_INC_USE_COUNT;

  return SUCCESS;
}


/* This function is called when a process closes the 
 * device file. It doesn't have a return value because 
 * it cannot fail. Regardless of what else happens, you 
 * should always be able to close a device (in 2.0, a 2.2
 * device file could be impossible to close).
 */
#if LINUX_VERSION_CODE &gt;= KERNEL_VERSION(2,2,0)
static int device_release(struct inode *inode, 
                          struct file *file)
#else
static void device_release(struct inode *inode, 
                           struct file *file)
#endif
{
#ifdef DEBUG
  printk (&quot;device_release(%p,%p)\n&quot;, inode, file);
#endif
 
  /* We're now ready for our next caller */
  Device_Open --;

  MOD_DEC_USE_COUNT;

#if LINUX_VERSION_CODE &gt;= KERNEL_VERSION(2,2,0)
  return 0;
#endif
}



/* This function is called whenever a process which 
 * has already opened the device file attempts to 
 * read from it. */
#if LINUX_VERSION_CODE &gt;= KERNEL_VERSION(2,2,0)
static ssize_t device_read(
    struct file *file,
    char *buffer, /* The buffer to fill with the data */   
    size_t length,     /* The length of the buffer */
    loff_t *offset) /* offset to the file */
#else
static int device_read(
    struct inode *inode,
    struct file *file,
    char *buffer,   /* The buffer to fill with the data */ 
    int length)     /* The length of the buffer 
                     * (mustn't write beyond that!) */
#endif
{
  /* Number of bytes actually written to the buffer */
  int bytes_read = 0;

#ifdef DEBUG
  printk(&quot;device_read(%p,%p,%d)\n&quot;, file, buffer, length);
#endif

  /* If we're at the end of the message, return 0 
   * (which signifies end of file) */
  if (*Message_Ptr == 0)
    return 0;

  /* Actually put the data into the buffer */
  while (length &amp;&amp; *Message_Ptr)  {

    /* Because the buffer is in the user data segment, 
     * not the kernel data segment, assignment wouldn't 
     * work. Instead, we have to use put_user which 
     * copies data from the kernel data segment to the 
     * user data segment. */
    put_user(*(Message_Ptr++), buffer++);
    length --;
    bytes_read ++;
  }

#ifdef DEBUG
   printk (&quot;Read %d bytes, %d left\n&quot;, bytes_read, length);
#endif

   /* Read functions are supposed to return the number 
    * of bytes actually inserted into the buffer */
  return bytes_read;
}


/* This function is called when somebody tries to 
 * write into our device file. */ 
#if LINUX_VERSION_CODE &gt;= KERNEL_VERSION(2,2,0)
static ssize_t device_write(struct file *file,
                            const char *buffer,
                            size_t length,
                            loff_t *offset)
#else
static int device_write(struct inode *inode,
                        struct file *file,
                        const char *buffer,
                        int length)
#endif
{
  int i;

#ifdef DEBUG
  printk (&quot;device_write(%p,%s,%d)&quot;,
    file, buffer, length);
#endif

  for(i=0; i&lt;length &amp;&amp; i&lt;BUF_LEN; i++)
#if LINUX_VERSION_CODE &gt;= KERNEL_VERSION(2,2,0)
    get_user(Message[i], buffer+i);
#else
    Message[i] = get_user(buffer+i);
#endif  

  Message_Ptr = Message;

  /* Again, return the number of input characters used */
  return i;
}


/* This function is called whenever a process tries to 
 * do an ioctl on our device file. We get two extra 
 * parameters (additional to the inode and file 
 * structures, which all device functions get): the number
 * of the ioctl called and the parameter given to the 
 * ioctl function.
 *
 * If the ioctl is write or read/write (meaning output 
 * is returned to the calling process), the ioctl call 
 * returns the output of this function.
 */
int device_ioctl(
    struct inode *inode,
    struct file *file,
    unsigned int ioctl_num,/* The number of the ioctl */
    unsigned long ioctl_param) /* The parameter to it */
{
  int i;
  char *temp;
#if LINUX_VERSION_CODE &gt;= KERNEL_VERSION(2,2,0)
  char ch;
#endif

  /* Switch according to the ioctl called */
  switch (ioctl_num) {
    case IOCTL_SET_MSG:
      /* Receive a pointer to a message (in user space) 
       * and set that to be the device's message. */ 

      /* Get the parameter given to ioctl by the process */
      temp = (char *) ioctl_param;
   
      /* Find the length of the message */
#if LINUX_VERSION_CODE &gt;= KERNEL_VERSION(2,2,0)
      get_user(ch, temp);
      for (i=0; ch &amp;&amp; i&lt;BUF_LEN; i++, temp++)
        get_user(ch, temp);
#else
      for (i=0; get_user(temp) &amp;&amp; i&lt;BUF_LEN; i++, temp++)
	;
#endif

⌨️ 快捷键说明

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