USB 驱动程序在接收大量数据时导致 Linux 内核崩溃

USB 驱动程序在接收大量数据时导致 Linux 内核崩溃

我使用的是 Linux ubuntu 5.4.0-1026-raspi。我正在尝试为我的 Cypress CY8CKIT-059 PSoC 5LP 编写自己的 USB 驱动程序。 Cypress PSoC 上的 USB 接口配置为将通过批量输入端点接收到的任何数据发送回通过批量输出端点。基本上这可行,但仅适用于少量数据。魔法屏障大约有2000Bytes左右。当发送超过2000Bytes时整个系统突然崩溃。在系统日志中我找不到任何有用的信息。有人可以向我解释一下这里发生了什么吗?

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/circ_buf.h>
#include <linux/kref.h>
#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/wait.h> /* wait_queue_head_t, wait_event_interruptible, wake_up_interruptible  */

#define DRIVER_NAME "Cypress_USB_Bulk_Transfer_Driver"
#define USB_CYPRESS_VENDOR_ID 0x04B4
#define USB_CYPRESS_PRODUCT_ID 0x8051
#define MIN(a,b) (((a) <= (b)) ? (a) : (b))
#define KB *1024
#define RING_BUFFER_SIZE 8 KB

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cypress USB Bulk Data Transfer Driver");
MODULE_VERSION("1.0");

/* static spinlock */
static DEFINE_SPINLOCK(lock);

/* read waitqueue */
static wait_queue_head_t read_waitqueue;

/* prototypes */
static int cypress_probe(struct usb_interface *intf, const struct usb_device_id *id);
static void cypress_disconnect(struct usb_interface *interface);
static int cypress_open(struct inode *inode, struct file *file);
static int cypress_release(struct inode *inode, struct file *file);
static ssize_t cypress_write(struct file *file, const char __user *buffer, size_t count, loff_t *offset);
static void cypress_write_bulk_callback(struct urb *urb);
static ssize_t cypress_read(struct file *file, char *buffer, size_t count, loff_t *offset);
static unsigned int cypress_poll(struct file *file, struct poll_table_struct *wait);

/* device specific structure */
struct cypress_usb_dev{
    struct usb_device *device;
    struct usb_host_interface *interface;
    /* bulk in */
    size_t bulk_in_size;
    __u8 bulk_in_endpointAddr;
    struct circ_buf bulk_in_ring;
    /*bulk out */
    size_t bulk_out_size;
    __u8 bulk_out_endpointAddr;
    char *bulk_out_buffer;
    /* urbs */
    struct urb *urb_r1, *urb_r2;
    struct usb_anchor   read_urbs, write_urbs;
    /* buffer to receive data */
    void *buffer_urb_in1;
    dma_addr_t dma_buffer_urb_in1;
    bool submitted_urb_r1;
    void *buffer_urb_in2;
    dma_addr_t dma_buffer_urb_in2;
    bool submitted_urb_r2;
    struct kref kref;
};

/* table of devices that work with this driver */
static struct usb_device_id id_table [] = {
        { USB_DEVICE(USB_CYPRESS_VENDOR_ID, USB_CYPRESS_PRODUCT_ID) },
        {},                      /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, id_table);

/* USB driver struct */
static struct usb_driver cypress_driver = {
        .name        = "Cypress USB Bulk Data Transfer driver",
        .probe       = cypress_probe,
        .disconnect  = cypress_disconnect,
        .id_table    = id_table,
};

/* file_operations struct callback for system calls */
static const struct file_operations cypress_fops = {
    .owner =    THIS_MODULE,
    .open =     cypress_open,
    .read =     cypress_read,
    .write =    cypress_write,
    .poll =     cypress_poll,
    .release =  cypress_release,
};

/* used to register for a minor number with the USB subsystem */
static struct usb_class_driver cypress_class = {
    .name =         "usb/cypress_driver%d", /* name shown in sysfs */
    .fops =         &cypress_fops,
};

static void cypress_delete(struct kref *kref)
{
    struct cypress_usb_dev *cyp = container_of(kref, struct cypress_usb_dev, kref);

    wake_up(&read_waitqueue);
    /* free device memory */
    usb_kill_anchored_urbs(&cyp->read_urbs);
    //usb_kill_urb(cyp->urb_r1);
    //usb_kill_urb(cyp->urb_r2);
    usb_kill_anchored_urbs(&cyp->write_urbs);
    usb_free_coherent(cyp->device, cyp->bulk_in_size, cyp->buffer_urb_in1, cyp->dma_buffer_urb_in1);
    usb_free_coherent(cyp->device, cyp->bulk_in_size, cyp->buffer_urb_in2, cyp->dma_buffer_urb_in2);
    kfree(cyp->bulk_in_ring.buf);
    kfree(cyp);
}

/* probe function is called on device insertation through the USB core */
static int cypress_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
    struct cypress_usb_dev *cyp;
    struct usb_device *device;
    struct usb_host_interface *intf_desc;
    struct usb_endpoint_descriptor *endpoint;
    int i, minor, retval;

    /* allocate memory for the device */
    cyp = kzalloc(sizeof(struct cypress_usb_dev), GFP_KERNEL);
    if(cyp == NULL)
    {
        printk(KERN_ALERT "%s Failed to allocate memory for the device.", DRIVER_NAME);
        retval = -ENOMEM;
        goto error;
    }

    kref_init(&cyp->kref);
    init_usb_anchor(&cyp->read_urbs);
    init_usb_anchor(&cyp->write_urbs);
    init_waitqueue_head(&read_waitqueue);

    /* get container device from interface pointer */
    device = interface_to_usbdev(interface);
    cyp->device = device;

    /* get pointer to interface descriptor */
    intf_desc = interface->cur_altsetting;
    cyp->interface = intf_desc;

    printk(KERN_INFO "%s [*] Cypress USB BULK Transfer device detected (Vendor:%04X Product:%04X)", DRIVER_NAME, id->idVendor, id->idProduct);
    printk(KERN_INFO "%s     Interface %d with class %02X and %d endpoints now probed", \
           DRIVER_NAME, intf_desc->desc.bInterfaceNumber, intf_desc->desc.bInterfaceClass, intf_desc->desc.bNumEndpoints);

    /* print all available endpoints */
    for(i=0; i<intf_desc->desc.bNumEndpoints; i++)
    {
        /* get pointer to endpoint */
        endpoint = &intf_desc->endpoint[i].desc;
        
        /* print endpoint information */
        printk(KERN_INFO "%s\t\t EP[%d]->bEndpointAddress: 0x%02X", DRIVER_NAME, i, endpoint->bEndpointAddress);
        printk(KERN_INFO "%s\t\t EP[%d]->bDescriptorType: 0x%02X", DRIVER_NAME, i, endpoint->bDescriptorType);
        printk(KERN_INFO "%s\t\t EP[%d]->wMaxPacketSize: 0x%04X (%d)", DRIVER_NAME, i, endpoint->wMaxPacketSize, endpoint->wMaxPacketSize);
    }

    /* check if bulk in endpoint is available in the probed interface */
    if(!usb_find_bulk_in_endpoint(intf_desc, &endpoint))
    {
        /* store endpoint information */
        cyp->bulk_in_size = endpoint->wMaxPacketSize;
        cyp->bulk_in_endpointAddr = endpoint->bEndpointAddress;
        printk(KERN_INFO "%s     Found suitable bulk in endpoint in interface", DRIVER_NAME);
    }
    else
    {
        printk(KERN_ALERT "%s Failed to find suitable bulk in endpoint in interface", DRIVER_NAME);
        retval = -ENODEV;
        goto error;
    }

    /* check if bulk out endpoint is available in the probed interface */
    if(!usb_find_bulk_out_endpoint(intf_desc, &endpoint))
    {
        /* store endpoint information */
        cyp->bulk_out_size = endpoint->wMaxPacketSize;
        cyp->bulk_out_endpointAddr = endpoint->bEndpointAddress;
        printk(KERN_INFO "%s     Found suitable bulk out endpoint in interface", DRIVER_NAME);
    }
    else
    {
        printk(KERN_ALERT "%s Failed to find suitable bulk out endpoint in interface", DRIVER_NAME);
        retval = -ENODEV;
        goto error;
    }

    /* register device and ask for a minor number */
    minor = usb_register_dev(interface, &cypress_class);
    if(minor)
    {
        printk(KERN_ALERT "%s Failed to register USB device. error: %d", DRIVER_NAME, minor);
        usb_deregister_dev(interface, &cypress_class);
        retval = minor;
        goto error;
    }
    printk(KERN_INFO "%s     Minor %d obtained for Cypress USB Bulk Transfer device", DRIVER_NAME, interface->minor);

    /* save driver data within the interface */
    usb_set_intfdata(interface, cyp);

    return 0;

    error:
        kref_put(&cyp->kref, cypress_delete);
        return retval;
}

/* callback function for read urb */
static void cypress_read_bulk_callback1(struct urb *urb)
{
    struct cypress_usb_dev *cyp;
    int retval;
    unsigned long flags;

    /* get pointer to device struct */
    cyp = urb->context;
    cyp->submitted_urb_r1 = false;

    printk(KERN_DEBUG "%s URB1 read callback triggered %s", DRIVER_NAME, (char*)urb->transfer_buffer);

    if (urb->status)
    {
        /* device was unlinked do not resubmit urb */
        if(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)
        {
            return;
        }
        printk(KERN_ERR "%s Failed bulk read attemting to resubmit, error: %d", DRIVER_NAME, urb->status);

        /* using spin_lock_irqsave - just because it is always save */
        spin_lock_irqsave(&lock, flags);
        goto resubmit;
    }


    /* using spin_lock_irqsave - just because it is always save */
    spin_lock_irqsave(&lock, flags);

    /* copy data to circular buffer */
    memcpy(&cyp->bulk_in_ring.buf[cyp->bulk_in_ring.head], urb->transfer_buffer, urb->actual_length);

    /* update circular buffer index */
    cyp->bulk_in_ring.head = (cyp->bulk_in_ring.head + urb->actual_length) & (RING_BUFFER_SIZE -1);
    wake_up(&read_waitqueue);
    printk(KERN_NOTICE "%s Head at %d", DRIVER_NAME, cyp->bulk_in_ring.head);

    resubmit:
        /* check if enough space is available in circular buffer */
        if(CIRC_SPACE(cyp->bulk_in_ring.head, cyp->bulk_in_ring.tail, RING_BUFFER_SIZE) < (cyp->urb_r1->transfer_buffer_length + cyp->urb_r2->transfer_buffer_length))
        {
            printk(KERN_NOTICE "%s Bulk in Circular Buffer full", DRIVER_NAME);
            goto exit;
        }
        
        /* resubmit urb if enough space is available in circular buffer */
        if ((retval = usb_submit_urb(urb, GFP_KERNEL))) {
            printk(KERN_ERR "%s Failed to resubmit read URB, error %d", DRIVER_NAME, retval);
            goto exit;
        }
        usb_anchor_urb(urb, &cyp->read_urbs);

        cyp->submitted_urb_r1 = true;

    exit:
        spin_unlock_irqrestore(&lock, flags);

    return;
}

/* callback function for read urb */
static void cypress_read_bulk_callback2(struct urb *urb)
{
    struct cypress_usb_dev *cyp;
    int retval;
    unsigned long flags;

    /* get pointer to device struct */
    cyp = urb->context;
    cyp->submitted_urb_r2 = false;

    printk(KERN_DEBUG "%s URB2 read callback triggered %s", DRIVER_NAME, (char*)urb->transfer_buffer);

    if (urb->status)
    {
        /* device was unlinked do not resubmit urb */
        if(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)
        {
            return;
        }
        printk(KERN_ERR "%s Failed bulk read attemting to resubmit, error: %d", DRIVER_NAME, urb->status);

        /* using spin_lock_irqsave - just because it is always save */
        spin_lock_irqsave(&lock, flags);
        goto resubmit;
    }


    /* using spin_lock_irqsave - just because it is always save */
    spin_lock_irqsave(&lock, flags);

    /* copy data to circular buffer */
    memcpy(&cyp->bulk_in_ring.buf[cyp->bulk_in_ring.head], urb->transfer_buffer, urb->actual_length);

    /* update circular buffer index */
    cyp->bulk_in_ring.head = (cyp->bulk_in_ring.head + urb->actual_length) & (RING_BUFFER_SIZE -1);
    wake_up(&read_waitqueue);
    printk(KERN_NOTICE "%s Head at %d", DRIVER_NAME, cyp->bulk_in_ring.head);

    resubmit:
        /* check if enough space is available in circular buffer */
        if(CIRC_SPACE(cyp->bulk_in_ring.head, cyp->bulk_in_ring.tail, RING_BUFFER_SIZE) < (cyp->urb_r1->transfer_buffer_length + cyp->urb_r2->transfer_buffer_length))
        {
            printk(KERN_NOTICE "%s Bulk in Circular Buffer full", DRIVER_NAME);
            goto exit;
        }
        
        /* resubmit urb if enough space is available in circular buffer */
        if ((retval = usb_submit_urb(urb, GFP_KERNEL))) {
            printk(KERN_ERR "%s Failed to resubmit read URB, error %d", DRIVER_NAME, retval);
            goto exit;
        }
        usb_anchor_urb(urb, &cyp->read_urbs);

        cyp->submitted_urb_r2 = true;

    exit:
        spin_unlock_irqrestore(&lock, flags);

    return;
}

/* callback to open system call */
static int cypress_open(struct inode *inode, struct file *file)
{
    int minor;
    struct usb_interface *interface;
    struct cypress_usb_dev *cyp;
    unsigned int bulk_in_pipe;
    int retval;

    /* get minor number for device file */
    minor = iminor(inode);

    /* get USB interface for device file */
    interface = usb_find_interface(&cypress_driver, minor);
    if(!interface)
    {
        printk(KERN_ERR "%s Failed to find USB device", DRIVER_NAME);
        return -ENODEV;
    }

    /* get device data */
    cyp = usb_get_intfdata(interface);
    if (!cyp) {
        printk(KERN_ERR "%s No device data avialable in the USB device", DRIVER_NAME);
        return -ENODEV;
    }

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

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

    cyp->bulk_in_ring.buf = kzalloc(RING_BUFFER_SIZE, GFP_KERNEL);
    if(cyp->bulk_in_ring.buf == NULL)
    {
        printk(KERN_ALERT "%s Failed to allocate memory for bulk input ring buffer.", DRIVER_NAME);
        retval = -ENOMEM;
        goto error_free_ring;
    }

    /* setup urbs for reading from device */
    /* setup two urbs to ensure, that there is always a urb in the endpoints queue */
    cyp->urb_r1 = usb_alloc_urb(0, GFP_KERNEL);
    cyp->urb_r2 = usb_alloc_urb(0, GFP_KERNEL);
    if (!cyp->urb_r1 || !cyp->urb_r2) {
        printk(KERN_ALERT "%s Failed to allocate memory for URBs", DRIVER_NAME);
        retval = -ENOMEM;
        goto error_free_urb;
    }

    /* allocate dma-consistent buffer */
    cyp->buffer_urb_in1 = usb_alloc_coherent(cyp->device, cyp->bulk_in_size, GFP_KERNEL, &cyp->dma_buffer_urb_in1);
    cyp->buffer_urb_in2 = usb_alloc_coherent(cyp->device, cyp->bulk_in_size, GFP_KERNEL, &cyp->dma_buffer_urb_in2);
    if (!cyp->buffer_urb_in1 || !cyp->buffer_urb_in2) {
        printk(KERN_ALERT "%s Failed to allocate memory for read URBs data buffer", DRIVER_NAME);
        retval = -ENOMEM;
        goto error_free_dma;
    }

    /* create pipe for bulk input*/
    bulk_in_pipe = usb_rcvbulkpipe(cyp->device, cyp->bulk_in_endpointAddr);

    /* initialize the receiving urbs */
    usb_fill_bulk_urb(cyp->urb_r1, cyp->device, bulk_in_pipe, cyp->buffer_urb_in1,\
            cyp->bulk_in_size, cypress_read_bulk_callback1, cyp);
    cyp->urb_r1->transfer_dma = cyp->dma_buffer_urb_in1;
    cyp->urb_r1->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

    usb_fill_bulk_urb(cyp->urb_r2, cyp->device, bulk_in_pipe, cyp->buffer_urb_in2,\
            cyp->bulk_in_size, cypress_read_bulk_callback2, cyp);
    cyp->urb_r2->transfer_dma = cyp->dma_buffer_urb_in2;
    cyp->urb_r2->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

    /* check if enough space is available in circular buffer */
    if(CIRC_SPACE(cyp->bulk_in_ring.head, cyp->bulk_in_ring.tail, RING_BUFFER_SIZE) < (cyp->urb_r1->transfer_buffer_length + cyp->urb_r1->transfer_buffer_length))
    {
        printk(KERN_ERR "%s Bulk in Circular Buffer too small", DRIVER_NAME);
        retval = -ENOMEM;
        goto error_free_dma;
    }

    /* submit urb */
    if ((retval = usb_submit_urb(cyp->urb_r1, GFP_KERNEL)) || (retval = usb_submit_urb(cyp->urb_r2, GFP_KERNEL))) {
        printk(KERN_ERR "%s Failed to submit read URBs, error %d", DRIVER_NAME, retval);
        goto error_kill_urb;
    }
    
    /* anchor urbs */
    usb_anchor_urb(cyp->urb_r1, &cyp->read_urbs);
    usb_anchor_urb(cyp->urb_r2, &cyp->read_urbs);

    cyp->submitted_urb_r1 = true;
    cyp->submitted_urb_r2 = true;

    return 0;

    /* handle exceptions */
    error_kill_urb:
        usb_kill_urb(cyp->urb_r1);
        usb_kill_urb(cyp->urb_r2);

    error_free_dma:
        usb_free_coherent(cyp->device, cyp->bulk_in_size, cyp->buffer_urb_in1, cyp->dma_buffer_urb_in1);
        usb_free_coherent(cyp->device, cyp->bulk_in_size, cyp->buffer_urb_in2, cyp->dma_buffer_urb_in2);

    error_free_urb:
        usb_free_urb(cyp->urb_r1);
        usb_free_urb(cyp->urb_r2);
    
    error_free_ring:
        kfree(cyp->bulk_in_ring.buf);

    return retval;
}

/* callback function for release system call */
static int cypress_release(struct inode *inode, struct file *file)
{
    struct cypress_usb_dev *cyp;

    cyp = file->private_data;

    if(cyp == NULL) return -ENODEV;

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

/* callback function for write urb */
static void cypress_write_bulk_callback(struct urb *urb)
{
    struct cypress_usb_dev *cyp;

    /* get pointer to device struct */
    cyp = urb->context;

    printk(KERN_DEBUG "%s URB write callback triggered", DRIVER_NAME);

    /* check urb status */
    if (urb->status && !(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN))
    {
        printk(KERN_ERR "%s Failed bulk write, error: %d", DRIVER_NAME, urb->status);
        goto exit;
    }

    printk(KERN_DEBUG "%s URB write was successful", DRIVER_NAME);

    exit:
        /* free allocated buffer */
        usb_free_coherent(urb->dev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma);
        return;
        /* don't need to free urb, this is done automatically */
}

/* callback to write system call */
static ssize_t cypress_write(struct file *file, const char __user *buffer, size_t count, loff_t *offset)
{
    struct urb *urb;
    struct cypress_usb_dev *cyp;
    unsigned int bulk_out_pipe;
    int retval;

    /* verify that there is something to do */
    if(count == 0)  goto exit;

    /* get pointer to device struct */
    cyp = file->private_data;

    /* allocate memory for urb */
    urb = usb_alloc_urb(0, GFP_KERNEL);
    if (!urb) {
        printk(KERN_ALERT "%s Failed to allocate memory for URBs", DRIVER_NAME);
        retval = -ENOMEM;
        goto error;
    }
    
    /* allocate dma-consistent buffer */
    cyp->bulk_out_buffer = usb_alloc_coherent(cyp->device, count, GFP_KERNEL, &urb->transfer_dma);
    if (!cyp->bulk_out_buffer)
    {
        printk(KERN_ALERT "%s Failed to allocate memory for write URBs data buffer", DRIVER_NAME);
        retval = -ENOMEM;
        goto error;
    }

    /* copy data from user space */
    if (copy_from_user(cyp->bulk_out_buffer, buffer, count))
    {
        printk(KERN_ERR "%s Failed to copy data from user space", DRIVER_NAME);
        retval =  -EFAULT;
        goto error;
    }

    /* create pipe for bulk out */
    bulk_out_pipe = usb_sndbulkpipe(cyp->device, cyp->bulk_out_endpointAddr);

    /* initialize the urb */
    usb_fill_bulk_urb(urb, cyp->device, bulk_out_pipe, cyp->bulk_out_buffer, \
                      count, cypress_write_bulk_callback, cyp);
    urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

    /* submit the urb */
    retval = usb_submit_urb(urb, GFP_KERNEL);
    if(retval)
    {
        printk(KERN_ERR "%s Failed to submit write URB, error %d", DRIVER_NAME, retval);
        goto error;
    }
    usb_anchor_urb(urb, &cyp->write_urbs);

    return count;

    error:
        usb_kill_urb(urb);
        usb_free_coherent(cyp->device, count, cyp->bulk_out_buffer, urb->transfer_dma);
        usb_free_urb(urb);
        return retval;
    
    exit:    
        return 0;
}

/* callback to read system call */
static ssize_t cypress_read(struct file *file, char *buffer, size_t count, loff_t *offset)
{
    struct cypress_usb_dev *cyp;
    int retval, circ_cnt, wrote_cnt;
    unsigned long flags;

    /* get pointer to device struct */
    cyp = file->private_data;
    if(cyp == NULL) return -ENODEV;

    /* using spin_lock_irqsave - just because it is always save */
    spin_lock_irqsave(&lock, flags);

    /* read count available in buffer */
    circ_cnt = CIRC_CNT(cyp->bulk_in_ring.head, cyp->bulk_in_ring.tail, RING_BUFFER_SIZE);
    wrote_cnt = MIN(circ_cnt, count);
    printk(KERN_NOTICE "%s Count is %d", DRIVER_NAME, wrote_cnt);

    if(wrote_cnt == 0)
    {
        *buffer = '\0';
        goto exit;
    }

    /* copy data to user space */
    if(copy_to_user((void *)buffer, &cyp->bulk_in_ring.buf[cyp->bulk_in_ring.tail], wrote_cnt))
    {
        printk(KERN_ERR "%s Failed to copy data to user space", DRIVER_NAME);
        retval = -EFAULT;
        goto error;
    }

    /* update circular buffer index */
    cyp->bulk_in_ring.tail = (cyp->bulk_in_ring.tail + wrote_cnt) & (RING_BUFFER_SIZE - 1);
    printk(KERN_NOTICE "%s Tail at %d", DRIVER_NAME, cyp->bulk_in_ring.tail);

    /* resubmit urb1 if necessary */
    if(!cyp->submitted_urb_r1)
    {
        /* check if enough space is available in circular buffer */
        if(CIRC_SPACE(cyp->bulk_in_ring.head, cyp->bulk_in_ring.tail, RING_BUFFER_SIZE) >= (cyp->urb_r1->transfer_buffer_length + cyp->urb_r2->transfer_buffer_length))
        {          
            if ((retval = usb_submit_urb(cyp->urb_r1, GFP_KERNEL))) {
                printk(KERN_ERR "%s Failed to resubmit read URB, error %d", DRIVER_NAME, retval);
                goto error;
            }
            cyp->submitted_urb_r1 = true;
            usb_anchor_urb(cyp->urb_r1, &cyp->read_urbs);
        }
    }

    /* resubmit urb2 if necessary */
    if(!cyp->submitted_urb_r2)
    {
        /* check if enough space is available in circular buffer */
        if(CIRC_SPACE(cyp->bulk_in_ring.head, cyp->bulk_in_ring.tail, RING_BUFFER_SIZE) >= (cyp->urb_r1->transfer_buffer_length + cyp->urb_r2->transfer_buffer_length))
        {
            if ((retval = usb_submit_urb(cyp->urb_r2, GFP_KERNEL))) {
                printk(KERN_ERR "%s Failed to resubmit read URB, error %d", DRIVER_NAME, retval);
                goto error;
            }
            cyp->submitted_urb_r2 = true;
            usb_anchor_urb(cyp->urb_r2, &cyp->read_urbs);
        }
    }

    exit:
        spin_unlock_irqrestore(&lock, flags);
        return wrote_cnt;

    error:
        spin_unlock_irqrestore(&lock, flags);
        return retval;
}

/* callback to open system call */
/* Returing 0 will sleep the kernel until an event happens in the queue */
static unsigned int cypress_poll(struct file *file, struct poll_table_struct *wait)
{
    struct cypress_usb_dev *cyp;
    int circ_cnt;
    unsigned long flags, retval;

    /* get pointer to device struct */
    cyp = file->private_data;
    if(cyp == NULL) return POLLERR;

    /* this call does not sleep the kernel - it just makes the kernel call poll again if we return 0 */
    poll_wait(file, &read_waitqueue, wait);

    /* using spin_lock_irqsave - just because it is always save */
    spin_lock_irqsave(&lock, flags);

    /* read count available in buffer */
    circ_cnt = CIRC_CNT(cyp->bulk_in_ring.head, cyp->bulk_in_ring.tail, RING_BUFFER_SIZE);
    if(circ_cnt == 0)
    {   
        /* no data to be read from circular buffer */
        retval =  0;   /* return 0 sleeps the kernel */
        goto exit;
    }
    else
    {
        retval = POLLIN | POLLRDNORM;
        goto exit;
    }

    exit:
        spin_unlock_irqrestore(&lock, flags);
        return retval;
}

/* disconnect is called if device is removed */
static void cypress_disconnect(struct usb_interface *interface)
{
    struct cypress_usb_dev *cyp;

    /* get USB interface for device file */
    cyp = usb_get_intfdata(interface);

    /* remove information from interface */
    usb_set_intfdata(interface, NULL);

    /* release minor */
    usb_deregister_dev(interface, &cypress_class);

    /* decrement our usage count */
    kref_put(&cyp->kref, cypress_delete);

    return;
}

/* register the usb driver with the USB subsystem */
static int __init usb_cypress_init(void)
{
    int result;

    printk(KERN_INFO "%s [*] Register Cypress USB Bulk Transfer driver with the USB subsystem", DRIVER_NAME);

    /* register this driver with the USB subsystem */
    result = usb_register(&cypress_driver);
    if(result) {
        printk(KERN_ALERT "%s\t Failed to register Cypress USB Bulk Transfer driver with the USB subsystem, error %d", DRIVER_NAME, result);
        usb_deregister(&cypress_driver);
        return result;
    }

    return 0;
}
module_init(usb_cypress_init);

/* deregister the usb driver with the USB subsystem */
static void __exit usb_cypress_exit(void)
{
    printk(KERN_INFO "%s [*] Deregistered Cypress USB Bulk Transfer driver with the USB subsystem", DRIVER_NAME);

    /* deregister this driver with the USB subsystem */
    usb_deregister(&cypress_driver);
}
module_exit(usb_cypress_exit);

我的用户空间应用程序如下所示:

static void read_thread_cleanup_handler(void *arg)
{
    log_info("Terminate read thread - cleanup handler");
}

void *read_thread_function(void *conf_values)
{
    FILE *dest_file;
    struct pollfd poll_fd;
    char *buffer;
    int usb_dev, ret;
    ssize_t read_count;
    configValues_typ *config_values = (configValues_typ *)conf_values;

    /* push thread cleanup handler to stack */
    pthread_cleanup_push(read_thread_cleanup_handler, NULL);

    log_info("Read Thread started: %d", getpid());

    /* fill poll struct */
    poll_fd.fd = config_values->usb_dev;
    poll_fd.events = POLLIN;

    dest_file = fopen(config_values->dest_file_path, "a+");
    if(dest_file == NULL)
    {
        log_error("Failed to open destination file %s, error: %s", config_values->dest_file_path, strerror(errno));
        ret = -2;
        goto read_thread_error;
    }
    
    buffer = malloc(BUFFER_SIZE);
    if(buffer == NULL)
    {
        log_error("Failed to allocate memory for buffer, error: %s",strerror(errno));
        ret = -3;
        goto read_thread_error;
    }
    while(!terminate)
    {
        /* poll to read file (blocking) - using an infinite timeout */
        if(poll(&poll_fd, 1, -1) < 0)
        {
            log_error("Failed polling Cypress device driver for data to read, error: %s", strerror(errno));
            ret = -4;
            goto read_thread_error;
        }
        read_count = read(config_values->usb_dev, buffer, BUFFER_SIZE);
        if(read_count < 0)
        {
            log_error("Failed to read from Cypress device driver, error: %s", strerror(errno));
            ret = -5;
            goto read_thread_error;
        }
        else if(read_count > 0)
        {
            if(fprintf(dest_file, "%s",buffer) < 0)
            {
                log_error("Failed to write to destination file %s, error: %s", config_values->dest_file_path, strerror(errno));
                ret = -6;
                goto read_thread_error;
            }
        }
    }
    pthread_cleanup_pop(1);
    return NULL;
    
    read_thread_error:
        log_error("Terminate read thread on error");
        pthread_exit(&ret);
}

相关内容