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

问题描述:

我正在为有三种不同读/写操作(闪存,EEPROM和I2C)的USB设备编写驱动程序,每种设备都有不同的实现。我一直在为此挠头,因为我对整个Linux内核开发领域都很陌生。我读过,我应该不惜一切代价避免ioctl,但我不知道如何实现这一点。由于linux中的所有内容都是文件,我可以创建多个端点来写入每个位置吗?我怎么会这样做呢?我会定义多个usb_class_driver结构吗?具有多个读取操作,ioctl或fops的Linux USB驱动程序?

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

由于对等时传输的限制以及缺少对dma传输的直接控制(两者都需要产品最终产品),我不能使用libusb。

UPDATE: 尝试使用多种通用的文件操作为每个端点和获得的-98响应代码(已注册),我想我将不得不使用单一端点的ioctl之后。不工作的代码如下:

在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, 
}; 

在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; 
} 

外箱思考:

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

+0

不幸的是,由于需要同步urb传输和DMA控制,我不能使用libusb(x)。 – Nielsvh

设备驱动程序可以按照您最初的建议创建3个设备。如果设备上有单个IRQ,则该模型甚至更适合。

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

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

的[IOCTL]客服用作捕捉所有的那些不干净符合UNIX流I/O模式操作。