Escribir controladores USB sin usbfs

I am learning Linux Device Driver programming - specifically USB drivers. All the tutorials for USB drivers require usbfs, which has been disabled in the newer kernels. Solution, which I hope someone here confirms for me, was to build the kernel with usbfs enabled.

Firstly, I am looking for the reason - as why usbfs has been disabled? One of the guys vaguely mentioned about some issues with the udev.

Secondly, is there a way to write drivers without usbfs? If it has been disabled, the developers would have provided some alternative (this is me guessing).

Aquí está mi código ...

ddk.h

#ifndef DDK_H
#define DDK_H

#ifdef __KERNEL__
#include <linux/usb.h>

#define DDK_VENDOR_ID 0x16c0
#define DDK_PRODUCT_ID 0x05dc

#define ENABLE_FILE_OPS
//#define ENABLE_USB_DEV // TODO: Debug enabling this
/* Crashing possibly because udev is doing something on its vendor, device id match */

#define CUSTOM_RQ_SET_LED_STATUS    1
#define CUSTOM_RQ_GET_LED_STATUS    2
#define CUSTOM_RQ_SET_MEM_RD_OFFSET 3
#define CUSTOM_RQ_GET_MEM_RD_OFFSET 4
#define CUSTOM_RQ_SET_MEM_WR_OFFSET 5
#define CUSTOM_RQ_GET_MEM_WR_OFFSET 6
#define CUSTOM_RQ_GET_MEM_SIZE      7

#define MAX_ENDPOINTS 4

#define MEM_EP_IN (USB_DIR_IN | 0x01)
#define MEM_EP_OUT 0x01
#define SER_EP_IN (USB_DIR_IN | 0x02)
#define SER_EP_OUT 0x02
#endif

#endif

ddk_led.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/usb.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>

#include "ddk_led.h"

static struct ddk_device ddk_dev; // Need to be persistent

#ifdef ENABLE_FILE_OPS
static dev_t dev;
static struct cdev c_dev;
static struct class *cl;

static int ddk_open(struct inode *i, struct file *f)
{
    return 0;
}
static int ddk_close(struct inode *i, struct file *f)
{
    return 0;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35))
static int ddk_ioctl(struct inode *i, struct file *f, unsigned int cmd, unsigned long arg)
#else
static long ddk_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
#endif
{
    char buf;
    int val;
    int retval;

    switch (cmd)
    {
        case DDK_LED_GET:
            /* Control IN */
            retval = usb_control_msg(ddk_dev.device, usb_rcvctrlpipe(ddk_dev.device, 0),
                        CUSTOM_RQ_GET_LED_STATUS, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                        0, 0, &buf, sizeof(buf), 0);
            if (retval < 0)
            {
                printk(KERN_ERR "Control message returned %d\n", retval);
                return retval;
            }
            val = buf;
            if (copy_to_user((int *)arg, &val, sizeof(int)))
            {
                return -EFAULT;
            }
            break;
        case DDK_LED_SET:
            /* Control OUT */
            val = arg;
            retval = usb_control_msg(ddk_dev.device, usb_sndctrlpipe(ddk_dev.device, 0),
                        CUSTOM_RQ_SET_LED_STATUS, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                        val, 0, NULL, 0, 0);
            if (retval < 0)
            {
                printk(KERN_ERR "Control message returned %d\n", retval);
                return retval;
            }
            break;
        default:
            return -EINVAL;
            break;
    }
    return 0;
}

static struct file_operations fops =
{
    .open = ddk_open,
    .release = ddk_close,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35))
    .ioctl = ddk_ioctl,
#else
    .unlocked_ioctl = ddk_ioctl,
#endif
};

static int char_register_dev(struct usb_interface *interface, struct usb_class_driver *class)
{
    int ret;
    struct device *dev_ret;

    if ((ret = alloc_chrdev_region(&dev, 0, 1, "ddk_led")) < 0)
    {
        printk(KERN_INFO "Error retrieving MAj Min numbers\n");
        return ret;
    }
    printk(KERN_INFO "(Major, Minor): (%d, %d)\n", MAJOR(dev), MINOR(dev));
    interface->minor = MINOR(dev);

    cdev_init(&c_dev, class->fops);
    if ((ret = cdev_add(&c_dev, dev, 1)) < 0)
    {
        return ret;
    }

    if (IS_ERR(cl = class_create(THIS_MODULE, "usb")))
    {
        cdev_del(&c_dev);
        unregister_chrdev_region(dev, 1);
        return PTR_ERR(cl);
    }
    if (IS_ERR(dev_ret = device_create(cl, NULL, dev, NULL, class->name, 0)))
    {
        class_destroy(cl);
        cdev_del(&c_dev);
        unregister_chrdev_region(dev, 1);
        return PTR_ERR(dev_ret);
    }
    return 0;
}

static void char_deregister_dev(struct usb_interface *interface, struct usb_class_driver *class)
{
    device_destroy(cl, dev);
    class_destroy(cl);
    cdev_del(&c_dev);
    unregister_chrdev_region(dev, 1);
}
#endif

static int ddk_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
    struct usb_host_interface *iface_desc;
    struct usb_endpoint_descriptor *endpoint;
    int i;
    int retval;

    iface_desc = interface->cur_altsetting;
    printk(KERN_INFO "DDK USB i/f %d now probed: (%04X:%04X)\n",
        iface_desc->desc.bInterfaceNumber, id->idVendor, id->idProduct);
    printk(KERN_INFO "ID->bNumEndpoints: %02X\n", iface_desc->desc.bNumEndpoints);
    printk(KERN_INFO "ID->bInterfaceClass: %02X\n", iface_desc->desc.bInterfaceClass);

    /* Set up the endpoint information. Assuming there is 1 in & 1 out */
    memset(&ddk_dev, 0, sizeof(ddk_dev));
    for (i = 0; i < iface_desc->desc.bNumEndpoints; i++)
    {
        endpoint = &iface_desc->endpoint[i].desc;

        printk(KERN_INFO "ED[%d]->bEndpointAddress: 0x%02X\n", i, endpoint->bEndpointAddress);
        printk(KERN_INFO "ED[%d]->bmAttributes: 0x%02X\n", i, endpoint->bmAttributes);
        printk(KERN_INFO "ED[%d]->wMaxPacketSize: 0x%04X (%d)\n", i, endpoint->wMaxPacketSize, endpoint->wMaxPacketSize);
    }

    ddk_dev.device = interface_to_usbdev(interface);

#ifdef ENABLE_FILE_OPS
    ddk_dev.class.name = "usb/ddk_led%d";
    ddk_dev.class.fops = &fops;
    retval = char_register_dev(interface, &ddk_dev.class);
    if (retval)
    {
        /* Something prevented us from registering this driver */
        printk(KERN_ERR "Not able to get a minor for this device.\n");
        return retval;
    }
    else
    {
        printk(KERN_INFO "Minor obtained: %d\n", interface->minor);
    }
#else
    interface->minor = iface_desc->desc.bInterfaceNumber;
#endif

    return 0;
}

static void ddk_disconnect(struct usb_interface *interface)
{
    printk(KERN_INFO "Releasing Minor: %d\n", interface->minor);

#ifdef ENABLE_FILE_OPS
    /* Give back our minor */
    char_deregister_dev(interface, &ddk_dev.class);
#endif

    printk(KERN_INFO "DDK USB i/f %d now disconnected\n",
            interface->cur_altsetting->desc.bInterfaceNumber);
}

/* Table of devices that work with this driver */
static struct usb_device_id ddk_table[] =
{
    {
        USB_DEVICE(DDK_VENDOR_ID, DDK_PRODUCT_ID)
    },
    {} /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, ddk_table);

static struct usb_driver ddk_driver =
{
    .name = "ddk_led",
    .probe = ddk_probe,
    .disconnect = ddk_disconnect,
    .id_table = ddk_table,
};

static int __init ddk_init(void)
{
    int result;

    /* Register this driver with the USB subsystem */
    if ((result = usb_register(&ddk_driver)))
    {
        printk(KERN_ERR "usb_register failed. Error number %d\n", result);
    }
    printk(KERN_INFO "DDK usb_registered\n");
    return result;
}

static void __exit ddk_exit(void)
{
    /* Deregister this driver with the USB subsystem */
    usb_deregister(&ddk_driver);
    printk(KERN_INFO "DDK usb_deregistered\n");
}

module_init(ddk_init);
module_exit(ddk_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <email@sarika-pugs.com>");
MODULE_DESCRIPTION("USB LED Device Driver for DDK v1.1");

ddk_led.h

#ifndef DDK_LED_H
#define DDK_LED_H

#include <linux/ioctl.h>

#ifdef __KERNEL__
#include "ddk.h"

struct ddk_device
{
    struct usb_device *device;
    struct usb_class_driver class;
};
#endif

#define DDK_LED_GET _IOR('u', 1, int *)
#define DDK_LED_SET _IOW('u', 2, int)

#endif

Codes are meant to manipulate the led on a LDDK kit - an AVR based board. It fails to create the device file in /dev/usb/ (no directory called usb in the first place!)

Links to some reading material is appreciated. Thank you.

preguntado el 05 de mayo de 13 a las 09:05

2 Respuestas

Regarding your second question - the most important part (lowest level) of linux usb drivers is written in kernel-space not in user-space. In kernel space software talks directly to hardware.

Usbfs is just some kind of interface to user-space programs/libraries like libusb.

Respondido el 18 de Septiembre de 13 a las 11:09

Google for instance "writing an usb kernel driver". Will give you links like este. ¡Buena suerte!

Respondido el 18 de Septiembre de 13 a las 09:09

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.