具有多个读取操作的Linux USB驱动程序,ioctl还是fops?

时间:2022-04-05 16:08:18

I am writing a driver for a USB device that has three different read/write operations (flash, EEPROM, and I2C), each with a different implementation. I've been scratching my head about this, since I'm new to this whole world of linux kernel development. I've read that I should avoid ioctl at all costs, but I cannot tell how to implement this. Since everything in linux is a file, could I create multiple endpoints to write to for each location? How would I even go about doing this? Would I define multiple usb_class_driver structs?

我正在为USB设备编写驱动程序,该驱动程序具有三种不同的读/写操作(闪存,EEPROM和I2C),每种操作都有不同的实现。我一直在讨论这个问题,因为我是Linux内核开发的全新世界。我已经读过我应该不惜一切代价避免ioctl,但我不知道如何实现它。由于linux中的所有内容都是文件,我是否可以为每个位置创建多个端点来写入?我怎么会这样做呢?我会定义多个usb_class_driver结构吗?

On the flip side, should I include all the functionality in a single endpoint and use ioctl? Is it better to split the work up from the same driver or to consolidate all the functionality in one place?

另一方面,我应该在单个端点中包含所有功能并使用ioctl吗?将工作从同一驱动程序拆分或在一个地方整合所有功能是否更好?

I cannot use libusb due to its limitations on isochronous transfers and lack of direct control over dma transfers (both needed the product final product).

我不能使用libusb,因为它在等时传输方面受到限制,并且缺乏对dma传输的直接控制(两者都需要产品最终产品)。

UPDATE: After trying to use multiple generic file operations for each endpoint and getting a response code of -98 (already registered), I think I am going to have to use the single endpoint with ioctl. The code that isn't working is as follows:

更新:尝试为每个端点使用多个通用文件操作并获得-98(已经注册)的响应代码后,我想我将不得不使用ioctl的单个端点。无效的代码如下:

In adriver.h

static struct usb_class_driver adriver_eeprom_class = {
    .name = "usb/adriver_eeprom%d",
    .fops = &adriver_eeprom_fops,
    .minor_base = USB_SKEL_MINOR_BASE,
};
static struct usb_class_driver adriver_flash_class = {
    .name = "usb/adriver_flash%d",
    .fops = &adriver_flash_fops,
    .minor_base = USB_SKEL_MINOR_BASE,
};
static struct usb_class_driver adriver_i2c_class = {
    .name = "usb/adriver_i2c%d",
    .fops = &adriver_i2c_fops,
    .minor_base = USB_SKEL_MINOR_BASE,
};
static struct usb_class_driver driver_fifo_class = {
    .name = "usb/driver_fifo%d",
    .fops = &driver_fifo_fops,
    .minor_base = USB_SKEL_MINOR_BASE,
};
static struct usb_class_driver adriver_class = {
    .name = "usb/adriver%d",
    .fops = &adriver_fops,
    .minor_base = USB_SKEL_MINOR_BASE,
};

In adriver.c

static int adriver_probe(struct usb_interface *interface, const struct usb_device_id *id) {
    struct usb_device *udev = interface_to_usbdev(interface);
    struct usb_adriver *gdev;
    int retval = -ENOMEM;
    gdev = kmalloc(sizeof(struct usb_adriver), GFP_KERNEL);
    if(gdev == NULL)
    {
        dev_err(&interface->dev, "Out of memory\n");
        goto error;
    }
    memset(gdev, 0x00, sizeof(*gdev));

    kref_init(&gdev->kref);

    gdev->udev = usb_get_dev(udev);

    usb_set_intfdata(interface,gdev);

    retval = usb_register_dev(interface, &adriver_eeprom_class);
    if (retval) {
        /* something prevented us from registering this driver */
        pr_err("Not able to get a minor for this device.");
        usb_set_intfdata(interface, NULL);
        goto error;
    }
    retval = usb_register_dev(interface, &adriver_flash_class);
    if (retval) {
        /* something prevented us from registering this driver */
        pr_err("Not able to get a minor for this device.");
        usb_set_intfdata(interface, NULL);
        goto error;
    }
    retval = usb_register_dev(interface, &adriver_i2c_class);
    if (retval) {
        /* something prevented us from registering this driver */
        pr_err("Not able to get a minor for this device.");
        usb_set_intfdata(interface, NULL);
        goto error;
    }
    retval = usb_register_dev(interface, &adriver_fifo_class);
    if (retval) {
        /* something prevented us from registering this driver */
        pr_err("Not able to get a minor for this device.");
        usb_set_intfdata(interface, NULL);
        goto error;
    }
    retval = usb_register_dev(interface, &adriver_class);
    if (retval) {
        /* something prevented us from registering this driver */
        pr_err("Not able to get a minor for this device.");
        usb_set_intfdata(interface, NULL);
        goto error;
    }

    dev_info(&interface->dev, "USB adriver device now attached\n");
    return 0;

error:
    if (gdev)
        kref_put(&gdev->kref, adriver_delete);
    return retval;
}

2 个解决方案

#1


1  

The device driver can create 3 devices as you originally proposed. If there is a single IRQ on the device, this model is even more apropos.

设备驱动程序可以像您最初提出的那样创建3个设备。如果设备上只有一个IRQ,那么这个模型就更适合了。

With a bit of luck and skill (probably more of the latter), the driver's read and write routines need be implemented only as one function for read and one for write with an extra parameter passed, or the read/write routine infers which device it is by inspecting its struct file * parameter (if named f, then MINOR(f -> f_dentry -> d_inode -> i_rdev) gives the device's minor device I.D. Since you control minor device assignment in your probe() function with device_create(), you can leverage that to have useful type information associated.

有了一点运气和技巧(可能更多的是后者),驱动程序的读写例程只需要作为一个读取函数实现,一个用于写入并传递额外参数,或者读/写例程推断出哪个设备是通过检查其struct file *参数(如果命名为f,则MINOR(f - > f_dentry - > d_inode - > i_rdev)给出设备的次要设备ID因为您使用device_create()控制probe()函数中的次要设备分配,您可以利用它来关联有用的类型信息。

In this way, it is easy to avoid ioctl which really should be avoided for simple read and write operations. That makes it easy to use the device from bash scripts, command line, etc. If an ioctl is involved, it means a programming language is required to use it.
The man page for ioctl() says

通过这种方式,很容易避免ioctl,这对于简单的读写操作确实应该避免。这使得从bash脚本,命令行等使用设备变得容易。如果涉及ioctl,则意味着需要使用编程语言。 ioctl()的手册页说

The [ioctl] call is used as a catch-all for operations that don't cleanly fit the UNIX stream I/O model.

[ioctl]调用用作不完全适合UNIX流I / O模型的操作的全能。

#2


0  

Thinking outside the box:

外箱思考:

Assuming it is feasible with your device, you could consider writing a user space driver using libusb (old link) (SourceForge) or another user space USB library. Then you can debug easier, develop and test easier, and you gain the added advantage of potential cross-platform compatibility and without needing to deal with writing a kernel driver.

假设您的设备可行,您可以考虑使用libusb(旧链接)(SourceForge)或其他用户空间USB库编写用户空间驱动程序。然后,您可以更轻松地调试,开发和测试,并且您可以获得潜在的跨平台兼容性的额外优势,而无需编写内核驱动程序。

#1


1  

The device driver can create 3 devices as you originally proposed. If there is a single IRQ on the device, this model is even more apropos.

设备驱动程序可以像您最初提出的那样创建3个设备。如果设备上只有一个IRQ,那么这个模型就更适合了。

With a bit of luck and skill (probably more of the latter), the driver's read and write routines need be implemented only as one function for read and one for write with an extra parameter passed, or the read/write routine infers which device it is by inspecting its struct file * parameter (if named f, then MINOR(f -> f_dentry -> d_inode -> i_rdev) gives the device's minor device I.D. Since you control minor device assignment in your probe() function with device_create(), you can leverage that to have useful type information associated.

有了一点运气和技巧(可能更多的是后者),驱动程序的读写例程只需要作为一个读取函数实现,一个用于写入并传递额外参数,或者读/写例程推断出哪个设备是通过检查其struct file *参数(如果命名为f,则MINOR(f - > f_dentry - > d_inode - > i_rdev)给出设备的次要设备ID因为您使用device_create()控制probe()函数中的次要设备分配,您可以利用它来关联有用的类型信息。

In this way, it is easy to avoid ioctl which really should be avoided for simple read and write operations. That makes it easy to use the device from bash scripts, command line, etc. If an ioctl is involved, it means a programming language is required to use it.
The man page for ioctl() says

通过这种方式,很容易避免ioctl,这对于简单的读写操作确实应该避免。这使得从bash脚本,命令行等使用设备变得容易。如果涉及ioctl,则意味着需要使用编程语言。 ioctl()的手册页说

The [ioctl] call is used as a catch-all for operations that don't cleanly fit the UNIX stream I/O model.

[ioctl]调用用作不完全适合UNIX流I / O模型的操作的全能。

#2


0  

Thinking outside the box:

外箱思考:

Assuming it is feasible with your device, you could consider writing a user space driver using libusb (old link) (SourceForge) or another user space USB library. Then you can debug easier, develop and test easier, and you gain the added advantage of potential cross-platform compatibility and without needing to deal with writing a kernel driver.

假设您的设备可行,您可以考虑使用libusb(旧链接)(SourceForge)或其他用户空间USB库编写用户空间驱动程序。然后,您可以更轻松地调试,开发和测试,并且您可以获得潜在的跨平台兼容性的额外优势,而无需编写内核驱动程序。