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

📄 printer.c

📁 S3C2440ARM9开发板的USB驱动程序
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * printer.c  Version 0.11 * * 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> # Copyright (c) 2001 Pete Zaitcev	<zaitcev@redhat.com> # Copyright (c) 2001 David Paschal	<paschal@rcsis.com> * * 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 - add proto_bias option (Pete Zaitcev) *	v0.11 - add hpoj.sourceforge.net ioctls (David Paschal) *//* * 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.11"#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 DEVICE_ID_SIZE		1024/* ioctls: */#define LPGETSTATUS		0x060b		/* same as in drivers/char/lp.c */#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/* 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)/* * 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_WRITE_TIMEOUT	(5*HZ)			/* 5 seconds */#define USBLP_FIRST_PROTOCOL	1#define USBLP_LAST_PROTOCOL	3#define USBLP_MAX_PROTOCOLS	(USBLP_LAST_PROTOCOL+1)struct usblp {	struct usb_device 	*dev;			/* USB device */	devfs_handle_t		devfs;			/* devfs device */	struct semaphore	sem;			/* locks this struct, especially "dev" */	char			*buf;		/* writeurb.transfer_buffer */	struct urb		readurb, writeurb;	/* The urbs */	wait_queue_head_t	wait;			/* Zzzzz ... */	int			readcount;		/* Counter for reads */	int			ifnum;			/* Interface number */	/* 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 */	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 */};#ifdef DEBUGstatic void usblp_dump(struct usblp *usblp) {	int p;	dbg("usblp=0x%p", usblp);	dbg("dev=0x%p", usblp->dev);	dbg("devfs=0x%p", usblp->devfs);	dbg("buf=0x%p", usblp->buf);	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("quirks=%d", usblp->quirks);	dbg("used=%d", usblp->used);	dbg("bidir=%d", usblp->bidir);	dbg("device_id_string=\"%s\"",		usblp->device_id_string ?			usblp->device_id_string + 2 :			(unsigned char *)"(null)");}#endifextern 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 */	{ 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) */	{ 0, 0 }};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);/* * 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 = usb_control_msg(usblp->dev,		dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0),		request, type | dir | recip, value, usblp->ifnum, buf, len, USBLP_WRITE_TIMEOUT);	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_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(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", "unknown error" };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 void usblp_unlink_urbs(struct usblp *usblp){	usb_unlink_urb(&usblp->writeurb);	if (usblp->bidir)		usb_unlink_urb(&usblp->readurb);}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) {		usblp_unlink_urbs(usblp);		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, i;	unsigned char lpstatus, newChannel;	int status;	int twoints[2];	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;				}				length = usblp_cache_device_id_string(usblp);				if (length < 0) {					retval = length;					goto done;				}				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;			case IOCNR_GET_PROTOCOLS:				if (_IOC_DIR(cmd) != _IOC_READ ||				    _IOC_SIZE(cmd) < sizeof(twoints)) {					retval = -EINVAL;					goto done;				}				twoints[0] = usblp->current_protocol;				twoints[1] = 0;				for (i = USBLP_FIRST_PROTOCOL;				     i <= USBLP_LAST_PROTOCOL; i++) {					if (usblp->protocol[i].alt_setting >= 0)						twoints[1] |= (1<<i);				}				if (copy_to_user((unsigned char *)arg,						(unsigned char *)twoints,						sizeof(twoints))) {					retval = -EFAULT;					goto done;				}				break;			case IOCNR_SET_PROTOCOL:				if (_IOC_DIR(cmd) != _IOC_WRITE) {					retval = -EINVAL;					goto done;				}#ifdef DEBUG				if (arg == -10) {					usblp_dump(usblp);					break;				}#endif				usblp_unlink_urbs(usblp);				retval = usblp_set_protocol(usblp, arg);				if (retval < 0) {					usblp_set_protocol(usblp,						usblp->current_protocol);				}				break;			case IOCNR_HP_SET_CHANNEL:				if (_IOC_DIR(cmd) != _IOC_WRITE ||				    usblp->dev->descriptor.idVendor != 0x03F0 ||				    usblp->quirks & USBLP_QUIRK_BIDIR) {					retval = -EINVAL;					goto done;				}				err = usblp_hp_channel_change_request(usblp,					arg, &newChannel);				if (err < 0) {					err("usblp%d: error = %d setting "						"HP channel",						usblp->minor, err);					retval = -EIO;					goto done;				}				dbg("usblp%d requested/got HP channel %ld/%d",					usblp->minor, arg, newChannel);				break;			case IOCNR_GET_BUS_ADDRESS:				if (_IOC_DIR(cmd) != _IOC_READ ||				    _IOC_SIZE(cmd) < sizeof(twoints)) {					retval = -EINVAL;					goto done;				}				twoints[0] = usblp->dev->bus->busnum;				twoints[1] = usblp->dev->devnum;				if (copy_to_user((unsigned char *)arg,						(unsigned char *)twoints,						sizeof(twoints))) {					retval = -EFAULT;					goto done;				}				dbg("usblp%d is bus=%d, device=%d",					usblp->minor, twoints[0], twoints[1]);				break;			case IOCNR_GET_VID_PID:				if (_IOC_DIR(cmd) != _IOC_READ ||				    _IOC_SIZE(cmd) < sizeof(twoints)) {					retval = -EINVAL;					goto done;				}				twoints[0] = usblp->dev->descriptor.idVendor;				twoints[1] = usblp->dev->descriptor.idProduct;				if (copy_to_user((unsigned char *)arg,						(unsigned char *)twoints,						sizeof(twoints))) {					retval = -EFAULT;					goto done;				}

⌨️ 快捷键说明

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