usblp.c
来自「linux 内核源代码」· C语言 代码 · 共 1,436 行 · 第 1/3 页
C
1,436 行
/* * usblp.c * * Copyright (c) 1999 Michael Gee <michael@linuxspecific.com> * Copyright (c) 1999 Pavel Machek <pavel@suse.cz> * Copyright (c) 2000 Randy Dunlap <rdunlap@xenotime.net> * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz> # Copyright (c) 2001 Pete Zaitcev <zaitcev@redhat.com> # Copyright (c) 2001 David Paschal <paschal@rcsis.com> * Copyright (c) 2006 Oliver Neukum <oliver@neukum.name> * * USB Printer Device Class driver for USB printers and printer cables * * Sponsored by SuSE * * ChangeLog: * v0.1 - thorough cleaning, URBification, almost a rewrite * v0.2 - some more cleanups * v0.3 - cleaner again, waitqueue fixes * v0.4 - fixes in unidirectional mode * v0.5 - add DEVICE_ID string support * v0.6 - never time out * v0.7 - fixed bulk-IN read and poll (David Paschal) * v0.8 - add devfs support * v0.9 - fix unplug-while-open paths * v0.10- remove sleep_on, fix error on oom (oliver@neukum.org) * v0.11 - add proto_bias option (Pete Zaitcev) * v0.12 - add hpoj.sourceforge.net ioctls (David Paschal) * v0.13 - alloc space for statusbuf (<status> not on stack); * use usb_buffer_alloc() for read buf & write buf; * none - Maintained in Linux kernel after v0.13 *//* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/signal.h>#include <linux/poll.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/lp.h>#include <linux/mutex.h>#undef DEBUG#include <linux/usb.h>/* * Version Information */#define DRIVER_AUTHOR "Michael Gee, Pavel Machek, Vojtech Pavlik, Randy Dunlap, Pete Zaitcev, David Paschal"#define DRIVER_DESC "USB Printer Device Class driver"#define USBLP_BUF_SIZE 8192#define USBLP_BUF_SIZE_IN 1024#define USBLP_DEVICE_ID_SIZE 1024/* ioctls: */#define IOCNR_GET_DEVICE_ID 1#define IOCNR_GET_PROTOCOLS 2#define IOCNR_SET_PROTOCOL 3#define IOCNR_HP_SET_CHANNEL 4#define IOCNR_GET_BUS_ADDRESS 5#define IOCNR_GET_VID_PID 6#define IOCNR_SOFT_RESET 7/* Get device_id string: */#define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len)/* The following ioctls were added for http://hpoj.sourceforge.net: *//* Get two-int array: * [0]=current protocol (1=7/1/1, 2=7/1/2, 3=7/1/3), * [1]=supported protocol mask (mask&(1<<n)!=0 means 7/1/n supported): */#define LPIOC_GET_PROTOCOLS(len) _IOC(_IOC_READ, 'P', IOCNR_GET_PROTOCOLS, len)/* Set protocol (arg: 1=7/1/1, 2=7/1/2, 3=7/1/3): */#define LPIOC_SET_PROTOCOL _IOC(_IOC_WRITE, 'P', IOCNR_SET_PROTOCOL, 0)/* Set channel number (HP Vendor-specific command): */#define LPIOC_HP_SET_CHANNEL _IOC(_IOC_WRITE, 'P', IOCNR_HP_SET_CHANNEL, 0)/* Get two-int array: [0]=bus number, [1]=device address: */#define LPIOC_GET_BUS_ADDRESS(len) _IOC(_IOC_READ, 'P', IOCNR_GET_BUS_ADDRESS, len)/* Get two-int array: [0]=vendor ID, [1]=product ID: */#define LPIOC_GET_VID_PID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_VID_PID, len)/* Perform class specific soft reset */#define LPIOC_SOFT_RESET _IOC(_IOC_NONE, 'P', IOCNR_SOFT_RESET, 0);/* * A DEVICE_ID string may include the printer's serial number. * It should end with a semi-colon (';'). * An example from an HP 970C DeskJet printer is (this is one long string, * with the serial number changed):MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:Hewlett-Packard DeskJet 970C;SERN:US970CSEPROF;VSTATUS:$HB0$NC0,ff,DN,IDLE,CUT,K1,C0,DP,NR,KP000,CP027;VP:0800,FL,B0;VJ: ; *//* * USB Printer Requests */#define USBLP_REQ_GET_ID 0x00#define USBLP_REQ_GET_STATUS 0x01#define USBLP_REQ_RESET 0x02#define USBLP_REQ_HP_CHANNEL_CHANGE_REQUEST 0x00 /* HP Vendor-specific */#define USBLP_MINORS 16#define USBLP_MINOR_BASE 0#define USBLP_CTL_TIMEOUT 5000 /* 5 seconds */#define USBLP_FIRST_PROTOCOL 1#define USBLP_LAST_PROTOCOL 3#define USBLP_MAX_PROTOCOLS (USBLP_LAST_PROTOCOL+1)/* * some arbitrary status buffer size; * need a status buffer that is allocated via kmalloc(), not on stack */#define STATUS_BUF_SIZE 8/* * Locks down the locking order: * ->wmut locks wstatus. * ->mut locks the whole usblp, except [rw]complete, and thus, by indirection, * [rw]status. We only touch status when we know the side idle. * ->lock locks what interrupt accesses. */struct usblp { struct usb_device *dev; /* USB device */ struct mutex wmut; struct mutex mut; spinlock_t lock; /* locks rcomplete, wcomplete */ char *readbuf; /* read transfer_buffer */ char *statusbuf; /* status transfer_buffer */ struct usb_anchor urbs; wait_queue_head_t rwait, wwait; int readcount; /* Counter for reads */ int ifnum; /* Interface number */ struct usb_interface *intf; /* The interface */ /* Alternate-setting numbers and endpoints for each protocol * (7/1/{index=1,2,3}) that the device supports: */ struct { int alt_setting; struct usb_endpoint_descriptor *epwrite; struct usb_endpoint_descriptor *epread; } protocol[USBLP_MAX_PROTOCOLS]; int current_protocol; int minor; /* minor number of device */ int wcomplete, rcomplete; int wstatus; /* bytes written or error */ int rstatus; /* bytes ready or error */ unsigned int quirks; /* quirks flags */ unsigned int flags; /* mode flags */ unsigned char used; /* True if open */ unsigned char present; /* True if not disconnected */ unsigned char bidir; /* interface is bidirectional */ unsigned char sleeping; /* interface is suspended */ unsigned char no_paper; /* Paper Out happened */ unsigned char *device_id_string; /* IEEE 1284 DEVICE ID string (ptr) */ /* first 2 bytes are (big-endian) length */};#ifdef DEBUGstatic void usblp_dump(struct usblp *usblp) { int p; dbg("usblp=0x%p", usblp); dbg("dev=0x%p", usblp->dev); dbg("present=%d", usblp->present); dbg("readbuf=0x%p", usblp->readbuf); dbg("readcount=%d", usblp->readcount); dbg("ifnum=%d", usblp->ifnum); for (p = USBLP_FIRST_PROTOCOL; p <= USBLP_LAST_PROTOCOL; p++) { dbg("protocol[%d].alt_setting=%d", p, usblp->protocol[p].alt_setting); dbg("protocol[%d].epwrite=%p", p, usblp->protocol[p].epwrite); dbg("protocol[%d].epread=%p", p, usblp->protocol[p].epread); } dbg("current_protocol=%d", usblp->current_protocol); dbg("minor=%d", usblp->minor); dbg("wstatus=%d", usblp->wstatus); dbg("rstatus=%d", usblp->rstatus); dbg("quirks=%d", usblp->quirks); dbg("used=%d", usblp->used); dbg("bidir=%d", usblp->bidir); dbg("sleeping=%d", usblp->sleeping); dbg("device_id_string=\"%s\"", usblp->device_id_string ? usblp->device_id_string + 2 : (unsigned char *)"(null)");}#endif/* Quirks: various printer quirks are handled by this table & its flags. */struct quirk_printer_struct { __u16 vendorId; __u16 productId; unsigned int quirks;};#define USBLP_QUIRK_BIDIR 0x1 /* reports bidir but requires unidirectional mode (no INs/reads) */#define USBLP_QUIRK_USB_INIT 0x2 /* needs vendor USB init string */#define USBLP_QUIRK_BAD_CLASS 0x4 /* descriptor uses vendor-specific Class or SubClass */static const struct quirk_printer_struct quirk_printers[] = { { 0x03f0, 0x0004, USBLP_QUIRK_BIDIR }, /* HP DeskJet 895C */ { 0x03f0, 0x0104, USBLP_QUIRK_BIDIR }, /* HP DeskJet 880C */ { 0x03f0, 0x0204, USBLP_QUIRK_BIDIR }, /* HP DeskJet 815C */ { 0x03f0, 0x0304, USBLP_QUIRK_BIDIR }, /* HP DeskJet 810C/812C */ { 0x03f0, 0x0404, USBLP_QUIRK_BIDIR }, /* HP DeskJet 830C */ { 0x03f0, 0x0504, USBLP_QUIRK_BIDIR }, /* HP DeskJet 885C */ { 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */ { 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */ { 0x03f0, 0x1104, USBLP_QUIRK_BIDIR }, /* HP Deskjet 959C */ { 0x0409, 0xefbe, USBLP_QUIRK_BIDIR }, /* NEC Picty900 (HP OEM) */ { 0x0409, 0xbef4, USBLP_QUIRK_BIDIR }, /* NEC Picty760 (HP OEM) */ { 0x0409, 0xf0be, USBLP_QUIRK_BIDIR }, /* NEC Picty920 (HP OEM) */ { 0x0409, 0xf1be, USBLP_QUIRK_BIDIR }, /* NEC Picty800 (HP OEM) */ { 0x0482, 0x0010, USBLP_QUIRK_BIDIR }, /* Kyocera Mita FS 820, by zut <kernel@zut.de> */ { 0x04b8, 0x0202, USBLP_QUIRK_BAD_CLASS }, /* Seiko Epson Receipt Printer M129C */ { 0, 0 }};static int usblp_wwait(struct usblp *usblp, int nonblock);static int usblp_wtest(struct usblp *usblp, int nonblock);static int usblp_rwait_and_lock(struct usblp *usblp, int nonblock);static int usblp_rtest(struct usblp *usblp, int nonblock);static int usblp_submit_read(struct usblp *usblp);static int usblp_select_alts(struct usblp *usblp);static int usblp_set_protocol(struct usblp *usblp, int protocol);static int usblp_cache_device_id_string(struct usblp *usblp);/* forward reference to make our lives easier */static struct usb_driver usblp_driver;static DEFINE_MUTEX(usblp_mutex); /* locks the existence of usblp's *//* * Functions for usblp control messages. */static int usblp_ctrl_msg(struct usblp *usblp, int request, int type, int dir, int recip, int value, void *buf, int len){ int retval; int index = usblp->ifnum; /* High byte has the interface index. Low byte has the alternate setting. */ if ((request == USBLP_REQ_GET_ID) && (type == USB_TYPE_CLASS)) { index = (usblp->ifnum<<8)|usblp->protocol[usblp->current_protocol].alt_setting; } retval = usb_control_msg(usblp->dev, dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0), request, type | dir | recip, value, index, buf, len, USBLP_CTL_TIMEOUT); dbg("usblp_control_msg: rq: 0x%02x dir: %d recip: %d value: %d idx: %d len: %#x result: %d", request, !!dir, recip, value, index, len, retval); return retval < 0 ? retval : 0;}#define usblp_read_status(usblp, status)\ usblp_ctrl_msg(usblp, USBLP_REQ_GET_STATUS, USB_TYPE_CLASS, USB_DIR_IN, USB_RECIP_INTERFACE, 0, status, 1)#define usblp_get_id(usblp, config, id, maxlen)\ usblp_ctrl_msg(usblp, USBLP_REQ_GET_ID, USB_TYPE_CLASS, USB_DIR_IN, USB_RECIP_INTERFACE, config, id, maxlen)#define usblp_reset(usblp)\ usblp_ctrl_msg(usblp, USBLP_REQ_RESET, USB_TYPE_CLASS, USB_DIR_OUT, USB_RECIP_OTHER, 0, NULL, 0)#define usblp_hp_channel_change_request(usblp, channel, buffer) \ usblp_ctrl_msg(usblp, USBLP_REQ_HP_CHANNEL_CHANGE_REQUEST, USB_TYPE_VENDOR, USB_DIR_IN, USB_RECIP_INTERFACE, channel, buffer, 1)/* * See the description for usblp_select_alts() below for the usage * explanation. Look into your /proc/bus/usb/devices and dmesg in * case of any trouble. */static int proto_bias = -1;/* * URB callback. */static void usblp_bulk_read(struct urb *urb){ struct usblp *usblp = urb->context; int status = urb->status; if (usblp->present && usblp->used) { if (status) printk(KERN_WARNING "usblp%d: " "nonzero read bulk status received: %d\n", usblp->minor, status); } spin_lock(&usblp->lock); if (status < 0) usblp->rstatus = status; else usblp->rstatus = urb->actual_length; usblp->rcomplete = 1; wake_up(&usblp->rwait); spin_unlock(&usblp->lock); usb_free_urb(urb);}static void usblp_bulk_write(struct urb *urb){ struct usblp *usblp = urb->context; int status = urb->status; if (usblp->present && usblp->used) { if (status) printk(KERN_WARNING "usblp%d: " "nonzero write bulk status received: %d\n", usblp->minor, status); } spin_lock(&usblp->lock); if (status < 0) usblp->wstatus = status; else usblp->wstatus = urb->actual_length; usblp->no_paper = 0; usblp->wcomplete = 1; wake_up(&usblp->wwait); spin_unlock(&usblp->lock); usb_free_urb(urb);}/* * Get and print printer errors. */static const char *usblp_messages[] = { "ok", "out of paper", "off-line", "on fire" };static int usblp_check_status(struct usblp *usblp, int err){ unsigned char status, newerr = 0; int error; mutex_lock(&usblp->mut); if ((error = usblp_read_status(usblp, usblp->statusbuf)) < 0) { mutex_unlock(&usblp->mut); if (printk_ratelimit()) printk(KERN_ERR "usblp%d: error %d reading printer status\n", usblp->minor, error); return 0; } status = *usblp->statusbuf; mutex_unlock(&usblp->mut); if (~status & LP_PERRORP) newerr = 3; if (status & LP_POUTPA) newerr = 1; if (~status & LP_PSELECD) newerr = 2; if (newerr != err) { printk(KERN_INFO "usblp%d: %s\n", usblp->minor, usblp_messages[newerr]); } return newerr;}static int handle_bidir (struct usblp *usblp){ if (usblp->bidir && usblp->used && !usblp->sleeping) { if (usblp_submit_read(usblp) < 0) return -EIO; } return 0;}/* * File op functions. */static int usblp_open(struct inode *inode, struct file *file){ int minor = iminor(inode); struct usblp *usblp; struct usb_interface *intf; int retval; if (minor < 0) return -ENODEV; mutex_lock (&usblp_mutex); retval = -ENODEV; intf = usb_find_interface(&usblp_driver, minor); if (!intf) { goto out; } usblp = usb_get_intfdata (intf); if (!usblp || !usblp->dev || !usblp->present) goto out; retval = -EBUSY; if (usblp->used) goto out; /* * We do not implement LP_ABORTOPEN/LPABORTOPEN for two reasons: * - We do not want persistent state which close(2) does not clear * - It is not used anyway, according to CUPS people */ retval = usb_autopm_get_interface(intf); if (retval < 0) goto out; usblp->used = 1; file->private_data = usblp; usblp->wcomplete = 1; /* we begin writeable */ usblp->wstatus = 0; usblp->rcomplete = 0; if (handle_bidir(usblp) < 0) { usblp->used = 0; file->private_data = NULL; retval = -EIO; }out: mutex_unlock (&usblp_mutex); return retval;}static void usblp_cleanup (struct usblp *usblp){ printk(KERN_INFO "usblp%d: removed\n", usblp->minor); kfree(usblp->readbuf); kfree (usblp->device_id_string); kfree (usblp->statusbuf); kfree (usblp);}static void usblp_unlink_urbs(struct usblp *usblp){ usb_kill_anchored_urbs(&usblp->urbs);}static int usblp_release(struct inode *inode, struct file *file){ struct usblp *usblp = file->private_data; usblp->flags &= ~LP_ABORT; mutex_lock (&usblp_mutex); usblp->used = 0; if (usblp->present) { usblp_unlink_urbs(usblp); usb_autopm_put_interface(usblp->intf); } else /* finish cleanup from disconnect */ usblp_cleanup (usblp); mutex_unlock (&usblp_mutex); return 0;}/* No kernel lock - fine */static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait){ int ret; unsigned long flags; struct usblp *usblp = file->private_data; /* Should we check file->f_mode & FMODE_WRITE before poll_wait()? */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?