tiglusb.c
来自「是关于linux2.5.1的完全源码」· C语言 代码 · 共 497 行
C
497 行
/* Hey EMACS -*- linux-c -*- * * tiglusb -- Texas Instruments' USB GraphLink (aka SilverLink) driver. * Target: Texas Instruments graphing calculators (http://lpg.ticalc.org). * * Copyright (C) 2001-2002: * Romain Lievin <roms@lpg.ticalc.org> * Julien BLACHE <jb@technologeek.org> * under the terms of the GNU General Public License. * * Based on dabusb.c, printer.c & scanner.c * * Please see the file: linux/Documentation/usb/SilverLink.txt * and the website at: http://lpg.ticalc.org/prj_usb/ * for more info. * */#include <linux/module.h>#include <linux/socket.h>#include <linux/miscdevice.h>#include <linux/slab.h>#include <linux/init.h>#include <asm/uaccess.h>#include <linux/delay.h>#include <linux/usb.h>#include <linux/smp_lock.h>#include <linux/devfs_fs_kernel.h>#include <linux/ticable.h>#include "tiglusb.h"/* * Version Information */#define DRIVER_VERSION "1.03"#define DRIVER_AUTHOR "Romain Lievin <roms@lpg.ticalc.org> & Julien Blache <jb@jblache.org>"#define DRIVER_DESC "TI-GRAPH LINK USB (aka SilverLink) driver"#define DRIVER_LICENSE "GPL"/* ----- global variables --------------------------------------------- */static tiglusb_t tiglusb[MAXTIGL];static int timeout = TIMAXTIME; /* timeout in tenth of seconds */static devfs_handle_t devfs_handle;/*---------- misc functions ------------------------------------------- *//* Unregister device */static void usblp_cleanup (tiglusb_t * s){ devfs_unregister (s->devfs); //memset(tiglusb[s->minor], 0, sizeof(tiglusb_t)); info ("tiglusb%d removed", s->minor);}/* Re-initialize device */static int clear_device (struct usb_device *dev){ if (usb_set_configuration (dev, dev->config[0].bConfigurationValue) < 0) { printk ("tiglusb: clear_device failed\n"); return -1; } return 0;}/* Clear input & output pipes (endpoints) */static int clear_pipes (struct usb_device *dev){ unsigned int pipe; pipe = usb_sndbulkpipe (dev, 1); if (usb_clear_halt (dev, usb_pipeendpoint (pipe))) { printk ("tiglusb: clear_pipe (r), request failed\n"); return -1; } pipe = usb_sndbulkpipe (dev, 2); if (usb_clear_halt (dev, usb_pipeendpoint (pipe))) { printk ("tiglusb: clear_pipe (w), request failed\n"); return -1; } return 0;}/* ----- kernel module functions--------------------------------------- */static int tiglusb_open (struct inode *inode, struct file *file){ int devnum = minor (inode->i_rdev); ptiglusb_t s; if (devnum < TIUSB_MINOR || devnum >= (TIUSB_MINOR + MAXTIGL)) return -EIO; s = &tiglusb[devnum - TIUSB_MINOR]; down (&s->mutex); while (!s->dev || s->opened) { up (&s->mutex); if (file->f_flags & O_NONBLOCK) { return -EBUSY; } schedule_timeout (HZ / 2); if (signal_pending (current)) { return -EAGAIN; } down (&s->mutex); } s->opened = 1; up (&s->mutex); file->f_pos = 0; file->private_data = s; return 0;}static int tiglusb_release (struct inode *inode, struct file *file){ ptiglusb_t s = (ptiglusb_t) file->private_data; lock_kernel (); down (&s->mutex); s->state = _stopped; up (&s->mutex); if (!s->remove_pending) clear_device (s->dev); else wake_up (&s->remove_ok); s->opened = 0; unlock_kernel (); return 0;}static ssize_t tiglusb_read (struct file *file, char *buf, size_t count, loff_t * ppos){ ptiglusb_t s = (ptiglusb_t) file->private_data; ssize_t ret = 0; int bytes_to_read = 0; int bytes_read = 0; int result = 0; char buffer[BULK_RCV_MAX]; unsigned int pipe; if (*ppos) return -ESPIPE; if (s->remove_pending) return -EIO; if (!s->dev) return -EIO; bytes_to_read = (count >= BULK_RCV_MAX) ? BULK_RCV_MAX : count; pipe = usb_rcvbulkpipe (s->dev, 1); result = usb_bulk_msg (s->dev, pipe, buffer, bytes_to_read, &bytes_read, HZ / (timeout / 10)); if (result == -ETIMEDOUT) { /* NAK */ ret = result; if (!bytes_read) { printk ("quirk !\n"); } warn ("tiglusb_read, NAK received."); goto out; } else if (result == -EPIPE) { /* STALL -- shouldn't happen */ warn ("CLEAR_FEATURE request to remove STALL condition.\n"); if (usb_clear_halt (s->dev, usb_pipeendpoint (pipe))) warn ("send_packet, request failed\n"); //clear_device(s->dev); ret = result; goto out; } else if (result < 0) { /* We should not get any I/O errors */ warn ("funky result: %d. Please notify maintainer.", result); ret = -EIO; goto out; } if (copy_to_user (buf, buffer, bytes_read)) { ret = -EFAULT; goto out; } out: return ret ? ret : bytes_read;}static ssize_t tiglusb_write (struct file *file, const char *buf, size_t count, loff_t * ppos){ ptiglusb_t s = (ptiglusb_t) file->private_data; ssize_t ret = 0; int bytes_to_write = 0; int bytes_written = 0; int result = 0; char buffer[BULK_SND_MAX]; unsigned int pipe; if (*ppos) return -ESPIPE; if (s->remove_pending) return -EIO; if (!s->dev) return -EIO; bytes_to_write = (count >= BULK_SND_MAX) ? BULK_SND_MAX : count; if (copy_from_user (buffer, buf, bytes_to_write)) { ret = -EFAULT; goto out; } pipe = usb_sndbulkpipe (s->dev, 2); result = usb_bulk_msg (s->dev, pipe, buffer, bytes_to_write, &bytes_written, HZ / (timeout / 10)); if (result == -ETIMEDOUT) { /* NAK */ warn ("tiglusb_write, NAK received."); ret = result; goto out; } else if (result == -EPIPE) { /* STALL -- shouldn't happen */ warn ("CLEAR_FEATURE request to remove STALL condition."); if (usb_clear_halt (s->dev, usb_pipeendpoint (pipe))) warn ("send_packet, request failed\n"); //clear_device(s->dev); ret = result; goto out; } else if (result < 0) { /* We should not get any I/O errors */ warn ("funky result: %d. Please notify maintainer.", result); ret = -EIO; goto out; } if (bytes_written != bytes_to_write) { ret = -EIO; goto out; } out: return ret ? ret : bytes_written;}static int tiglusb_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ ptiglusb_t s = (ptiglusb_t) file->private_data; int ret = 0; if (s->remove_pending) return -EIO; down (&s->mutex); if (!s->dev) { up (&s->mutex); return -EIO; } switch (cmd) { case IOCTL_TIUSB_TIMEOUT: timeout = arg; // timeout value in tenth of seconds break; case IOCTL_TIUSB_RESET_DEVICE: printk (KERN_DEBUG "IOCTL_TIGLUSB_RESET_DEVICE\n"); if (clear_device (s->dev)) ret = -EIO; break; case IOCTL_TIUSB_RESET_PIPES: printk (KERN_DEBUG "IOCTL_TIGLUSB_RESET_PIPES\n"); if (clear_pipes (s->dev)) ret = -EIO; break; default: ret = -ENOTTY; break; } up (&s->mutex); return ret;}/* ----- kernel module registering ------------------------------------ */static struct file_operations tiglusb_fops = { owner: THIS_MODULE, llseek: no_llseek, read: tiglusb_read, write: tiglusb_write, ioctl: tiglusb_ioctl, open: tiglusb_open, release: tiglusb_release,};static int tiglusb_find_struct (void){ int u; for (u = 0; u < MAXTIGL; u++) { ptiglusb_t s = &tiglusb[u]; if (!s->dev) return u; } return -1;}/* --- initialisation code ------------------------------------- */static void *tiglusb_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id){ int minor; ptiglusb_t s; char name[8]; printk ("tiglusb: probing vendor id 0x%x, device id 0x%x ifnum:%d\n", dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); /* * We don't handle multiple configurations. As of version 0x0103 of * the TIGL hardware, there's only 1 configuration. */ if (dev->descriptor.bNumConfigurations != 1) return NULL; if ((dev->descriptor.idProduct != 0xe001) && (dev->descriptor.idVendor != 0x451)) return NULL; if (usb_set_configuration (dev, dev->config[0].bConfigurationValue) < 0) { printk ("tiglusb_probe: set_configuration failed\n"); return NULL; } minor = tiglusb_find_struct (); if (minor == -1) return NULL; s = &tiglusb[minor]; down (&s->mutex); s->remove_pending = 0; s->dev = dev; up (&s->mutex); dbg ("bound to interface: %d", ifnum); sprintf (name, "%d", s->minor); printk ("tiglusb: registering to devfs : major = %d, minor = %d, node = %s\n", TIUSB_MAJOR, (TIUSB_MINOR + s->minor), name); s->devfs = devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT, TIUSB_MAJOR, TIUSB_MINOR + s->minor, S_IFCHR | S_IRUGO | S_IWUGO, &tiglusb_fops, NULL); /* Display firmware version */ printk ("tiglusb: link cable version %i.%02x\n", dev->descriptor.bcdDevice >> 8, dev->descriptor.bcdDevice & 0xff); return s;}static void tiglusb_disconnect (struct usb_device *dev, void *drv_context){ ptiglusb_t s = (ptiglusb_t) drv_context; if (!s || !s->dev) printk ("bogus disconnect"); s->remove_pending = 1; wake_up (&s->wait); if (s->state == _started) sleep_on (&s->remove_ok); down (&s->mutex); s->dev = NULL; s->opened = 0; /* cleanup now or later, on close */ if (!s->opened) usblp_cleanup (s); else up (&s->mutex); /* unregister device */ devfs_unregister (s->devfs); s->devfs = NULL; printk ("tiglusb: device disconnected\n");}static struct usb_device_id tiglusb_ids[] = { {USB_DEVICE (0x0451, 0xe001)}, {}};MODULE_DEVICE_TABLE (usb, tiglusb_ids);static struct usb_driver tiglusb_driver = { owner: THIS_MODULE, name: "tiglusb", probe: tiglusb_probe, disconnect: tiglusb_disconnect, id_table: tiglusb_ids,};/* --- initialisation code ------------------------------------- */#ifndef MODULE/* You must set these - there is no sane way to probe for this cable. * You can use 'tipar=timeout,delay' to set these now. */static int __init tiglusb_setup (char *str){ int ints[2]; str = get_options (str, ARRAY_SIZE (ints), ints); if (ints[0] > 0) { timeout = ints[1]; } return 1;}#endifstatic int __init tiglusb_init (void){ unsigned u; int result; /* initialize struct */ for (u = 0; u < MAXTIGL; u++) { ptiglusb_t s = &tiglusb[u]; memset (s, 0, sizeof (tiglusb_t)); init_MUTEX (&s->mutex); s->dev = NULL; s->minor = u; s->opened = 0; init_waitqueue_head (&s->wait); init_waitqueue_head (&s->remove_ok); } /* register device */ if (devfs_register_chrdev (TIUSB_MAJOR, "tiglusb", &tiglusb_fops)) { printk ("tiglusb: unable to get major %d\n", TIUSB_MAJOR); return -EIO; } /* Use devfs, tree: /dev/ticables/usb/[0..3] */ devfs_handle = devfs_mk_dir (NULL, "ticables/usb", NULL); /* register USB module */ result = usb_register (&tiglusb_driver); if (result < 0) { devfs_unregister_chrdev (TIUSB_MAJOR, "tiglusb"); return -1; } info (DRIVER_DESC ", " DRIVER_VERSION); return 0;}static void __exit tiglusb_cleanup (void){ usb_deregister (&tiglusb_driver); devfs_unregister (devfs_handle); devfs_unregister_chrdev (TIUSB_MAJOR, "tiglusb");}/* --------------------------------------------------------------------- */__setup ("tiusb=", tiglusb_setup);module_init (tiglusb_init);module_exit (tiglusb_cleanup);MODULE_AUTHOR (DRIVER_AUTHOR);MODULE_DESCRIPTION (DRIVER_DESC);MODULE_LICENSE (DRIVER_LICENSE);EXPORT_NO_SYMBOLS;MODULE_PARM (timeout, "i");MODULE_PARM_DESC (timeout, "Timeout (default=1.5 seconds)");/* --------------------------------------------------------------------- */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?