📄 scanner.c
字号:
/* -*- linux-c -*- *//* * Driver for USB Scanners (linux-2.3.33) * * David E. Nelson (dnelson@jump.net) * * 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 program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Based upon mouse.c (Brad Keryan) and printer.c (Michael Gee). * * History * 0.1 8/31/1999 * * Developed/tested using linux-2.3.15 with minor ohci.c changes to * support short packes during bulk xfer mode. Some testing was * done with ohci-hcd but the performace was low. Very limited * testing was performed with uhci but I was unable to get it to * work. Initial relase to the linux-usb development effort. * * 0.2 10/16/1999 * * FIXED: * - Device can't be opened unless a scanner is plugged into the USB. * - Finally settled on a reasonable value for the I/O buffer's. * - Cleaned up write_scanner() * - Disabled read/write stats * - A little more code cleanup * * 0.3 10/18/1999 * * FIXED: * - Device registration changed to reflect new device * allocation/registration for linux-2.3.22+. * - Adopted David Brownell's <david-b@pacbell.net> technique for * assigning bulk endpoints. * - Removed unnessesary #include's * - Scanner model now reported via syslog INFO after being detected * *and* configured. * - Added user specified verdor:product USB ID's which can be passed * as module parameters. * * 0.3.1 * FIXED: * - Applied patches for linux-2.3.25. * - Error number reporting changed to reflect negative return codes. * * 0.3.2 * FIXED: * - Applied patches for linux-2.3.26 to scanner_init(). * - Debug read/write stats now report values as signed decimal. * * * 0.3.3 * FIXED: * - Updated the bulk_msg() calls to usb usb_bulk_msg(). * - Added a small delay in the write_scanner() method to aid in * avoiding NULL data reads on HP scanners. We'll see how this works. * - Return values from usb_bulk_msg() now ignore positive values for * use with the ohci driver. * - Added conditional debugging instead of commenting/uncommenting * all over the place. * - kfree()'d the pointer after using usb_string() as documented in * linux-usb-api.txt. * - Added usb_set_configuration(). It got lost in version 0.3 -- ack! * - Added the HP 5200C USB Vendor/Product ID's * * TODO * - Simultaneous multiple device attachment * - ioctl()'s ? * * Thanks to: * - All the folks on the linux-usb list who put up with me. :) This * has been a great learning experience for me. * - To Linus Torvalds for this great OS. * - The GNU folks. * - The folks that forwarded Vendor:Product ID's to me. * - And anybody else who chimed in with reports and suggestions. * * Performance: * System: Pentium 120, 80 MB RAM, OHCI, Linux 2.3.23, HP 4100C USB Scanner * 300 dpi scan of the entire bed * 24 Bit Color ~ 70 secs - 3.6 Mbit/sec * 8 Bit Gray ~ 17 secs - 4.2 Mbit/sec * */#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <asm/uaccess.h>#include <linux/malloc.h>#include <linux/delay.h>#undef DEBUG /* Enable to print results of read/write_scanner() calls */#undef RD_DATA_DUMP /* Enable to dump data - limited to 24 bytes */#undef WR_DATA_DUMP#include "usb.h"#define IBUF_SIZE 32768#define OBUF_SIZE 4096struct hpscan_usb_data { struct usb_device *hpscan_dev; int isopen; /* Not zero if the device is open */ int present; /* Device is present on the bus */ char *obuf, *ibuf; /* transfer buffers */ char iep, oep; /* I/O Endpoints */};static struct hpscan_usb_data hpscan;MODULE_AUTHOR("David E. Nelson, dnelson@jump.net, http://www.jump.net/~dnelson");MODULE_DESCRIPTION("USB Scanner Driver");static __u16 vendor=0, product=0;MODULE_PARM(vendor, "i");MODULE_PARM_DESC(vendor, "User specified USB idVendor");MODULE_PARM(product, "i");MODULE_PARM_DESC(product, "User specified USB idProduct");static intopen_scanner(struct inode * inode, struct file * file){ struct hpscan_usb_data *hps = &hpscan; if (!hps->present) { return -ENODEV; } if (!hps->hpscan_dev) { return -ENODEV; } if (hps->isopen) { return -EBUSY; } hps->isopen = 1; MOD_INC_USE_COUNT; return 0;}static intclose_scanner(struct inode * inode, struct file * file){ struct hpscan_usb_data *hps = &hpscan; hps->isopen = 0; MOD_DEC_USE_COUNT; return 0;}static ssize_twrite_scanner(struct file * file, const char * buffer, size_t count, loff_t *ppos){ struct hpscan_usb_data *hps = &hpscan; unsigned long copy_size; unsigned long bytes_written = 0; int partial; ssize_t ret = 0; int result = 0; char *obuf = hps->obuf; set_current_state(TASK_INTERRUPTIBLE); while (count > 0) { if (signal_pending(current)) { ret = -ERESTARTSYS; break; } copy_size = (count > OBUF_SIZE) ? OBUF_SIZE : count; if (copy_from_user(hps->obuf, buffer, copy_size)) { ret = -EFAULT; break; } result = usb_bulk_msg(hps->hpscan_dev,usb_sndbulkpipe(hps->hpscan_dev, hps->oep), obuf, copy_size, &partial, 30*HZ); dbg("write stats: result:%d copy_size:%lu partial:%d", (int)result, copy_size, partial); if (result == USB_ST_TIMEOUT) { /* NAK -- shouldn't happen */ warn("write_scanner: NAK recieved."); ret = -ETIME; break; } else if (result < 0) { /* We should not get any I/O errors */ warn("write_scanner: funky result: %d. Please notify the maintainer.", result); ret = -EIO; break; } #ifdef WR_DATA_DUMP if (partial) { unsigned char cnt, cnt_max; cnt_max = (partial > 24) ? 24 : partial; printk(KERN_DEBUG __FILE__ ": dump: "); for (cnt=0; cnt < cnt_max; cnt++) { printk("%X ", obuf[cnt]); } printk("\n"); }#endif if (partial != copy_size) { /* Unable to write complete amount */ ret = -EIO; break; } if (partial) { /* Data written */ obuf += partial; count -= partial; bytes_written += partial; } else { /* No data written */ ret = 0; bytes_written = 0; break; } }// mdelay(5); set_current_state(TASK_RUNNING); return ret ? ret : bytes_written;}static ssize_tread_scanner(struct file * file, char * buffer, size_t count, loff_t *ppos){ struct hpscan_usb_data *hps = &hpscan; ssize_t read_count, ret = 0; int partial; int this_read; int result; char *ibuf = hps->ibuf; read_count = 0; set_current_state(TASK_INTERRUPTIBLE); while (count) { if (signal_pending(current)) { ret = -ERESTARTSYS; break; } this_read = (count > IBUF_SIZE) ? IBUF_SIZE : count; result = usb_bulk_msg(hps->hpscan_dev, usb_rcvbulkpipe(hps->hpscan_dev, hps->iep), ibuf, this_read, &partial, 60*HZ); dbg("read stats: result:%d this_read:%u partial:%d", (int)result, this_read, partial); if (result == USB_ST_TIMEOUT) { /* NAK -- shouldn't happen */ warn("read_scanner: NAK received"); ret = -ETIME; break; } else if ((result < 0) && (result != USB_ST_DATAUNDERRUN)) { warn("read_scanner: funky result: %d. Please notify the maintainer.", (int)result); ret = -EIO; break; }#ifdef RD_DATA_DUMP if (partial) { unsigned char cnt, cnt_max; cnt_max = (partial > 24) ? 24 : partial; printk(KERN_DEBUG __FILE__ ": dump: "); for (cnt=0; cnt < cnt_max; cnt++) { printk("%X ", ibuf[cnt]); } printk("\n"); }#endif if (partial) { /* Data returned */ count = this_read = partial; } else { ret = 0; read_count = 0; break; } if (this_read) { if (copy_to_user(buffer, ibuf, this_read)) { ret = -EFAULT; break; } count -= this_read; read_count += this_read; buffer += this_read; } } set_current_state(TASK_RUNNING); return ret ? ret : read_count;}static void *probe_scanner(struct usb_device *dev, unsigned int ifnum){ struct hpscan_usb_data *hps = &hpscan; struct usb_endpoint_descriptor *endpoint; char *ident; hps->present = 0; if (vendor != 0 || product != 0) info("USB Scanner Vendor:Product - %x:%x\n", vendor, product);/* There doesn't seem to be an imaging class defined in the USB * Spec. (yet). If there is, HP isn't following it and it doesn't * look like anybody else is either. Therefore, we have to test the * Vendor and Product ID's to see what we have. This makes this * driver a high maintenance driver since it has to be updated with * each release of a product. Also, other scanners may be able to use * this driver but again, their Vendor and Product ID's must be added. * * NOTE: Just because a product is supported here does not mean that * applications exist that support the product. It's in the hopes * that this will allow developers a means to produce applications * that will support USB products. * * Until we detect a device which is pleasing, we silently punt. * */ if (dev->descriptor.idVendor != 0x03f0 && /* Hewlett Packard */ dev->descriptor.idVendor != 0x06bd && /* AGFA */ dev->descriptor.idVendor != 0x1606 && /* UMAX */ dev->descriptor.idVendor != vendor ) { /* User specified */ return NULL; } if (dev->descriptor.idProduct != 0x0101 && /* HP 4100C */ dev->descriptor.idProduct != 0x0102 && /* HP 4200C & PhotoSmart S20? */ dev->descriptor.idProduct != 0x0202 && /* HP 5100C */ dev->descriptor.idProduct != 0x0401 && /* HP 5200C */ dev->descriptor.idProduct != 0x0201 && /* HP 6200C */ dev->descriptor.idProduct != 0x0601 && /* HP 6300C */ dev->descriptor.idProduct != 0x0001 && /* AGFA SnapScan 1212U */ dev->descriptor.idProduct != 0x0030 && /* Umax 2000U */ dev->descriptor.idProduct != product) { /* User specified */ return NULL; }/* After this point we can be a little noisy about what we are trying to * configure. */ if (dev->descriptor.bNumConfigurations != 1 || dev->config[0].bNumInterfaces != 1) { dbg("probe_scanner: only simple configurations supported"); return NULL; } endpoint = dev->config[0].interface[0].altsetting[0].endpoint; if (endpoint[0].bmAttributes != USB_ENDPOINT_XFER_BULK || endpoint [1].bmAttributes != USB_ENDPOINT_XFER_BULK) { dbg("probe_scanner: invalid bulk endpoints"); return NULL; } if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { dbg("probe_scanner: failed usb_set_configuration"); hps->hpscan_dev = NULL; return NULL; }/* By the time we get here, we should be dealing with a fairly simple * device that supports at least two bulk endpoints on endpoints 1 and * 2. * * We determine the bulk endpoints so that the read_*() and write_*() * procedures can recv/send data to the correct endpoint. * */ hps->iep = hps->oep = 0; if ((endpoint[0].bEndpointAddress & 0x80) == 0x80) { hps->iep = endpoint[0].bEndpointAddress & 0x7f; } else { hps->oep = endpoint[0].bEndpointAddress; } if ((endpoint[1].bEndpointAddress & 0x80) == 0x80) { hps->iep = endpoint[1].bEndpointAddress & 0x7f; } else { hps->oep = endpoint[1].bEndpointAddress; } ident = kmalloc(256, GFP_KERNEL); if (ident) { usb_string(dev, dev->descriptor.iProduct, ident, 256); info("USB Scanner (%s) found at address %d", ident, dev->devnum); kfree(ident); } dbg("probe_scanner: using bulk endpoints - In: %x Out: %x", hps->iep, hps->oep); hps->present = 1; hps->hpscan_dev = dev; if (!(hps->obuf = (char *)kmalloc(OBUF_SIZE, GFP_KERNEL))) { return NULL; } if (!(hps->ibuf = (char *)kmalloc(IBUF_SIZE, GFP_KERNEL))) { return NULL; } return hps;}static voiddisconnect_scanner(struct usb_device *dev, void *ptr){ struct hpscan_usb_data *hps = (struct hpscan_usb_data *) ptr; if (hps->isopen) { /* better let it finish - the release will do whats needed */ hps->hpscan_dev = NULL; return; } kfree(hps->ibuf); kfree(hps->obuf); hps->present = 0;}static structfile_operations usb_scanner_fops = { NULL, /* seek */ read_scanner, write_scanner, NULL, /* readdir */ NULL, /* poll */ NULL, /* ioctl */ NULL, /* mmap */ open_scanner, NULL, /* flush */ close_scanner, NULL, NULL, /* fasync */};static structusb_driver scanner_driver = { "usbscanner", probe_scanner, disconnect_scanner, { NULL, NULL }, &usb_scanner_fops, 48};intusb_scanner_init(void){ if (usb_register(&scanner_driver) < 0) return -1; info("USB Scanner support registered."); return 0;}voidusb_scanner_cleanup(void){ struct hpscan_usb_data *hps = &hpscan; hps->present = 0; usb_deregister(&scanner_driver);}#ifdef MODULEintinit_module(void){ return usb_scanner_init();}voidcleanup_module(void){ usb_scanner_cleanup();}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -