编写没有usbfs的USB驱动程序

时间:2013-05-05 08:18:05

标签: usb linux-device-driver avr

我正在学习Linux设备驱动程序编程 - 特别是USB驱动程序。 USB驱动程序的所有教程都需要usbfs,它已在较新的内核中被禁用。解决方案,我希望有人在这里向我证实,是在启用usbfs的情况下构建内核。

首先,我正在寻找原因 - 为什么usbfs被禁用了?其中一个人模糊地提到了udev的一些问题。

其次,有没有办法在没有usbfs的情况下编写驱动程序?如果它被禁用,开发人员会提供一些替代方案(这是我猜测)。

继承我的代码......

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

代码用于操纵LDDK套件上的LED - 基于AVR的电路板。 它无法在/ dev / usb /中创建设备文件(首先没有名为usb的目录!)

赞赏一些阅读材料的链接。谢谢。

2 个答案:

答案 0 :(得分:1)

例如谷歌“编写一个usb内核驱动程序”。会给你this之类的链接。祝你好运!

答案 1 :(得分:1)

关于你的第二个问题 - linux usb驱动程序最重要的部分(最低级别)是用内核空间而不是用户空间编写的。在内核空间中,软件直接与硬件对话。

Usbfs只是用户空间程序/库(如libusb)的某种接口。