Linux 设备驱动程序

有人能解释一下吗,

  1. 什么是 IOCTL
  2. 这是干什么用的?
  3. 怎么用?
  4. 为什么我不能定义新的函数,做同样的工作作为 IOCTL
130886 次浏览

ioctl,意思是“输入-输出控制”是一种特定于设备的系统调用。Linux 中只有少数几个系统调用(300-400) ,它们不足以表达设备可能具有的所有独特功能。因此,驱动程序可以定义一个 ioctl,它允许用户空间应用程序向它发送订单。然而,ioctl 不是很灵活,而且容易变得有点混乱(几十个“神奇的数字”,只是工作... 或不) ,并且也可能是不安全的,因为你传递一个缓冲区到内核-错误的处理可以很容易破坏事情。

另一种选择是 sysfs接口,在该接口中,您在 /sys/下设置一个文件,并读/写该文件以从驱动程序获取信息。一个如何设置的例子:

static ssize_t mydrvr_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", DRIVER_RELEASE);
}


static DEVICE_ATTR(version, S_IRUGO, mydrvr_version_show, NULL);

在安装驱动程序期间:

device_create_file(dev, &dev_attr_version);

然后在 /sys/中为您的设备提供一个文件,例如,用于块驱动程序的 /sys/block/myblk/version

另一种使用量更大的方法是 netlink,它是一种通过 BSD 套接字接口与驱动程序通信的 IPC (行程间通讯)方法。这是使用,例如,由 WiFi 驱动程序。然后使用 libnllibnl3库从用户空间与它进行通信。

ioctl函数对于实现设备驱动程序来设置设备上的配置非常有用。例如,有配置选项的打印机可以检查和设置字体系列、字体大小等。ioctl可以用来获得当前的字体以及设置一个新的字体。用户应用程序使用 ioctl向打印机发送代码,告诉打印机返回当前字体或设置新字体。

int ioctl(int fd, int request, ...)
  1. fd是由 open返回的文件描述符;
  2. request是请求代码。例如,GETFONT将从打印机获得当前的字体,SETFONT将设置打印机上的字体;
  3. 第三个参数是 void *。根据第二个参数,第三个参数可能存在也可能不存在, 例如,如果第二个参数是 SETFONT,第三个参数可以是字体名称,如 "Arial";

int request不仅仅是一个宏。用户应用程序需要生成请求代码和设备驱动程序模块,以确定必须使用设备上的哪个配置。应用程序使用 ioctl发送请求代码,然后使用设备驱动程序模块中的请求代码确定要执行的操作。

请求代码由4个主要部分组成

    1. A Magic number - 8 bits
2. A sequence number - 8 bits
3. Argument type (typically 14 bits), if any.
4. Direction of data transfer (2 bits).

如果在打印机上设置字体的请求代码是 SETFONT,则数据传输的方向将是从用户应用程序到设备驱动程序模块(用户应用程序将字体名称 "Arial"发送到打印机)。 如果请求代码是 GETFONT,则方向是从打印机到用户应用程序。

为了生成请求代码,Linux 提供了一些预定义的类似函数的宏。

1. _IO(MAGIC, SEQ_NO)都是8位,0到255,比如说我们想暂停打印机。 这不需要数据传输。因此我们将生成如下所示的请求代码

#define PRIN_MAGIC 'P'
#define NUM 0
#define PAUSE_PRIN __IO(PRIN_MAGIC, NUM)

现在使用 ioctl作为

ret_val = ioctl(fd, PAUSE_PRIN);

驱动程序模块中相应的系统调用将接收代码并暂停打印机。

  1. __IOW(MAGIC, SEQ_NO, TYPE) MAGICSEQ_NO与上面的相同,TYPE给出下一个参数的类型,回想一下 ioctl的第三个参数是 void *__IOW中的 W 表示从用户应用程序到驱动程序模块的数据流。举个例子, 假设我们要将打印机字体设置为 "Arial"
#define PRIN_MAGIC 'S'
#define SEQ_NO 1
#define SETFONT __IOW(PRIN_MAGIC, SEQ_NO, unsigned long)

更进一步,

char *font = "Arial";
ret_val = ioctl(fd, SETFONT, font);

现在 font是一个指针,这意味着它是一个最好表示为 unsigned long的地址,因此 _IOW的第三部分提到了类型。此外,这个字体的地址传递到相应的系统调用中实现的设备驱动程序模块作为 unsigned long,我们需要在使用它之前将其转换为适当的类型。内核空间可以访问用户空间,因此这是可行的。另外两个类似函数的宏是 __IOR(MAGIC, SEQ_NO, TYPE)__IORW(MAGIC, SEQ_NO, TYPE),其中数据流将分别从内核空间到用户空间和两种方式。

如果有帮助请告诉我!