【发布时间】:2013-03-26 07:53:42
【问题描述】:
谁能解释一下,
- 什么是
IOCTL? - 它是做什么用的?
- 如何使用它?
- 为什么我不能定义与
IOCTL工作相同的新函数?
【问题讨论】:
标签: linux linux-kernel driver linux-device-driver ioctl
谁能解释一下,
IOCTL?IOCTL 工作相同的新函数?【问题讨论】:
标签: linux linux-kernel driver linux-device-driver ioctl
ioctl 函数可用于实现设备驱动程序以设置设备上的配置。例如具有检查和设置字体系列、字体大小等配置选项的打印机。ioctl 可用于获取当前字体以及将字体设置为新字体。用户应用程序使用ioctl 向打印机发送代码,告诉它返回当前字体或将字体设置为新字体。
int ioctl(int fd, int request, ...)
fd是文件描述符,open返回的那个;request 是请求代码。例如GETFONT 会从打印机获取当前字体,SETFONT 会设置打印机上的字体;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);
驱动模块中对应的系统调用会接收到代码并暂停打印机。
__IOW(MAGIC, SEQ_NO, TYPE)MAGIC和SEQ_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),其中数据流将分别从内核空间到用户空间以及双向。
如果这有帮助,请告诉我!
【讨论】:
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,它是一种 IPC(进程间通信)方法,通过 BSD 套接字接口与您的驱动程序通信。例如,WiFi 驱动程序使用它。然后,您可以使用 libnl 或 libnl3 库从用户空间与它通信。
【讨论】: