⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 iowarrior.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  Native support for the I/O-Warrior USB devices * *  Copyright (c) 2003-2005  Code Mercenaries GmbH *  written by Christian Lucht <lucht@codemercs.com> * *  based on *  usb-skeleton.c by Greg Kroah-Hartman  <greg@kroah.com> *  brlvger.c by Stephane Dalton  <sdalton@videotron.ca> *           and St�hane Doyon   <s.doyon@videotron.ca> * *  Released under the GPLv2. */#include <linux/module.h>#include <linux/usb.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/poll.h>#include <linux/version.h>#include <linux/usb/iowarrior.h>/* Version Information */#define DRIVER_VERSION "v0.4.0"#define DRIVER_AUTHOR "Christian Lucht <lucht@codemercs.com>"#define DRIVER_DESC "USB IO-Warrior driver (Linux 2.6.x)"#define USB_VENDOR_ID_CODEMERCS		1984/* low speed iowarrior */#define USB_DEVICE_ID_CODEMERCS_IOW40	0x1500#define USB_DEVICE_ID_CODEMERCS_IOW24	0x1501#define USB_DEVICE_ID_CODEMERCS_IOWPV1	0x1511#define USB_DEVICE_ID_CODEMERCS_IOWPV2	0x1512/* full speed iowarrior */#define USB_DEVICE_ID_CODEMERCS_IOW56	0x1503/* Get a minor range for your devices from the usb maintainer */#ifdef CONFIG_USB_DYNAMIC_MINORS#define IOWARRIOR_MINOR_BASE	0#else#define IOWARRIOR_MINOR_BASE	208	// SKELETON_MINOR_BASE 192 + 16, not offical yet#endif/* interrupt input queue size */#define MAX_INTERRUPT_BUFFER 16/*   maximum number of urbs that are submitted for writes at the same time,   this applies to the IOWarrior56 only!   IOWarrior24 and IOWarrior40 use synchronous usb_control_msg calls.*/#define MAX_WRITES_IN_FLIGHT 4/* Use our own dbg macro */#undef dbg#define dbg( format, arg... ) do { if( debug ) printk( KERN_DEBUG __FILE__ ": " format "\n" , ## arg ); } while ( 0 )MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");/* Module parameters */static int debug = 0;module_param(debug, bool, 0644);MODULE_PARM_DESC(debug, "debug=1 enables debugging messages");static struct usb_driver iowarrior_driver;static DEFINE_MUTEX(iowarrior_open_disc_lock);/*--------------*//*     data     *//*--------------*//* Structure to hold all of our device specific stuff */struct iowarrior {	struct mutex mutex;			/* locks this structure */	struct usb_device *udev;		/* save off the usb device pointer */	struct usb_interface *interface;	/* the interface for this device */	unsigned char minor;			/* the starting minor number for this device */	struct usb_endpoint_descriptor *int_out_endpoint;	/* endpoint for reading (needed for IOW56 only) */	struct usb_endpoint_descriptor *int_in_endpoint;	/* endpoint for reading */	struct urb *int_in_urb;		/* the urb for reading data */	unsigned char *int_in_buffer;	/* buffer for data to be read */	unsigned char serial_number;	/* to detect lost packages */	unsigned char *read_queue;	/* size is MAX_INTERRUPT_BUFFER * packet size */	wait_queue_head_t read_wait;	wait_queue_head_t write_wait;	/* wait-queue for writing to the device */	atomic_t write_busy;		/* number of write-urbs submitted */	atomic_t read_idx;	atomic_t intr_idx;	spinlock_t intr_idx_lock;	/* protects intr_idx */	atomic_t overflow_flag;		/* signals an index 'rollover' */	int present;			/* this is 1 as long as the device is connected */	int opened;			/* this is 1 if the device is currently open */	char chip_serial[9];		/* the serial number string of the chip connected */	int report_size;		/* number of bytes in a report */	u16 product_id;};/*--------------*//*    globals   *//*--------------*//* *  USB spec identifies 5 second timeouts. */#define GET_TIMEOUT 5#define USB_REQ_GET_REPORT  0x01//#if 0static int usb_get_report(struct usb_device *dev,			  struct usb_host_interface *inter, unsigned char type,			  unsigned char id, void *buf, int size){	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),			       USB_REQ_GET_REPORT,			       USB_DIR_IN | USB_TYPE_CLASS |			       USB_RECIP_INTERFACE, (type << 8) + id,			       inter->desc.bInterfaceNumber, buf, size,			       GET_TIMEOUT*HZ);}//#endif#define USB_REQ_SET_REPORT 0x09static int usb_set_report(struct usb_interface *intf, unsigned char type,			  unsigned char id, void *buf, int size){	return usb_control_msg(interface_to_usbdev(intf),			       usb_sndctrlpipe(interface_to_usbdev(intf), 0),			       USB_REQ_SET_REPORT,			       USB_TYPE_CLASS | USB_RECIP_INTERFACE,			       (type << 8) + id,			       intf->cur_altsetting->desc.bInterfaceNumber, buf,			       size, HZ);}/*---------------------*//* driver registration *//*---------------------*//* table of devices that work with this driver */static struct usb_device_id iowarrior_ids[] = {	{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40)},	{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24)},	{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV1)},	{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV2)},	{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW56)},	{}			/* Terminating entry */};MODULE_DEVICE_TABLE(usb, iowarrior_ids);/* * USB callback handler for reading data */static void iowarrior_callback(struct urb *urb){	struct iowarrior *dev = (struct iowarrior *)urb->context;	int intr_idx;	int read_idx;	int aux_idx;	int offset;	int status = urb->status;	int retval;	switch (status) {	case 0:		/* success */		break;	case -ECONNRESET:	case -ENOENT:	case -ESHUTDOWN:		return;	default:		goto exit;	}	spin_lock(&dev->intr_idx_lock);	intr_idx = atomic_read(&dev->intr_idx);	/* aux_idx become previous intr_idx */	aux_idx = (intr_idx == 0) ? (MAX_INTERRUPT_BUFFER - 1) : (intr_idx - 1);	read_idx = atomic_read(&dev->read_idx);	/* queue is not empty and it's interface 0 */	if ((intr_idx != read_idx)	    && (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0)) {		/* + 1 for serial number */		offset = aux_idx * (dev->report_size + 1);		if (!memcmp		    (dev->read_queue + offset, urb->transfer_buffer,		     dev->report_size)) {			/* equal values on interface 0 will be ignored */			spin_unlock(&dev->intr_idx_lock);			goto exit;		}	}	/* aux_idx become next intr_idx */	aux_idx = (intr_idx == (MAX_INTERRUPT_BUFFER - 1)) ? 0 : (intr_idx + 1);	if (read_idx == aux_idx) {		/* queue full, dropping oldest input */		read_idx = (++read_idx == MAX_INTERRUPT_BUFFER) ? 0 : read_idx;		atomic_set(&dev->read_idx, read_idx);		atomic_set(&dev->overflow_flag, 1);	}	/* +1 for serial number */	offset = intr_idx * (dev->report_size + 1);	memcpy(dev->read_queue + offset, urb->transfer_buffer,	       dev->report_size);	*(dev->read_queue + offset + (dev->report_size)) = dev->serial_number++;	atomic_set(&dev->intr_idx, aux_idx);	spin_unlock(&dev->intr_idx_lock);	/* tell the blocking read about the new data */	wake_up_interruptible(&dev->read_wait);exit:	retval = usb_submit_urb(urb, GFP_ATOMIC);	if (retval)		dev_err(&dev->interface->dev, "%s - usb_submit_urb failed with result %d\n",			__FUNCTION__, retval);}/* * USB Callback handler for write-ops */static void iowarrior_write_callback(struct urb *urb){	struct iowarrior *dev;	int status = urb->status;	dev = (struct iowarrior *)urb->context;	/* sync/async unlink faults aren't errors */	if (status &&	    !(status == -ENOENT ||	      status == -ECONNRESET || status == -ESHUTDOWN)) {		dbg("%s - nonzero write bulk status received: %d",		    __func__, status);	}	/* free up our allocated buffer */	usb_buffer_free(urb->dev, urb->transfer_buffer_length,			urb->transfer_buffer, urb->transfer_dma);	/* tell a waiting writer the interrupt-out-pipe is available again */	atomic_dec(&dev->write_busy);	wake_up_interruptible(&dev->write_wait);}/** *	iowarrior_delete */static inline void iowarrior_delete(struct iowarrior *dev){	dbg("%s - minor %d", __func__, dev->minor);	kfree(dev->int_in_buffer);	usb_free_urb(dev->int_in_urb);	kfree(dev->read_queue);	kfree(dev);}/*---------------------*//* fops implementation *//*---------------------*/static int read_index(struct iowarrior *dev){	int intr_idx, read_idx;	read_idx = atomic_read(&dev->read_idx);	intr_idx = atomic_read(&dev->intr_idx);	return (read_idx == intr_idx ? -1 : read_idx);}/** *  iowarrior_read */static ssize_t iowarrior_read(struct file *file, char __user *buffer,			      size_t count, loff_t *ppos){	struct iowarrior *dev;	int read_idx;	int offset;	dev = (struct iowarrior *)file->private_data;	/* verify that the device wasn't unplugged */	if (dev == NULL || !dev->present)		return -ENODEV;	dbg("%s - minor %d, count = %zd", __func__, dev->minor, count);	/* read count must be packet size (+ time stamp) */	if ((count != dev->report_size)	    && (count != (dev->report_size + 1)))		return -EINVAL;	/* repeat until no buffer overrun in callback handler occur */	do {		atomic_set(&dev->overflow_flag, 0);		if ((read_idx = read_index(dev)) == -1) {			/* queue emty */			if (file->f_flags & O_NONBLOCK)				return -EAGAIN;			else {				//next line will return when there is either new data, or the device is unplugged				int r = wait_event_interruptible(dev->read_wait,								 (!dev->present								  || (read_idx =								      read_index								      (dev)) !=								  -1));				if (r) {					//we were interrupted by a signal					return -ERESTART;				}				if (!dev->present) {					//The device was unplugged					return -ENODEV;				}				if (read_idx == -1) {					// Can this happen ???					return 0;				}			}		}		offset = read_idx * (dev->report_size + 1);		if (copy_to_user(buffer, dev->read_queue + offset, count)) {			return -EFAULT;		}	} while (atomic_read(&dev->overflow_flag));	read_idx = ++read_idx == MAX_INTERRUPT_BUFFER ? 0 : read_idx;	atomic_set(&dev->read_idx, read_idx);	return count;}/* * iowarrior_write */static ssize_t iowarrior_write(struct file *file,			       const char __user *user_buffer,			       size_t count, loff_t *ppos){	struct iowarrior *dev;	int retval = 0;	char *buf = NULL;	/* for IOW24 and IOW56 we need a buffer */	struct urb *int_out_urb = NULL;	dev = (struct iowarrior *)file->private_data;	mutex_lock(&dev->mutex);	/* verify that the device wasn't unplugged */	if (!dev->present) {		retval = -ENODEV;		goto exit;	}	dbg("%s - minor %d, count = %zd", __func__, dev->minor, count);	/* if count is 0 we're already done */	if (count == 0) {		retval = 0;		goto exit;	}	/* We only accept full reports */	if (count != dev->report_size) {		retval = -EINVAL;		goto exit;	}	switch (dev->product_id) {	case USB_DEVICE_ID_CODEMERCS_IOW24:	case USB_DEVICE_ID_CODEMERCS_IOWPV1:	case USB_DEVICE_ID_CODEMERCS_IOWPV2:	case USB_DEVICE_ID_CODEMERCS_IOW40:		/* IOW24 and IOW40 use a synchronous call */		buf = kmalloc(8, GFP_KERNEL);	/* 8 bytes are enough for both products */		if (!buf) {			retval = -ENOMEM;			goto exit;		}		if (copy_from_user(buf, user_buffer, count)) {			retval = -EFAULT;			kfree(buf);			goto exit;		}		retval = usb_set_report(dev->interface, 2, 0, buf, count);		kfree(buf);		goto exit;		break;	case USB_DEVICE_ID_CODEMERCS_IOW56:		/* The IOW56 uses asynchronous IO and more urbs */		if (atomic_read(&dev->write_busy) == MAX_WRITES_IN_FLIGHT) {			/* Wait until we are below the limit for submitted urbs */			if (file->f_flags & O_NONBLOCK) {				retval = -EAGAIN;				goto exit;			} else {				retval = wait_event_interruptible(dev->write_wait,								  (!dev->present || (atomic_read (&dev-> write_busy) < MAX_WRITES_IN_FLIGHT)));				if (retval) {					/* we were interrupted by a signal */					retval = -ERESTART;					goto exit;				}				if (!dev->present) {					/* The device was unplugged */					retval = -ENODEV;					goto exit;				}				if (!dev->opened) {					/* We were closed while waiting for an URB */					retval = -ENODEV;					goto exit;				}			}		}		atomic_inc(&dev->write_busy);		int_out_urb = usb_alloc_urb(0, GFP_KERNEL);		if (!int_out_urb) {			retval = -ENOMEM;			dbg("%s Unable to allocate urb ", __func__);			goto error_no_urb;		}		buf = usb_buffer_alloc(dev->udev, dev->report_size,				       GFP_KERNEL, &int_out_urb->transfer_dma);		if (!buf) {			retval = -ENOMEM;			dbg("%s Unable to allocate buffer ", __func__);			goto error_no_buffer;		}		usb_fill_int_urb(int_out_urb, dev->udev,				 usb_sndintpipe(dev->udev,						dev->int_out_endpoint->bEndpointAddress),				 buf, dev->report_size,				 iowarrior_write_callback, dev,				 dev->int_out_endpoint->bInterval);		int_out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;		if (copy_from_user(buf, user_buffer, count)) {			retval = -EFAULT;			goto error;		}		retval = usb_submit_urb(int_out_urb, GFP_KERNEL);		if (retval) {			dbg("%s submit error %d for urb nr.%d", __func__,			    retval, atomic_read(&dev->write_busy));			goto error;		}		/* submit was ok */		retval = count;		usb_free_urb(int_out_urb);		goto exit;		break;	default:		/* what do we have here ? An unsupported Product-ID ? */		dev_err(&dev->interface->dev, "%s - not supported for product=0x%x\n",			__FUNCTION__, dev->product_id);		retval = -EFAULT;		goto exit;		break;	}error:	usb_buffer_free(dev->udev, dev->report_size, buf,			int_out_urb->transfer_dma);error_no_buffer:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -