📄 usb-c2420eb.c
字号:
/* * USB Chipcon C2420 Evaluation Board - 0.1.1 * * Copyright (C) 2004 Dmitriy Korovkin <korovkin@users.sourceforge.net>, * Based on USB Skeleton byr Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * * This driver allows to use Chipcon 2420 Evaluation board * * Thanks to Oliver Neukum and David Brownell for their help in debugging * this driver. */#include <linux/config.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/signal.h>#include <linux/errno.h>#include <linux/poll.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/fcntl.h>#include <linux/module.h>#include <linux/spinlock.h>#include <linux/list.h>#include <linux/smp_lock.h>#include <linux/usb.h>#include "usb-c2420eb.h" EXPORT_NO_SYMBOLS;#define CONFIG_USB_DEBUG#ifdef CONFIG_USB_DEBUG int debug = 1;#else int debug = 0;#endif#include "cc2420-functions.h"/* Version Information */#define DRIVER_VERSION "v0.1.1"#define DRIVER_AUTHOR "Dmitriy Korovkin, <korovkin@users.sourceforge.net>"#define DRIVER_DESC "USB C2420EB driver"/* Module paramaters */MODULE_PARM(debug, "i");MODULE_PARM_DESC(debug, "Debug enabled or not");/* Define these values to match your device */#define C2420EB_VENDOR_ID 0x11a0#define C2420EB_PRODUCT_ID 0xeb10#define C2420EB_PRODUCT_ID1 0xeb11/* table of devices that work with this driver */static struct usb_device_id c2420eb_table [] = { { USB_DEVICE(C2420EB_VENDOR_ID, C2420EB_PRODUCT_ID) }, { USB_DEVICE(C2420EB_VENDOR_ID, C2420EB_PRODUCT_ID1) }, { } /* Terminating entry */};MODULE_DEVICE_TABLE (usb, c2420eb_table);/* Get a minor range for your devices from the usb maintainer */#define USB_C2420EB_MINOR_BASE 192 /* we can have up to this number of device plugged in at once */#define MAX_DEVICES 16/* the global usb devfs handle */extern devfs_handle_t usb_devfs_handle;/* local function prototypes */static ssize_t c2420eb_read (struct file *file, char *buffer, size_t count, loff_t *ppos);static ssize_t c2420eb_write (struct file *file, const char *buffer, size_t count, loff_t *ppos);static int c2420eb_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);static int c2420eb_open (struct inode *inode, struct file *file);static int c2420eb_release (struct inode *inode, struct file *file);static void c2420eb_read_callback(struct urb *urb); static void* c2420eb_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id);static void c2420eb_disconnect (struct usb_device *dev, void *ptr);static void c2420eb_write_bulk_callback (struct urb *urb);/* array of pointers to our devices that are currently connected */static struct c2420eb* minor_table[MAX_DEVICES];/* lock to protect the minor_table structure */static DECLARE_MUTEX (minor_table_mutex);/* * File operations needed when we register this driver. * This assumes that this driver NEEDS file operations, * of course, which means that the driver is expected * to have a node in the /dev directory. If the USB * device were for a network interface then the driver * would use "struct net_driver" instead, and a serial * device would use "struct tty_driver". */static struct file_operations c2420eb_fops = { /* * The owner field is part of the module-locking * mechanism. The idea is that the kernel knows * which module to increment the use-counter of * BEFORE it calls the device's open() function. * This also means that the kernel can decrement * the use-counter again before calling release() * or should the open() function fail. * * Not all device structures have an "owner" field * yet. "struct file_operations" and "struct net_device" * do, while "struct tty_driver" does not. If the struct * has an "owner" field, then initialize it to the value * THIS_MODULE and the kernel will handle all module * locking for you automatically. Otherwise, you must * increment the use-counter in the open() function * and decrement it again in the release() function * yourself. */ owner: THIS_MODULE, read: c2420eb_read, write: c2420eb_write, ioctl: c2420eb_ioctl, open: c2420eb_open, release: c2420eb_release,}; /* usb specific object needed to register this driver with the usb subsystem */static struct usb_driver c2420eb_driver = { name: "c2420eb", probe: c2420eb_probe, disconnect: c2420eb_disconnect, fops: &c2420eb_fops, minor: USB_C2420EB_MINOR_BASE, id_table: c2420eb_table,};/** * c2420_submitUrb * Function submits URB for reading * dev - device descriptor * idx - index of the read buffer to use * return 0 on success -<error code> otherwise */static inline int c2420_submitReadUrb(struct c2420eb* dev, int idx){ FILL_BULK_URB(dev->read_urb, dev->udev, usb_rcvbulkpipe (dev->udev, PACKET_READ_PIPE), dev->read_buffer[idx].buf, dev->readLen, c2420eb_read_callback, dev); return usb_submit_urb(dev->read_urb);}/** * c2420eb_debug_data */static inline void c2420eb_debug_data (const char* function, int size, const uint8_t* data){ int i; if (!debug) return; printk (KERN_DEBUG __FILE__": %s - length = %d, data = ", function, size); for (i = 0; i < size; ++i) { printk ("%.2x ", data[i]); } printk ("\n");}/** * c2420eb_delete */static inline void c2420eb_delete (struct c2420eb* dev){ int i; //just an index variable minor_table[dev->minor] = NULL; dbg("Bulk in buffer deallocated: %p", dev->bulk_in_buffer); if (dev->bulk_in_buffer != NULL) kfree (dev->bulk_in_buffer); for (i = 0; i < dev->readBufSize; i++) { dbg("Bulk in buffer deallocated: %p", dev->read_buffer[i].buf); if (dev->read_buffer[i].buf != NULL) kfree (dev->read_buffer[i].buf); } dbg("Bulk out buffer deallocated: %p", dev->bulk_out_buffer); if (dev->bulk_out_buffer != NULL) kfree (dev->bulk_out_buffer); dbg("Deallocate write URB %p", dev->write_urb); if (dev->write_urb != NULL) usb_free_urb (dev->write_urb); dbg("Deallocate read URB %p", dev->read_urb); if (dev->read_urb != NULL) usb_free_urb (dev->read_urb); kfree (dev);}/** * c2420eb_open */static int c2420eb_open (struct inode* inode, struct file* file){ struct c2420eb* dev = NULL; int subminor; //minor device number in the area of these devices int retval = 0; //result of function calls dbg(__FUNCTION__); subminor = MINOR (inode->i_rdev) - USB_C2420EB_MINOR_BASE; /* * Note: for now we access odd minored devicess to access communication * feature and we access even number to access the internal software */ if ((subminor < 0) || (subminor >= MAX_DEVICES)) return -ENODEV; /* Increment our usage count for the module. * This is redundant here, because "struct file_operations" * has an "owner" field. This line is included here soley as * a reference for drivers using lesser structures... ;-) */ MOD_INC_USE_COUNT; /* lock our minor table and get our local data for this minor */ down (&minor_table_mutex); dev = minor_table[subminor]; if (dev == NULL) { up (&minor_table_mutex); MOD_DEC_USE_COUNT; return -ENODEV; } //We may use device only once if (dev->open_count > 0) { up (&minor_table_mutex); MOD_DEC_USE_COUNT; return -EBUSY; } /* lock this device */ down (&dev->sem); /* unlock the minor table */ up (&minor_table_mutex); if (!dev->open_count) //Start read machine { dev->readIdx = 0; //No data available for now dev->writeIdx = 0; //No data available for now dev->bufOver = 0; //Let's mark buffer is not overflowed } /* increment our usage count for the driver */ spin_lock(&dev->readLock); ++dev->open_count; spin_unlock(&dev->readLock); /* save our object in the file's private structure */ file->private_data = dev; /* unlock this device */ up (&dev->sem); return retval;}/** * c2420eb_release */static int c2420eb_release (struct inode* inode, struct file* file){ struct c2420eb* dev; int retval = 0; dev = (struct c2420eb*)file->private_data; if (dev == NULL) { dbg (__FUNCTION__ " - object is NULL"); return -ENODEV; } dbg(__FUNCTION__ " - minor %d", dev->minor); /* lock our minor table */ down (&minor_table_mutex); /* lock our device */ down (&dev->sem); if (dev->open_count <= 0) { dbg (__FUNCTION__ " - device not opened"); retval = -ENODEV; goto exit_not_opened; } if (dev->udev == NULL) { /* the device was unplugged before the file was released */ up (&dev->sem); c2420eb_delete (dev); up (&minor_table_mutex); MOD_DEC_USE_COUNT; return 0; } //Switch receiver off c2420eb_trxOff(dev); /* decrement our usage count for the device */ spin_lock(&dev->readLock); --dev->open_count; spin_unlock(&dev->readLock); if (dev->open_count <= 0) { /* shutdown any bulk reads/writes that might be going on */ usb_unlink_urb (dev->write_urb); usb_unlink_urb (dev->read_urb); dev->open_count = 0; } /* decrement our usage count for the module */ MOD_DEC_USE_COUNT;exit_not_opened: up (&dev->sem); up (&minor_table_mutex); return retval;}/** * c2420eb_read_callback */static void c2420eb_read_callback(struct urb *urb){ struct c2420eb* dev = (struct c2420eb*)urb->context; //device descriptor pointer int result; //operation result int newIdx; //new write index if (dev->open_count > 0 && dev->udev) { wake_up_interruptible(&dev->readwq); if (urb->status == -75) urb->status = 0; if (urb->status != 0) dbg("Error URB status %d", urb->status); dev->read_buffer[dev->writeIdx].status = urb->status; dev->read_buffer[dev->writeIdx].len = urb->actual_length; if (urb->transfer_buffer != dev->read_buffer[dev->writeIdx].buf) err("Diff buffers"); newIdx = (dev->writeIdx + 1) % dev->readBufSize; if (newIdx != dev->readIdx) { dev->writeIdx = newIdx; if ((result = c2420_submitReadUrb(dev, dev->writeIdx)) < 0) err("Callback: Unable submit URB %d", result); } else { spin_lock(&dev->readLock); dev->bufOver = 1; spin_unlock(&dev->readLock); dbg("Overflow"); } } return;}/** * c2420eb_read */static ssize_t c2420eb_read (struct file* file, char* buffer, size_t count, loff_t* ppos){ struct c2420eb *dev; int retval = 0; int len; //actual length of data to read bool bufOverflow; //if our read buffer overflows unsigned long flags; //variable used to store registers dev = (struct c2420eb*)file->private_data;//// dbg(__FUNCTION__ " - minor %d, count = %d", dev->minor, count); /* lock this object */ down (&dev->sem); wait_event_interruptible(dev->readwq, dev->readIdx != dev->writeIdx); if (signal_pending(current)) { up (&dev->sem); return -EINTR; } if (dev->read_buffer[dev->readIdx].status == 0) { len = (dev->read_buffer[dev->readIdx].len > count)? count: dev->read_buffer[dev->readIdx].len; if (copy_to_user (buffer, dev->read_buffer[dev->readIdx].buf, len)) retval = -EFAULT; else retval = len; } else retval = dev->read_buffer[dev->readIdx].status; dev->readIdx = (dev->readIdx + 1) % dev->readBufSize; spin_lock_irqsave(&dev->readLock, flags); bufOverflow = dev->bufOver; spin_unlock_irqrestore(&dev->readLock, flags); //If bufOver variable is set, //callback will be never called until we submit an URB if (bufOverflow && dev->udev) { dev->writeIdx = (dev->writeIdx + 1) % dev->readBufSize; dev->bufOver = 0; if ((retval = c2420_submitReadUrb(dev, dev->writeIdx)) < 0) { dev->bufOver = 1; err("Read: unable to submit URB: %d", retval); } } /* unlock the device */ up (&dev->sem); return retval;}/** * c2420eb_write */static ssize_t c2420eb_write (struct file* file, const char* buffer, size_t count, loff_t* ppos){ struct c2420eb* dev; ssize_t bytes_written = 0; int retval = 0; dev = (struct c2420eb*)file->private_data; dbg(__FUNCTION__ " - minor %d, count = %d", dev->minor, count); /* lock this object */ down (&dev->sem); /* verify that the device wasn't unplugged */ if (dev->udev == NULL) { retval = -ENODEV; goto exit; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -