Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15497101
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类: LINUX

2008-05-27 17:47:14

linux 2.6下编译usb驱动和arm板进行数据通信

文件原型drivers/usb/misc/usblcd.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include <linux/usb.h>

#define DRIVER_VERSION "USBLCD Driver Version 1.05"

#define USBLCD_MINOR        144

#define IOCTL_GET_HARD_VERSION    1
#define IOCTL_GET_DRV_VERSION    2


static struct usb_device_id id_table [] = {
    { .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, },
    { .idVendor = 0x8086, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, },//gliethttp_20080527添加自己的vid
    { .idVendor = 0x1286, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, },//gliethttp_20080527添加自己的vid
    { },
};
MODULE_DEVICE_TABLE (usb, id_table);


struct usb_lcd {
    struct usb_device *    udev;            /* init: probe_lcd */
    struct usb_interface * interface;        /* the interface for this device */
    unsigned char * bulk_in_buffer;        /* the buffer to receive data */
    size_t            bulk_in_size;        /* the size of the receive buffer */
    __u8            bulk_in_endpointAddr;    /* the address of the bulk in endpoint */
    __u8            bulk_out_endpointAddr;    /* the address of the bulk out endpoint */
    struct kref        kref;
    struct semaphore    limit_sem;        /* to stop writes at full throttle from
                             * using up all RAM */

};
#define to_lcd_dev(d) container_of(d, struct usb_lcd, kref)

#define USB_LCD_CONCURRENT_WRITES    1 //gliethttp_20080527同时允许1个task控制写函数,其他task将阻塞

static struct usb_driver lcd_driver;
static DEFINE_MUTEX(usb_lcd_open_mutex);


static void lcd_delete(struct kref *kref)
{
    struct usb_lcd *dev = to_lcd_dev(kref);

    usb_put_dev(dev->udev);
    kfree (dev->bulk_in_buffer);
    kfree (dev);
}


static int lcd_open(struct inode *inode, struct file *file)
{
    struct usb_lcd *dev;
    struct usb_interface *interface;
    int subminor;
    int retval = 0;

    subminor = iminor(inode);

    mutex_lock(&usb_lcd_open_mutex);
    interface = usb_find_interface(&lcd_driver, subminor);
    if (!interface) {
        err ("USBLCD: %s - error, can't find device for minor %d",
         __FUNCTION__, subminor);
        retval = -ENODEV;
        goto exit;
    }

    dev = usb_get_intfdata(interface);
    if (!dev) {
        retval = -ENODEV;
        goto exit;
    }

    /* increment our usage count for the device */
    kref_get(&dev->kref);

    /* save our object in the file's private structure */
    file->private_data = dev;

exit:
    mutex_unlock(&usb_lcd_open_mutex);
    return retval;
}

static int lcd_release(struct inode *inode, struct file *file)
{
    struct usb_lcd *dev;

    dev = (struct usb_lcd *)file->private_data;
    if (dev == NULL)
        return -ENODEV;

    /* decrement the count on our device */
    kref_put(&dev->kref, lcd_delete);
    return 0;
}

static ssize_t lcd_read(struct file *file, char __user * buffer, size_t count, loff_t *ppos)
{
    struct usb_lcd *dev;
    int retval = 0;
    int bytes_read;

    dev = (struct usb_lcd *)file->private_data;

    /* do a blocking bulk read to get data from the device */
    retval = usb_bulk_msg(dev->udev,
             usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
             dev->bulk_in_buffer,
             min(dev->bulk_in_size, count),
             &bytes_read, 30*60*1000);//gliethttp_20080527 修改超时时间
    /* if the read was successful, copy the data to userspace */
    if (!retval) {
        if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read))
            retval = -EFAULT;
        else
            retval = bytes_read;
    }

    return retval;
}

static int lcd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
    struct usb_lcd *dev;
    u16 bcdDevice;
    char buf[30];

    dev = (struct usb_lcd *)file->private_data;
    if (dev == NULL)
        return -ENODEV;
    
    switch (cmd) {
    case IOCTL_GET_HARD_VERSION:
        bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice);
        sprintf(buf,"%1d%1d.%1d%1d",
            (bcdDevice & 0xF000)>>12,
            (bcdDevice & 0xF00)>>8,
            (bcdDevice & 0xF0)>>4,
            (bcdDevice & 0xF));
        if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0)
            return -EFAULT;
        break;
    case IOCTL_GET_DRV_VERSION:
        sprintf(buf,DRIVER_VERSION);
        if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0)
            return -EFAULT;
        break;
    default:
        return -ENOTTY;
        break;
    }

    return 0;
}

static void lcd_write_bulk_callback(struct urb *urb)
{
    struct usb_lcd *dev;

    dev = (struct usb_lcd *)urb->context;

    /* sync/async unlink faults aren't errors */
    if (urb->status &&
     !(urb->status == -ENOENT ||
     urb->status == -ECONNRESET ||
              urb->status == -ESHUTDOWN)) {
        dbg("USBLCD: %s - nonzero write bulk status received: %d",
         __FUNCTION__, urb->status);
    }

    /* free up our allocated buffer */
    usb_buffer_free(urb->dev, urb->transfer_buffer_length,
            urb->transfer_buffer, urb->transfer_dma);
    up(&dev->limit_sem);
}

static ssize_t lcd_write(struct file *file, const char __user * user_buffer, size_t count, loff_t *ppos)
{
    struct usb_lcd *dev;
        int retval = 0, r;
    struct urb *urb = NULL;
    char *buf = NULL;
    
    dev = (struct usb_lcd *)file->private_data;
    
    /* verify that we actually have some data to write */
    if (count == 0)
        goto exit;

    r = down_interruptible(&dev->limit_sem);
    if (r < 0)
        return -EINTR;

    /* create a urb, and a buffer for it, and copy the data to the urb */
    urb = usb_alloc_urb(0, GFP_KERNEL);
    if (!urb) {
        retval = -ENOMEM;
        goto err_no_buf;
    }
    
    buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
    if (!buf) {
        retval = -ENOMEM;
        goto error;
    }
    
    if (copy_from_user(buf, user_buffer, count)) {
        retval = -EFAULT;
        goto error;
    }
    
    /* initialize the urb properly */
    usb_fill_bulk_urb(urb, dev->udev,
             usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
             buf, count, lcd_write_bulk_callback, dev);
    urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
    
    /* send the data out the bulk port */
    retval = usb_submit_urb(urb, GFP_KERNEL);
    if (retval) {
        err("USBLCD: %s - failed submitting write urb, error %d", __FUNCTION__, retval);
        goto error;
    }
    
    /* release our reference to this urb, the USB core will eventually free it entirely */
    usb_free_urb(urb);

    r = down_interruptible(&dev->limit_sem);//等待,直到数据完全发送完毕
    up(&dev->limit_sem);

//  mdelay(10);


exit:
    return count;

error:
    usb_buffer_free(dev->udev, count, buf, urb->transfer_dma);
    usb_free_urb(urb);
err_no_buf:
    up(&dev->limit_sem);
    return retval;
}

static const struct file_operations lcd_fops = {
        .owner = THIS_MODULE,
        .read = lcd_read,
        .write = lcd_write,
        .open = lcd_open,
        .ioctl = lcd_ioctl,
        .release = lcd_release,
};

/*
 * usb class driver info in order to get a minor number from the usb core,
 * and to have the device registered with the driver core
 */

static struct usb_class_driver lcd_class = {
        .name = "gliethttp_flash%d",
        .fops = &lcd_fops,
        .minor_base = USBLCD_MINOR,
};

static int lcd_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
    struct usb_lcd *dev = NULL;
    struct usb_host_interface *iface_desc;
    struct usb_endpoint_descriptor *endpoint;
    size_t buffer_size;
    int i;
    int retval = -ENOMEM;

    /* allocate memory for our device state and initialize it */
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    if (dev == NULL) {
        err("Out of memory");
        goto error;
    }
    kref_init(&dev->kref);
    sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES);

    dev->udev = usb_get_dev(interface_to_usbdev(interface));
    dev->interface = interface;

/*
    //gliethttp_20080527
    if (le16_to_cpu(dev->udev->descriptor.idProduct) != 0x0001) {
        warn(KERN_INFO "USBLCD model not supported.");
        return -ENODEV;
    }
*/

    
    /* set up the endpoint information */
    /* use only the first bulk-in and bulk-out endpoints */
    iface_desc = interface->cur_altsetting;
    for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
        endpoint = &iface_desc->endpoint[i].desc;

        if (!dev->bulk_in_endpointAddr &&
         usb_endpoint_is_bulk_in(endpoint)) {
            /* we found a bulk in endpoint */
            buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
            dev->bulk_in_size = buffer_size;
            dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
            dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
            if (!dev->bulk_in_buffer) {
                err("Could not allocate bulk_in_buffer");
                goto error;
            }
        }

        if (!dev->bulk_out_endpointAddr &&
         usb_endpoint_is_bulk_out(endpoint)) {
            /* we found a bulk out endpoint */
            dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
        }
    }
    if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
        err("Could not find both bulk-in and bulk-out endpoints");
        goto error;
    }

    /* save our data pointer in this interface device */
    usb_set_intfdata(interface, dev);

    /* we can register the device now, as it is ready */
    retval = usb_register_dev(interface, &lcd_class);
    if (retval) {
        /* something prevented us from registering this driver */
        err("Not able to get a minor for this device.");
        usb_set_intfdata(interface, NULL);
        goto error;
    }

    i = le16_to_cpu(dev->udev->descriptor.bcdDevice);

    info("USBLCD Version %1d%1d.%1d%1d found at address %d",
        (i & 0xF000)>>12,(i & 0xF00)>>8,(i & 0xF0)>>4,(i & 0xF),
        dev->udev->devnum);

    /* let the user know what node this device is now attached to */
    info("USB LCD device now attached to USBLCD-%d", interface->minor);
    return 0;

error:
    if (dev)
        kref_put(&dev->kref, lcd_delete);
    return retval;
}

static void lcd_disconnect(struct usb_interface *interface)
{
    struct usb_lcd *dev;
        int minor = interface->minor;

        /* prevent skel_open() from racing skel_disconnect() */
        mutex_lock(&usb_lcd_open_mutex);

        dev = usb_get_intfdata(interface);
        usb_set_intfdata(interface, NULL);

        /* give back our minor */
        usb_deregister_dev(interface, &lcd_class);
 
    mutex_unlock(&usb_lcd_open_mutex);

    /* decrement our usage count */
    kref_put(&dev->kref, lcd_delete);

    info("USB LCD #%d now disconnected", minor);
}

static struct usb_driver lcd_driver = {
    .name =        "usblcd",
    .probe =    lcd_probe,
    .disconnect =    lcd_disconnect,
    .id_table =    id_table,
};

static int __init usb_lcd_init(void)
{
    int result;
    
    result = usb_register(&lcd_driver);
    if (result)
        err("usb_register failed. Error number %d", result);

    return result;
}


static void __exit usb_lcd_exit(void)
{
    usb_deregister(&lcd_driver);
}

module_init(usb_lcd_init);
module_exit(usb_lcd_exit);

MODULE_AUTHOR("Georges Toth ");
MODULE_DESCRIPTION(DRIVER_VERSION);
MODULE_LICENSE("GPL");


luther@gliethttp:~/work/kernel/module_drivers/gliethttp_flash$ echo "obj-m:=gliethttp_flash.o" > Makefile


luther@gliethttp:~/work/kernel/module_drivers/gliethttp_flash$ make -C /lib/modules/`uname -r`/build M=`pwd` modules
make: Entering directory `/usr/src/linux-headers-2.6.22-14-generic'
  CC [M] /home/luther/work/kernel/module_drivers/gliethttp_flash/gliethttp_flash.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC /home/luther/work/kernel/module_drivers/gliethttp_flash/gliethttp_flash.mod.o
  LD [M] /home/luther/work/kernel/module_drivers/gliethttp_flash/gliethttp_flash.ko
make: Leaving directory `/usr/src/linux-headers-2.6.22-14-generic'

luther@gliethttp:~/work/kernel/module_drivers/gliethttp_flash$


luther@gliethttp:~/work/kernel/module_drivers/gliethttp_flash$ ls /dev/gliethttp_flash0
/dev/gliethttp_flash0
luther@gliethttp:~/work/kernel/module_drivers/gliethttp_flash$

但是和libusb-0.1.12一样,
Intel处理器(DELL OptiPlex 330) 不支持AMD处理器(DELL OptiPlex 740)主板上ubuntu出现不稳定现象,在
AMD处理器(DELL OptiPlex 740) 不支持Intel处理器(DELL OptiPlex 330)主板上表现出色,
如果这样来看的话,只能说ubuntu在DELL OptiPlex 330主板usb驱动模块不稳定了,但是
在DELL OptiPlex 330上对u盘进行数据操作一切正常,具体不清楚到底是咋回事,唉!!!

[注:很有可能DELL OptiPlex 330上usb数据传输速度太快,下面arm板上的驱动对于快速数据传输反应不过来,但是加入mdelay(10)进行10ms延时之后,仍然出现同样的问题,所以80%问题是ubuntu在DELL OptiPlex 330主板上不稳定]


ps:可以肯定的是pc确实已经把通信数据发送下去了,在调用read的时候,一直不能等到设备返回应该返回的状态信息,协议上可能不存在丢包后数据重发 机制,所以可能确实是因为pc下发的数据包丢失了,所以设备端因为没有收到数据,以及校验包完整性措施,进而,不能和tcp/ip数据传输那样有很好的容 错能力,看来marvell设计的这个通信不成熟阿,
所以在协议设计上,对丢包处理、数据干扰后的校验重传机制等对任何产品来说不是必要的,而是必须的,所以文件切包之后进行包序号标记是很有意义的,可以参考成熟的tcp/ip数据传输、校验、重传等方式![gliethttp_20080529]
ps: 所以从这里来看,所谓的设备通信不稳定,就是在于设备对丢包和误码处理不完善,虽然bulk类型端口的通信能够保证数据传输正确性,但是不能保证因为线路 干扰严重或者系统自身的问题超过bulk内定的重试次数之后引起的丢包现象![gliethttp_20080529]


应用程序:仅供参考


int usb_fd = 0;
char * DeviceName = "/dev/gliethttp_flash0";

//#define USBDEBUG


#ifdef USBDEBUG
int    dbg_level = 1;
#define usb_dbg(args...)    \
    do {            \
        printf(args);    \    
    }while(0)
#else
int    dbg_level = 0;
#define usb_dbg(args...)    \
    do {            \
    }while(0)
#endif

void dbg_usbStatus(WTPSTATUS *WtpStatus)
{
#ifdef USBDEBUG
    int i;

    if (WtpStatus->DLEN > 0)
    {
    for (i=0; i< WtpStatus->DLEN; i++)
     printf("data 0x%2x-----", WtpStatus->Data[i]);
    }
    printf ("\n");
    printf ("CMD 0x%2x-----", WtpStatus->CMD);
    printf ("SEQ 0x%2x-----", WtpStatus->SEQ);
    printf ("CID 0x%2x-----", WtpStatus->CID);
    printf ("Status 0x%2x----- ", WtpStatus->Status);
    printf ("Flags 0x%2x-----", WtpStatus->Flags);
    printf ("DLEN 0x%2x-----", WtpStatus->DLEN);
    printf ("\n");
#endif
}



BOOL OpenUsbPort (long int DelayTime)
{
    char Message[100];
    unsigned long int Seconds, count=100;
    BOOL DeviceFound = FALSE,MessageEnable = TRUE;
    struct termios options, old_options;
    int file;

    file = open(DeviceName, O_RDWR);
    if (file < 0)
    {
        printf("ERROR: Open driver failed:%d \n", errno);
        return FALSE;
    }

    usb_fd = file;
    usb_dbg("%s end, usb_fd %d\n", __func__, usb_fd);
    return TRUE;
}

BOOL WriteUsb (WTPCOMMAND WtpCmd,DWORD dwCmdSize)
{
    char Message[100];
    int result;
    DWORD IOCTL_CMD, dwActualBytes = 0;
    BOOL IsOk;

    if (usb_fd == 0)
    {
        sprintf (Message," Error: OpenUsbPort must be called prior to calling WriteUsb!");
        usb_dbg("%s %d USB COMMUNICATIONS ERROR\n", __func__, __LINE__);
        return FALSE;
    }

    result = write( usb_fd, &WtpCmd, dwCmdSize);
    if (result < 0)
    {
     sprintf (Message," Error: WriteUsb failed with error %d", result);
     usb_dbg("%s %d USB COMMUNICATIONS ERROR\n", __func__, __LINE__);
    }

    if (dbg_level)
    {
        sprintf (Message,"IOCTL code: %X",IOCTL_CMD);
        usb_dbg("%s %d USB write %d bytes\n", __func__, __LINE__, result);
    }

    return TRUE;
}

BOOL ReadUsb(WTPSTATUS *WtpStatus, DWORD dwSize, PDWORD pBytesRead)
{
    int result;
    
    usb_dbg("%s %d USB read %d bytes\n", __func__, __LINE__, dwSize);

    result = read(usb_fd, WtpStatus, dwSize);
    if (result <= 0)
    {
#if ___output_log__
     printf("%s return %d\n", __func__, result);
#endif
     return FALSE;
    }
    
    dbg_usbStatus(WtpStatus);
    
    *pBytesRead = result;
    return TRUE;
        
}

BOOL ReadUsbCharBuffer (char *WtpMessage,DWORD dwSize,PDWORD pBytesRead)
{
    int result;

    if (usb_fd < 0)
    {
        printf (" Error: OpenUsbPort must be called prior to calling ReadUsb!");
        return FALSE;
    }
    result = read(usb_fd, WtpMessage, dwSize);
    if (result < 0 )
    {
        printf (" Error: ReadFile failed with error\n");
        return FALSE;
    }
    
    *pBytesRead = result;
    if (*pBytesRead != dwSize) return FALSE;
    
    return TRUE;
}

void CloseUsbPort ()
{
    if (usb_fd != 0) close (usb_fd);
}

阅读(2899) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~