📄 printer.c
字号:
/* * printer.c Version 0.8 * * Copyright (c) 1999 Michael Gee <michael@linuxspecific.com> * Copyright (c) 1999 Pavel Machek <pavel@suse.cz> * Copyright (c) 2000 Randy Dunlap <randy.dunlap@intel.com> * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz> * * 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, paschal@rcsis.com) * v0.8 - add devfs support * v0.9 - fix unplug-while-open paths *//* * 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/smp_lock.h>#include <linux/signal.h>#include <linux/poll.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/lp.h>#include <linux/devfs_fs_kernel.h>#undef DEBUG#include <linux/usb.h>/* * Version Information */#define DRIVER_VERSION "v0.8"#define DRIVER_AUTHOR "Michael Gee, Pavel Machek, Vojtech Pavlik, Randy Dunlap"#define DRIVER_DESC "USB Printer Device Class driver"#define USBLP_BUF_SIZE 8192#define DEVICE_ID_SIZE 1024#define IOCNR_GET_DEVICE_ID 1#define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len) /* get device_id string */#define LPGETSTATUS 0x060b /* same as in drivers/char/lp.c *//* * 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_MINORS 16#define USBLP_MINOR_BASE 0#define USBLP_WRITE_TIMEOUT (5*HZ) /* 5 seconds */struct usblp { struct usb_device *dev; /* USB device */ devfs_handle_t devfs; /* devfs device */ struct semaphore sem; /* locks this struct, especially "dev" */ struct urb readurb, writeurb; /* The urbs */ wait_queue_head_t wait; /* Zzzzz ... */ int readcount; /* Counter for reads */ int ifnum; /* Interface number */ int minor; /* minor number of device */ unsigned int quirks; /* quirks flags */ unsigned char used; /* True if open */ unsigned char bidir; /* interface is bidirectional */ unsigned char *device_id_string; /* IEEE 1284 DEVICE ID string (ptr) */ /* first 2 bytes are (big-endian) length */};extern devfs_handle_t usb_devfs_handle; /* /dev/usb dir. */static struct usblp *usblp_table[USBLP_MINORS];/* 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 */static 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 */ { 0, 0 }};/* * Functions for usblp control messages. */static int usblp_ctrl_msg(struct usblp *usblp, int request, int dir, int recip, int value, void *buf, int len){ int retval = usb_control_msg(usblp->dev, dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0), request, USB_TYPE_CLASS | dir | recip, value, usblp->ifnum, buf, len, HZ * 5); dbg("usblp_control_msg: rq: 0x%02x dir: %d recip: %d value: %d len: %#x result: %d", request, !!dir, recip, value, len, retval); return retval < 0 ? retval : 0;}#define usblp_read_status(usblp, status)\ usblp_ctrl_msg(usblp, USBLP_REQ_GET_STATUS, 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_DIR_IN, USB_RECIP_INTERFACE, config, id, maxlen)#define usblp_reset(usblp)\ usblp_ctrl_msg(usblp, USBLP_REQ_RESET, USB_DIR_OUT, USB_RECIP_OTHER, 0, NULL, 0)/* * URB callback. */static void usblp_bulk(struct urb *urb){ struct usblp *usblp = urb->context; if (!usblp || !usblp->dev || !usblp->used) return; if (urb->status) warn("usblp%d: nonzero read/write bulk status received: %d", usblp->minor, urb->status); wake_up_interruptible(&usblp->wait);}/* * Get and print printer errors. */static 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; error = usblp_read_status (usblp, &status); if (error < 0) { err("usblp%d: error %d reading printer status", usblp->minor, error); return 0; } if (~status & LP_PERRORP) { newerr = 3; if (status & LP_POUTPA) newerr = 1; if (~status & LP_PSELECD) newerr = 2; } if (newerr != err) info("usblp%d: %s", usblp->minor, usblp_messages[newerr]); return newerr;}/* * File op functions. */static int usblp_open(struct inode *inode, struct file *file){ int minor = MINOR(inode->i_rdev) - USBLP_MINOR_BASE; struct usblp *usblp; int retval; if (minor < 0 || minor >= USBLP_MINORS) return -ENODEV; lock_kernel(); usblp = usblp_table[minor]; retval = -ENODEV; if (!usblp || !usblp->dev) goto out; retval = -EBUSY; if (usblp->used) goto out; /* * TODO: need to implement LP_ABORTOPEN + O_NONBLOCK as in drivers/char/lp.c ??? * This is #if 0-ed because we *don't* want to fail an open * just because the printer is off-line. */#if 0 if ((retval = usblp_check_status(usblp, 0))) { retval = retval > 1 ? -EIO : -ENOSPC; goto out; }#else retval = 0;#endif usblp->used = 1; file->private_data = usblp; usblp->writeurb.transfer_buffer_length = 0; usblp->writeurb.status = 0; if (usblp->bidir) { usblp->readcount = 0; usblp->readurb.dev = usblp->dev; if (usb_submit_urb(&usblp->readurb) < 0) { retval = -EIO; usblp->used = 0; file->private_data = NULL; } }out: unlock_kernel(); return retval;}static void usblp_cleanup (struct usblp *usblp){ devfs_unregister (usblp->devfs); usblp_table [usblp->minor] = NULL; info ("usblp%d: removed", usblp->minor); kfree (usblp->writeurb.transfer_buffer); kfree (usblp->device_id_string); kfree (usblp);}static int usblp_release(struct inode *inode, struct file *file){ struct usblp *usblp = file->private_data; down (&usblp->sem); lock_kernel(); usblp->used = 0; if (usblp->dev) { if (usblp->bidir) usb_unlink_urb(&usblp->readurb); usb_unlink_urb(&usblp->writeurb); up(&usblp->sem); } else /* finish cleanup from disconnect */ usblp_cleanup (usblp); unlock_kernel(); return 0;}/* No kernel lock - fine */static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait){ struct usblp *usblp = file->private_data; poll_wait(file, &usblp->wait, wait); return ((!usblp->bidir || usblp->readurb.status == -EINPROGRESS) ? 0 : POLLIN | POLLRDNORM) | (usblp->writeurb.status == -EINPROGRESS ? 0 : POLLOUT | POLLWRNORM);}static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct usblp *usblp = file->private_data; int length, err; unsigned char status; int retval = 0; down (&usblp->sem); if (!usblp->dev) { retval = -ENODEV; goto done; } if (_IOC_TYPE(cmd) == 'P') /* new-style ioctl number */ switch (_IOC_NR(cmd)) { case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */ if (_IOC_DIR(cmd) != _IOC_READ) { retval = -EINVAL; goto done; } err = usblp_get_id(usblp, 0, usblp->device_id_string, DEVICE_ID_SIZE - 1); if (err < 0) { dbg ("usblp%d: error = %d reading IEEE-1284 Device ID string", usblp->minor, err); usblp->device_id_string[0] = usblp->device_id_string[1] = '\0'; retval = -EIO; goto done; } length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; /* big-endian */ if (length < DEVICE_ID_SIZE) usblp->device_id_string[length] = '\0'; else usblp->device_id_string[DEVICE_ID_SIZE - 1] = '\0'; dbg ("usblp%d Device ID string [%d/max %d]='%s'", usblp->minor, length, _IOC_SIZE(cmd), &usblp->device_id_string[2]); if (length > _IOC_SIZE(cmd)) length = _IOC_SIZE(cmd); /* truncate */ if (copy_to_user((unsigned char *) arg, usblp->device_id_string, (unsigned long) length)) { retval = -EFAULT; goto done; } break; default: retval = -EINVAL; } else /* old-style ioctl value */ switch (cmd) { case LPGETSTATUS: if (usblp_read_status(usblp, &status)) { err("usblp%d: failed reading printer status", usblp->minor); retval = -EIO; goto done; } if (copy_to_user ((unsigned char *)arg, &status, 1)) retval = -EFAULT; break; default: retval = -EINVAL; }done: up (&usblp->sem); return retval;}static ssize_t usblp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos){ struct usblp *usblp = file->private_data; int timeout, err = 0, writecount = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -