usblp.c 32.3 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
2
 * usblp.c  Version 0.13
Linus Torvalds's avatar
Linus Torvalds committed
3 4 5
 *
 * Copyright (c) 1999 Michael Gee	<michael@linuxspecific.com>
 * Copyright (c) 1999 Pavel Machek	<pavel@suse.cz>
6
 * Copyright (c) 2000 Randy Dunlap	<rddunlap@osdl.org>
Linus Torvalds's avatar
Linus Torvalds committed
7
 * Copyright (c) 2000 Vojtech Pavlik	<vojtech@suse.cz>
David Paschal's avatar
David Paschal committed
8 9
 # Copyright (c) 2001 Pete Zaitcev	<zaitcev@redhat.com>
 # Copyright (c) 2001 David Paschal	<paschal@rcsis.com>
Linus Torvalds's avatar
Linus Torvalds committed
10 11 12 13 14 15 16 17 18 19 20 21
 *
 * 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
David Paschal's avatar
David Paschal committed
22
 *	v0.7 - fixed bulk-IN read and poll (David Paschal)
Linus Torvalds's avatar
Linus Torvalds committed
23
 *	v0.8 - add devfs support
Linus Torvalds's avatar
Linus Torvalds committed
24
 *	v0.9 - fix unplug-while-open paths
25
 *	v0.10- remove sleep_on, fix error on oom (oliver@neukum.org)
David Paschal's avatar
David Paschal committed
26 27
 *	v0.11 - add proto_bias option (Pete Zaitcev)
 *	v0.12 - add hpoj.sourceforge.net ioctls (David Paschal)
28 29
 *	v0.13 - alloc space for statusbuf (<status> not on stack);
 *		use usb_buffer_alloc() for read buf & write buf;
Linus Torvalds's avatar
Linus Torvalds committed
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
 */

/*
 * 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>
Linus Torvalds's avatar
Linus Torvalds committed
55
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
56 57 58 59
#include <linux/lp.h>
#undef DEBUG
#include <linux/usb.h>

Linus Torvalds's avatar
Linus Torvalds committed
60 61 62
/*
 * Version Information
 */
63
#define DRIVER_VERSION "v0.13"
David Paschal's avatar
David Paschal committed
64
#define DRIVER_AUTHOR "Michael Gee, Pavel Machek, Vojtech Pavlik, Randy Dunlap, Pete Zaitcev, David Paschal"
Linus Torvalds's avatar
Linus Torvalds committed
65 66
#define DRIVER_DESC "USB Printer Device Class driver"

Linus Torvalds's avatar
Linus Torvalds committed
67
#define USBLP_BUF_SIZE		8192
David S. Miller's avatar
David S. Miller committed
68
#define USBLP_DEVICE_ID_SIZE	1024
Linus Torvalds's avatar
Linus Torvalds committed
69

David Paschal's avatar
David Paschal committed
70
/* ioctls: */
Linus Torvalds's avatar
Linus Torvalds committed
71
#define LPGETSTATUS		0x060b		/* same as in drivers/char/lp.c */
David Paschal's avatar
David Paschal committed
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
#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)
Linus Torvalds's avatar
Linus Torvalds committed
93 94 95 96 97 98 99 100 101 102 103 104 105

/*
 * 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
 */

David Paschal's avatar
David Paschal committed
106 107 108 109
#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 */
Linus Torvalds's avatar
Linus Torvalds committed
110 111 112 113 114 115

#define USBLP_MINORS		16
#define USBLP_MINOR_BASE	0

#define USBLP_WRITE_TIMEOUT	(5*HZ)			/* 5 seconds */

David Paschal's avatar
David Paschal committed
116 117 118 119
#define USBLP_FIRST_PROTOCOL	1
#define USBLP_LAST_PROTOCOL	3
#define USBLP_MAX_PROTOCOLS	(USBLP_LAST_PROTOCOL+1)

120 121 122 123 124 125
/*
 * some arbitrary status buffer size;
 * need a status buffer that is allocated via kmalloc(), not on stack
 */
#define STATUS_BUF_SIZE		8

Linus Torvalds's avatar
Linus Torvalds committed
126 127
struct usblp {
	struct usb_device 	*dev;			/* USB device */
Linus Torvalds's avatar
Linus Torvalds committed
128
	struct semaphore	sem;			/* locks this struct, especially "dev" */
129 130 131
	char			*writebuf;		/* write transfer_buffer */
	char			*readbuf;		/* read transfer_buffer */
	char			*statusbuf;		/* status transfer_buffer */
Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
132
	struct urb		*readurb, *writeurb;	/* The urbs */
Linus Torvalds's avatar
Linus Torvalds committed
133 134 135
	wait_queue_head_t	wait;			/* Zzzzz ... */
	int			readcount;		/* Counter for reads */
	int			ifnum;			/* Interface number */
David Paschal's avatar
David Paschal committed
136 137 138 139 140 141 142 143
	/* 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;
Linus Torvalds's avatar
Linus Torvalds committed
144
	int			minor;			/* minor number of device */
145
	int			wcomplete;		/* writing is completed */
David Paschal's avatar
David Paschal committed
146
	int			rcomplete;		/* reading is completed */
Linus Torvalds's avatar
Linus Torvalds committed
147 148
	unsigned int		quirks;			/* quirks flags */
	unsigned char		used;			/* True if open */
149
	unsigned char		present;		/* True if not disconnected */
Linus Torvalds's avatar
Linus Torvalds committed
150 151 152 153 154
	unsigned char		bidir;			/* interface is bidirectional */
	unsigned char		*device_id_string;	/* IEEE 1284 DEVICE ID string (ptr) */
							/* first 2 bytes are (big-endian) length */
};

David Paschal's avatar
David Paschal committed
155 156 157 158 159 160
#ifdef DEBUG
static void usblp_dump(struct usblp *usblp) {
	int p;

	dbg("usblp=0x%p", usblp);
	dbg("dev=0x%p", usblp->dev);
161
	dbg("present=%d", usblp->present);
162 163 164 165
	dbg("readbuf=0x%p", usblp->readbuf);
	dbg("writebuf=0x%p", usblp->writebuf);
	dbg("readurb=0x%p", usblp->readurb);
	dbg("writeurb=0x%p", usblp->writeurb);
David Paschal's avatar
David Paschal committed
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
	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("wcomplete=%d", usblp->wcomplete);
	dbg("rcomplete=%d", usblp->rcomplete);
	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)");
}
#endif

Linus Torvalds's avatar
Linus Torvalds committed
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
/* 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 */
David Paschal's avatar
David Paschal committed
204 205 206
	{ 0x03f0, 0x0504, USBLP_QUIRK_BIDIR }, /* HP DeskJet 885C */
	{ 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */   
	{ 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */   
Kai Reichert's avatar
Kai Reichert committed
207
	{ 0x03f0, 0x1104, USBLP_QUIRK_BIDIR }, /* HP Deskjet 959C */
Taisuke Yamada's avatar
Taisuke Yamada committed
208
	{ 0x0409, 0xefbe, USBLP_QUIRK_BIDIR }, /* NEC Picty900 (HP OEM) */
David Paschal's avatar
David Paschal committed
209 210 211
	{ 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) */
Linus Torvalds's avatar
Linus Torvalds committed
212 213 214
	{ 0, 0 }
};

David Paschal's avatar
David Paschal committed
215 216 217 218
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);

219 220
/* forward reference to make our lives easier */
extern struct usb_driver usblp_driver;
David Paschal's avatar
David Paschal committed
221

Linus Torvalds's avatar
Linus Torvalds committed
222 223 224 225
/*
 * Functions for usblp control messages.
 */

David Paschal's avatar
David Paschal committed
226
static int usblp_ctrl_msg(struct usblp *usblp, int request, int type, int dir, int recip, int value, void *buf, int len)
Linus Torvalds's avatar
Linus Torvalds committed
227 228 229
{
	int retval = usb_control_msg(usblp->dev,
		dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0),
David Paschal's avatar
David Paschal committed
230
		request, type | dir | recip, value, usblp->ifnum, buf, len, USBLP_WRITE_TIMEOUT);
Linus Torvalds's avatar
Linus Torvalds committed
231 232 233 234 235 236
	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)\
David Paschal's avatar
David Paschal committed
237
	usblp_ctrl_msg(usblp, USBLP_REQ_GET_STATUS, USB_TYPE_CLASS, USB_DIR_IN, USB_RECIP_INTERFACE, 0, status, 1)
Linus Torvalds's avatar
Linus Torvalds committed
238
#define usblp_get_id(usblp, config, id, maxlen)\
David Paschal's avatar
David Paschal committed
239
	usblp_ctrl_msg(usblp, USBLP_REQ_GET_ID, USB_TYPE_CLASS, USB_DIR_IN, USB_RECIP_INTERFACE, config, id, maxlen)
Linus Torvalds's avatar
Linus Torvalds committed
240
#define usblp_reset(usblp)\
David Paschal's avatar
David Paschal committed
241 242 243 244 245 246 247 248 249 250 251
	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;
Linus Torvalds's avatar
Linus Torvalds committed
252 253 254 255 256

/*
 * URB callback.
 */

David S. Miller's avatar
David S. Miller committed
257
static void usblp_bulk_read(struct urb *urb, struct pt_regs *regs)
Linus Torvalds's avatar
Linus Torvalds committed
258 259 260
{
	struct usblp *usblp = urb->context;

261
	if (!usblp || !usblp->dev || !usblp->used || !usblp->present)
Linus Torvalds's avatar
Linus Torvalds committed
262 263
		return;

264
	if (unlikely(urb->status))
Linus Torvalds's avatar
Linus Torvalds committed
265 266
		warn("usblp%d: nonzero read/write bulk status received: %d",
			usblp->minor, urb->status);
267 268 269
	usblp->rcomplete = 1;
	wake_up_interruptible(&usblp->wait);
}
Linus Torvalds's avatar
Linus Torvalds committed
270

David S. Miller's avatar
David S. Miller committed
271
static void usblp_bulk_write(struct urb *urb, struct pt_regs *regs)
272 273 274
{
	struct usblp *usblp = urb->context;

275
	if (!usblp || !usblp->dev || !usblp->used || !usblp->present)
276 277 278 279 280 281
		return;

	if (unlikely(urb->status))
		warn("usblp%d: nonzero read/write bulk status received: %d",
			usblp->minor, urb->status);
	usblp->wcomplete = 1;
Linus Torvalds's avatar
Linus Torvalds committed
282 283 284 285 286 287 288 289 290 291 292 293
	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;
Linus Torvalds's avatar
Linus Torvalds committed
294
	int error;
Linus Torvalds's avatar
Linus Torvalds committed
295

296
	error = usblp_read_status (usblp, usblp->statusbuf);
Linus Torvalds's avatar
Linus Torvalds committed
297 298 299
	if (error < 0) {
		err("usblp%d: error %d reading printer status",
			usblp->minor, error);
Linus Torvalds's avatar
Linus Torvalds committed
300 301 302
		return 0;
	}

303
	status = *usblp->statusbuf;
304 305

	if (~status & LP_PERRORP)
Linus Torvalds's avatar
Linus Torvalds committed
306
		newerr = 3;
307 308 309 310
	if (status & LP_POUTPA)
		newerr = 1;
	if (~status & LP_PSELECD)
		newerr = 2;
Linus Torvalds's avatar
Linus Torvalds committed
311 312 313 314 315 316 317 318 319 320 321 322 323

	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)
{
324
	int minor = iminor(inode);
Linus Torvalds's avatar
Linus Torvalds committed
325
	struct usblp *usblp;
326
	struct usb_interface *intf;
Linus Torvalds's avatar
Linus Torvalds committed
327 328
	int retval;

329
	if (minor < 0)
Linus Torvalds's avatar
Linus Torvalds committed
330 331 332 333 334
		return -ENODEV;

	lock_kernel();

	retval = -ENODEV;
335
	intf = usb_find_interface(&usblp_driver, minor);
336 337 338
	if (!intf) {
		goto out;
	}
339
	usblp = usb_get_intfdata (intf);
340
	if (!usblp || !usblp->dev || !usblp->present)
Linus Torvalds's avatar
Linus Torvalds committed
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
		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
Linus Torvalds's avatar
Linus Torvalds committed
358
	retval = 0;
Linus Torvalds's avatar
Linus Torvalds committed
359 360 361 362 363
#endif

	usblp->used = 1;
	file->private_data = usblp;

Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
364
	usblp->writeurb->transfer_buffer_length = 0;
365 366
	usblp->wcomplete = 1; /* we begin writeable */
	usblp->rcomplete = 0;
Linus Torvalds's avatar
Linus Torvalds committed
367 368 369

	if (usblp->bidir) {
		usblp->readcount = 0;
Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
370 371
		usblp->readurb->dev = usblp->dev;
		if (usb_submit_urb(usblp->readurb, GFP_KERNEL) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
372 373 374 375
			retval = -EIO;
			usblp->used = 0;
			file->private_data = NULL;
		}
Linus Torvalds's avatar
Linus Torvalds committed
376 377 378 379 380 381
	}
out:
	unlock_kernel();
	return retval;
}

Linus Torvalds's avatar
Linus Torvalds committed
382 383
static void usblp_cleanup (struct usblp *usblp)
{
David Paschal's avatar
David Paschal committed
384
	info("usblp%d: removed", usblp->minor);
Linus Torvalds's avatar
Linus Torvalds committed
385

386 387 388
	usb_buffer_free (usblp->dev, USBLP_BUF_SIZE,
			usblp->writebuf, usblp->writeurb->transfer_dma);
	usb_buffer_free (usblp->dev, USBLP_BUF_SIZE,
389
			usblp->readbuf, usblp->readurb->transfer_dma);
Linus Torvalds's avatar
Linus Torvalds committed
390
	kfree (usblp->device_id_string);
391
	kfree (usblp->statusbuf);
Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
392 393
	usb_free_urb(usblp->writeurb);
	usb_free_urb(usblp->readurb);
Linus Torvalds's avatar
Linus Torvalds committed
394 395 396
	kfree (usblp);
}

David Paschal's avatar
David Paschal committed
397 398 399 400 401 402 403
static void usblp_unlink_urbs(struct usblp *usblp)
{
	usb_unlink_urb(usblp->writeurb);
	if (usblp->bidir)
		usb_unlink_urb(usblp->readurb);
}

Linus Torvalds's avatar
Linus Torvalds committed
404 405 406 407
static int usblp_release(struct inode *inode, struct file *file)
{
	struct usblp *usblp = file->private_data;

Linus Torvalds's avatar
Linus Torvalds committed
408
	down (&usblp->sem);
Linus Torvalds's avatar
Linus Torvalds committed
409
	usblp->used = 0;
410
	if (usblp->present) {
David Paschal's avatar
David Paschal committed
411
		usblp_unlink_urbs(usblp);
Linus Torvalds's avatar
Linus Torvalds committed
412 413 414
		up(&usblp->sem);
	} else 		/* finish cleanup from disconnect */
		usblp_cleanup (usblp);
Linus Torvalds's avatar
Linus Torvalds committed
415 416 417 418 419 420 421 422
	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);
423 424
 	return ((!usblp->bidir || !usblp->rcomplete) ? 0 : POLLIN  | POLLRDNORM)
 			       | (!usblp->wcomplete ? 0 : POLLOUT | POLLWRNORM);
Linus Torvalds's avatar
Linus Torvalds committed
425 426 427 428 429
}

static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
	struct usblp *usblp = file->private_data;
David Paschal's avatar
David Paschal committed
430
	int length, err, i;
431
	unsigned char newChannel;
432
	int status;
David Paschal's avatar
David Paschal committed
433
	int twoints[2];
Linus Torvalds's avatar
Linus Torvalds committed
434 435 436
	int retval = 0;

	down (&usblp->sem);
437
	if (!usblp->present) {
Linus Torvalds's avatar
Linus Torvalds committed
438 439 440
		retval = -ENODEV;
		goto done;
	}
Linus Torvalds's avatar
Linus Torvalds committed
441 442

	if (_IOC_TYPE(cmd) == 'P')	/* new-style ioctl number */
Linus Torvalds's avatar
Linus Torvalds committed
443

Linus Torvalds's avatar
Linus Torvalds committed
444 445 446
		switch (_IOC_NR(cmd)) {

			case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */
Linus Torvalds's avatar
Linus Torvalds committed
447 448 449 450
				if (_IOC_DIR(cmd) != _IOC_READ) {
					retval = -EINVAL;
					goto done;
				}
Linus Torvalds's avatar
Linus Torvalds committed
451

David Paschal's avatar
David Paschal committed
452 453 454 455 456 457 458 459
				length = usblp_cache_device_id_string(usblp);
				if (length < 0) {
					retval = length;
					goto done;
				}
				if (length > _IOC_SIZE(cmd))
					length = _IOC_SIZE(cmd); /* truncate */

460
				if (copy_to_user((void __user *) arg,
David Paschal's avatar
David Paschal committed
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
						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);
				}

484
				if (copy_to_user((void __user *)arg,
David Paschal's avatar
David Paschal committed
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
						(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);
Linus Torvalds's avatar
Linus Torvalds committed
524
				if (err < 0) {
David Paschal's avatar
David Paschal committed
525 526
					err("usblp%d: error = %d setting "
						"HP channel",
Linus Torvalds's avatar
Linus Torvalds committed
527
						usblp->minor, err);
Linus Torvalds's avatar
Linus Torvalds committed
528 529
					retval = -EIO;
					goto done;
Linus Torvalds's avatar
Linus Torvalds committed
530 531
				}

David Paschal's avatar
David Paschal committed
532 533 534
				dbg("usblp%d requested/got HP channel %ld/%d",
					usblp->minor, arg, newChannel);
				break;
Linus Torvalds's avatar
Linus Torvalds committed
535

David Paschal's avatar
David Paschal committed
536 537 538 539 540 541
			case IOCNR_GET_BUS_ADDRESS:
				if (_IOC_DIR(cmd) != _IOC_READ ||
				    _IOC_SIZE(cmd) < sizeof(twoints)) {
					retval = -EINVAL;
					goto done;
				}
Linus Torvalds's avatar
Linus Torvalds committed
542

David Paschal's avatar
David Paschal committed
543 544
				twoints[0] = usblp->dev->bus->busnum;
				twoints[1] = usblp->dev->devnum;
545
				if (copy_to_user((void __user *)arg,
David Paschal's avatar
David Paschal committed
546 547 548 549 550
						(unsigned char *)twoints,
						sizeof(twoints))) {
					retval = -EFAULT;
					goto done;
				}
Linus Torvalds's avatar
Linus Torvalds committed
551

David Paschal's avatar
David Paschal committed
552 553 554 555 556 557 558 559 560 561 562 563 564
				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;
565
				if (copy_to_user((void __user *)arg,
David Paschal's avatar
David Paschal committed
566 567
						(unsigned char *)twoints,
						sizeof(twoints))) {
Linus Torvalds's avatar
Linus Torvalds committed
568 569 570
					retval = -EFAULT;
					goto done;
				}
Linus Torvalds's avatar
Linus Torvalds committed
571

David Paschal's avatar
David Paschal committed
572 573
				dbg("usblp%d is VID=0x%4.4X, PID=0x%4.4X",
					usblp->minor, twoints[0], twoints[1]);
Linus Torvalds's avatar
Linus Torvalds committed
574 575 576
				break;

			default:
577
				retval = -ENOTTY;
Linus Torvalds's avatar
Linus Torvalds committed
578 579 580 581 582
		}
	else	/* old-style ioctl value */
		switch (cmd) {

			case LPGETSTATUS:
583
				if (usblp_read_status(usblp, usblp->statusbuf)) {
Linus Torvalds's avatar
Linus Torvalds committed
584
					err("usblp%d: failed reading printer status", usblp->minor);
Linus Torvalds's avatar
Linus Torvalds committed
585 586
					retval = -EIO;
					goto done;
Linus Torvalds's avatar
Linus Torvalds committed
587
				}
588
				status = *usblp->statusbuf;
589
				if (copy_to_user ((void __user *)arg, &status, sizeof(int)))
Linus Torvalds's avatar
Linus Torvalds committed
590
					retval = -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
591 592 593
				break;

			default:
594
				retval = -ENOTTY;
Linus Torvalds's avatar
Linus Torvalds committed
595 596
		}

Linus Torvalds's avatar
Linus Torvalds committed
597 598 599
done:
	up (&usblp->sem);
	return retval;
Linus Torvalds's avatar
Linus Torvalds committed
600 601
}

602
static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
Linus Torvalds's avatar
Linus Torvalds committed
603
{
604
	DECLARE_WAITQUEUE(wait, current);
Linus Torvalds's avatar
Linus Torvalds committed
605
	struct usblp *usblp = file->private_data;
606 607
	int timeout, err = 0;
	size_t writecount = 0;
Linus Torvalds's avatar
Linus Torvalds committed
608 609

	while (writecount < count) {
610 611
		if (!usblp->wcomplete) {
			barrier();
Linus Torvalds's avatar
Linus Torvalds committed
612
			if (file->f_flags & O_NONBLOCK)
613
				return writecount ? writecount : -EAGAIN;
Linus Torvalds's avatar
Linus Torvalds committed
614 615

			timeout = USBLP_WRITE_TIMEOUT;
616 617
			add_wait_queue(&usblp->wait, &wait);
			while ( 1==1 ) {
Linus Torvalds's avatar
Linus Torvalds committed
618

619 620
				if (signal_pending(current)) {
					remove_wait_queue(&usblp->wait, &wait);
Linus Torvalds's avatar
Linus Torvalds committed
621
					return writecount ? writecount : -EINTR;
622 623 624 625 626 627 628 629
				}
				set_current_state(TASK_INTERRUPTIBLE);
				if (timeout && !usblp->wcomplete) {
					timeout = schedule_timeout(timeout);
				} else {
					set_current_state(TASK_RUNNING);
					break;
				}
Linus Torvalds's avatar
Linus Torvalds committed
630
			}
631
			remove_wait_queue(&usblp->wait, &wait);
Linus Torvalds's avatar
Linus Torvalds committed
632 633
		}

Linus Torvalds's avatar
Linus Torvalds committed
634
		down (&usblp->sem);
635
		if (!usblp->present) {
Linus Torvalds's avatar
Linus Torvalds committed
636
			up (&usblp->sem);
Linus Torvalds's avatar
Linus Torvalds committed
637
			return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
638
		}
Linus Torvalds's avatar
Linus Torvalds committed
639

Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
640
		if (usblp->writeurb->status != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
641
			if (usblp->quirks & USBLP_QUIRK_BIDIR) {
642
				if (!usblp->wcomplete)
Linus Torvalds's avatar
Linus Torvalds committed
643
					err("usblp%d: error %d writing to printer",
Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
644 645
						usblp->minor, usblp->writeurb->status);
				err = usblp->writeurb->status;
Linus Torvalds's avatar
Linus Torvalds committed
646
			} else
Linus Torvalds's avatar
Linus Torvalds committed
647
				err = usblp_check_status(usblp, err);
Linus Torvalds's avatar
Linus Torvalds committed
648 649 650 651 652 653 654
			up (&usblp->sem);

			/* if the fault was due to disconnect, let khubd's
			 * call to usblp_disconnect() grab usblp->sem ...
			 */
			schedule ();
			continue;
Linus Torvalds's avatar
Linus Torvalds committed
655 656
		}

Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
657 658
		writecount += usblp->writeurb->transfer_buffer_length;
		usblp->writeurb->transfer_buffer_length = 0;
Linus Torvalds's avatar
Linus Torvalds committed
659

Linus Torvalds's avatar
Linus Torvalds committed
660 661 662 663
		if (writecount == count) {
			up (&usblp->sem);
			break;
		}
Linus Torvalds's avatar
Linus Torvalds committed
664

Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
665 666
		usblp->writeurb->transfer_buffer_length = (count - writecount) < USBLP_BUF_SIZE ?
							  (count - writecount) : USBLP_BUF_SIZE;
Linus Torvalds's avatar
Linus Torvalds committed
667

Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
668
		if (copy_from_user(usblp->writeurb->transfer_buffer, buffer + writecount,
669 670 671 672
				usblp->writeurb->transfer_buffer_length)) {
			up(&usblp->sem);
			return writecount ? writecount : -EFAULT;
		}
Linus Torvalds's avatar
Linus Torvalds committed
673

Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
674
		usblp->writeurb->dev = usblp->dev;
675
		usblp->wcomplete = 0;
676 677
		if (err = usb_submit_urb(usblp->writeurb, GFP_KERNEL)) {
			count = err != -ENOMEM ? -EIO : (writecount ? writecount : -ENOMEM);
678 679 680
			up (&usblp->sem);
			break;
		}
Linus Torvalds's avatar
Linus Torvalds committed
681
		up (&usblp->sem);
Linus Torvalds's avatar
Linus Torvalds committed
682 683 684 685 686
	}

	return count;
}

687
static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
Linus Torvalds's avatar
Linus Torvalds committed
688 689
{
	struct usblp *usblp = file->private_data;
690
	DECLARE_WAITQUEUE(wait, current);
Linus Torvalds's avatar
Linus Torvalds committed
691 692 693 694

	if (!usblp->bidir)
		return -EINVAL;

Linus Torvalds's avatar
Linus Torvalds committed
695
	down (&usblp->sem);
696
	if (!usblp->present) {
Linus Torvalds's avatar
Linus Torvalds committed
697 698 699 700
		count = -ENODEV;
		goto done;
	}

701 702
	if (!usblp->rcomplete) {
		barrier();
Linus Torvalds's avatar
Linus Torvalds committed
703

Linus Torvalds's avatar
Linus Torvalds committed
704 705 706 707
		if (file->f_flags & O_NONBLOCK) {
			count = -EAGAIN;
			goto done;
		}
Linus Torvalds's avatar
Linus Torvalds committed
708

709 710
		add_wait_queue(&usblp->wait, &wait);
		while (1==1) {
Linus Torvalds's avatar
Linus Torvalds committed
711 712
			if (signal_pending(current)) {
				count = -EINTR;
713
				remove_wait_queue(&usblp->wait, &wait);
Linus Torvalds's avatar
Linus Torvalds committed
714 715 716
				goto done;
			}
			up (&usblp->sem);
717 718 719 720 721 722 723
			set_current_state(TASK_INTERRUPTIBLE);
			if (!usblp->rcomplete) {
				schedule();
			} else {
				set_current_state(TASK_RUNNING);
				break;
			}
Linus Torvalds's avatar
Linus Torvalds committed
724
			down (&usblp->sem);
Linus Torvalds's avatar
Linus Torvalds committed
725
		}
726
		remove_wait_queue(&usblp->wait, &wait);
Linus Torvalds's avatar
Linus Torvalds committed
727 728
	}

729
	if (!usblp->present) {
Linus Torvalds's avatar
Linus Torvalds committed
730 731 732
		count = -ENODEV;
		goto done;
	}
Linus Torvalds's avatar
Linus Torvalds committed
733

Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
734
	if (usblp->readurb->status) {
Linus Torvalds's avatar
Linus Torvalds committed
735
		err("usblp%d: error %d reading from printer",
Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
736 737
			usblp->minor, usblp->readurb->status);
		usblp->readurb->dev = usblp->dev;
Linus Torvalds's avatar
Linus Torvalds committed
738
 		usblp->readcount = 0;
739
		if (usb_submit_urb(usblp->readurb, GFP_KERNEL) < 0)
740
			dbg("error submitting urb");
Linus Torvalds's avatar
Linus Torvalds committed
741 742
		count = -EIO;
		goto done;
Linus Torvalds's avatar
Linus Torvalds committed
743 744
	}

Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
745 746
	count = count < usblp->readurb->actual_length - usblp->readcount ?
		count :	usblp->readurb->actual_length - usblp->readcount;
Linus Torvalds's avatar
Linus Torvalds committed
747

Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
748
	if (copy_to_user(buffer, usblp->readurb->transfer_buffer + usblp->readcount, count)) {
Linus Torvalds's avatar
Linus Torvalds committed
749 750 751
		count = -EFAULT;
		goto done;
	}
Linus Torvalds's avatar
Linus Torvalds committed
752

Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
753
	if ((usblp->readcount += count) == usblp->readurb->actual_length) {
Linus Torvalds's avatar
Linus Torvalds committed
754
		usblp->readcount = 0;
Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
755
		usblp->readurb->dev = usblp->dev;
756
		usblp->rcomplete = 0;
Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
757
		if (usb_submit_urb(usblp->readurb, GFP_KERNEL)) {
758 759 760
			count = -EIO;
			goto done;
		}
Linus Torvalds's avatar
Linus Torvalds committed
761 762
	}

Linus Torvalds's avatar
Linus Torvalds committed
763 764
done:
	up (&usblp->sem);
Linus Torvalds's avatar
Linus Torvalds committed
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
	return count;
}

/*
 * Checks for printers that have quirks, such as requiring unidirectional
 * communication but reporting bidirectional; currently some HP printers
 * have this flaw (HP 810, 880, 895, etc.), or needing an init string
 * sent at each open (like some Epsons).
 * Returns 1 if found, 0 if not found.
 *
 * HP recommended that we use the bidirectional interface but
 * don't attempt any bulk IN transfers from the IN endpoint.
 * Here's some more detail on the problem:
 * The problem is not that it isn't bidirectional though. The problem
 * is that if you request a device ID, or status information, while
 * the buffers are full, the return data will end up in the print data
 * buffer. For example if you make sure you never request the device ID
 * while you are sending print data, and you don't try to query the
 * printer status every couple of milliseconds, you will probably be OK.
 */
static unsigned int usblp_quirks (__u16 vendor, __u16 product)
{
	int i;

	for (i = 0; quirk_printers[i].vendorId; i++) {
		if (vendor == quirk_printers[i].vendorId &&
		    product == quirk_printers[i].productId)
			return quirk_printers[i].quirks;
 	}
	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
797
static struct file_operations usblp_fops = {
798 799 800 801 802 803 804
	.owner =	THIS_MODULE,
	.read =		usblp_read,
	.write =	usblp_write,
	.poll =		usblp_poll,
	.ioctl =	usblp_ioctl,
	.open =		usblp_open,
	.release =	usblp_release,
Linus Torvalds's avatar
Linus Torvalds committed
805 806
};

807 808 809 810 811 812 813
static struct usb_class_driver usblp_class = {
	.name =		"usb/lp%d",
	.fops =		&usblp_fops,
	.mode =		S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
	.minor_base =	USBLP_MINOR_BASE,
};

814 815
static int usblp_probe(struct usb_interface *intf,
		       const struct usb_device_id *id)
Linus Torvalds's avatar
Linus Torvalds committed
816
{
817
	struct usb_device *dev = interface_to_usbdev (intf);
David Paschal's avatar
David Paschal committed
818 819
	struct usblp *usblp = 0;
	int protocol;
Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
820
	int retval;
Linus Torvalds's avatar
Linus Torvalds committed
821

David Paschal's avatar
David Paschal committed
822 823
	/* Malloc and start initializing usblp structure so we can use it
	 * directly. */
Linus Torvalds's avatar
Linus Torvalds committed
824
	if (!(usblp = kmalloc(sizeof(struct usblp), GFP_KERNEL))) {
David Paschal's avatar
David Paschal committed
825 826
		err("out of memory for usblp");
		goto abort;
Linus Torvalds's avatar
Linus Torvalds committed
827 828 829
	}
	memset(usblp, 0, sizeof(struct usblp));
	usblp->dev = dev;
David Paschal's avatar
David Paschal committed
830 831
	init_MUTEX (&usblp->sem);
	init_waitqueue_head(&usblp->wait);
832
	usblp->ifnum = intf->altsetting->desc.bInterfaceNumber;
Linus Torvalds's avatar
Linus Torvalds committed
833

Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
834 835 836
	usblp->writeurb = usb_alloc_urb(0, GFP_KERNEL);
	if (!usblp->writeurb) {
		err("out of memory");
837
		goto abort;
Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
838 839 840 841
	}
	usblp->readurb = usb_alloc_urb(0, GFP_KERNEL);
	if (!usblp->readurb) {
		err("out of memory");
842
		goto abort;
Greg Kroah-Hartman's avatar
USB  
Greg Kroah-Hartman committed
843 844
	}

David Paschal's avatar
David Paschal committed
845 846 847
	/* Malloc device ID string buffer to the largest expected length,
	 * since we can re-query it on an ioctl and a dynamic string
	 * could change in length. */
David S. Miller's avatar
David S. Miller committed
848
	if (!(usblp->device_id_string = kmalloc(USBLP_DEVICE_ID_SIZE, GFP_KERNEL))) {
David Paschal's avatar
David Paschal committed
849
		err("out of memory for device_id_string");
850
		goto abort;
Linus Torvalds's avatar
Linus Torvalds committed
851 852
	}

853
	usblp->writebuf = usblp->readbuf = NULL;
854 855
	usblp->writeurb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
	usblp->readurb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
856
	/* Malloc write & read buffers.  We somewhat wastefully
David Paschal's avatar
David Paschal committed
857 858
	 * malloc both regardless of bidirectionality, because the
	 * alternate setting can be changed later via an ioctl. */
859 860 861
	if (!(usblp->writebuf = usb_buffer_alloc(dev, USBLP_BUF_SIZE,
				GFP_KERNEL, &usblp->writeurb->transfer_dma))) {
		err("out of memory for write buf");
862
		goto abort;
863 864 865 866
	}
	if (!(usblp->readbuf = usb_buffer_alloc(dev, USBLP_BUF_SIZE,
				GFP_KERNEL, &usblp->readurb->transfer_dma))) {
		err("out of memory for read buf");
867
		goto abort;
868 869 870 871 872 873
	}

	/* Allocate buffer for printer status */
	usblp->statusbuf = kmalloc(STATUS_BUF_SIZE, GFP_KERNEL);
	if (!usblp->statusbuf) {
		err("out of memory for statusbuf");
874
		goto abort;
Linus Torvalds's avatar
Linus Torvalds committed
875 876
	}

David Paschal's avatar
David Paschal committed
877 878 879 880 881 882 883 884 885 886 887
	/* Lookup quirks for this printer. */
	usblp->quirks = usblp_quirks(
		dev->descriptor.idVendor,
		dev->descriptor.idProduct);

	/* Analyze and pick initial alternate settings and endpoints. */
	protocol = usblp_select_alts(usblp);
	if (protocol < 0) {
		dbg("incompatible printer-class device 0x%4.4X/0x%4.4X",
			dev->descriptor.idVendor,
			dev->descriptor.idProduct);
888
		goto abort;
David Paschal's avatar
David Paschal committed
889
	}
Linus Torvalds's avatar
Linus Torvalds committed
890

David Paschal's avatar
David Paschal committed
891 892
	/* Setup the selected alternate setting and endpoints. */
	if (usblp_set_protocol(usblp, protocol) < 0)
893
		goto abort;
Linus Torvalds's avatar
Linus Torvalds committed
894

David Paschal's avatar
David Paschal committed
895 896
	/* Retrieve and store the device ID string. */
	usblp_cache_device_id_string(usblp);
Linus Torvalds's avatar
Linus Torvalds committed
897 898 899 900 901

#ifdef DEBUG
	usblp_check_status(usblp, 0);
#endif

David Paschal's avatar
David Paschal committed
902 903
	info("usblp%d: USB %sdirectional printer dev %d "
		"if %d alt %d proto %d vid 0x%4.4X pid 0x%4.4X",
904 905
		usblp->minor, usblp->bidir ? "Bi" : "Uni", dev->devnum,
		usblp->ifnum,
David Paschal's avatar
David Paschal committed
906 907 908
		usblp->protocol[usblp->current_protocol].alt_setting,
		usblp->current_protocol, usblp->dev->descriptor.idVendor,
		usblp->dev->descriptor.idProduct);
Linus Torvalds's avatar
Linus Torvalds committed
909

910
	usb_set_intfdata (intf, usblp);
911

912 913
	usblp->present = 1;

914 915 916 917 918 919 920
	retval = usb_register_dev(intf, &usblp_class);
	if (retval) {
		err("Not able to get a minor for this device.");
		goto abort_intfdata;
	}
	usblp->minor = intf->minor;

921
	return 0;
David Paschal's avatar
David Paschal committed
922

923 924
abort_intfdata:
	usb_set_intfdata (intf, NULL);
David Paschal's avatar
David Paschal committed
925 926
abort:
	if (usblp) {
927 928 929 930 931 932
		if (usblp->writebuf)
			usb_buffer_free (usblp->dev, USBLP_BUF_SIZE,
				usblp->writebuf, usblp->writeurb->transfer_dma);
		if (usblp->readbuf)
			usb_buffer_free (usblp->dev, USBLP_BUF_SIZE,
				usblp->readbuf, usblp->writeurb->transfer_dma);
933 934
		kfree(usblp->statusbuf);
		kfree(usblp->device_id_string);
David Paschal's avatar
David Paschal committed
935 936 937 938
		usb_free_urb(usblp->writeurb);
		usb_free_urb(usblp->readurb);
		kfree(usblp);
	}
939
	return -EIO;
David Paschal's avatar
David Paschal committed
940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963
}

/*
 * We are a "new" style driver with usb_device_id table,
 * but our requirements are too intricate for simple match to handle.
 *
 * The "proto_bias" option may be used to specify the preferred protocol
 * for all USB printers (1=7/1/1, 2=7/1/2, 3=7/1/3).  If the device
 * supports the preferred protocol, then we bind to it.
 *
 * The best interface for us is 7/1/2, because it is compatible
 * with a stream of characters. If we find it, we bind to it.
 *
 * Note that the people from hpoj.sourceforge.net need to be able to
 * bind to 7/1/3 (MLC/1284.4), so we provide them ioctls for this purpose.
 *
 * Failing 7/1/2, we look for 7/1/3, even though it's probably not
 * stream-compatible, because this matches the behaviour of the old code.
 *
 * If nothing else, we bind to 7/1/1 - the unidirectional interface.
 */
static int usblp_select_alts(struct usblp *usblp)
{
	struct usb_interface *if_alt;
964
	struct usb_host_interface *ifd;
David Paschal's avatar
David Paschal committed
965 966 967
	struct usb_endpoint_descriptor *epd, *epwrite, *epread;
	int p, i, e;

968
	if_alt = usblp->dev->actconfig->interface[usblp->ifnum];
David Paschal's avatar
David Paschal committed
969 970 971 972 973 974 975 976

	for (p = 0; p < USBLP_MAX_PROTOCOLS; p++)
		usblp->protocol[p].alt_setting = -1;

	/* Find out what we have. */
	for (i = 0; i < if_alt->num_altsetting; i++) {
		ifd = &if_alt->altsetting[i];

977
		if (ifd->desc.bInterfaceClass != 7 || ifd->desc.bInterfaceSubClass != 1)
David Paschal's avatar
David Paschal committed
978 979
			continue;

980 981
		if (ifd->desc.bInterfaceProtocol < USBLP_FIRST_PROTOCOL ||
		    ifd->desc.bInterfaceProtocol > USBLP_LAST_PROTOCOL)
David Paschal's avatar
David Paschal committed
982 983 984 985
			continue;

		/* Look for bulk OUT and IN endpoints. */
		epwrite = epread = 0;
986 987
		for (e = 0; e < ifd->desc.bNumEndpoints; e++) {
			epd = &ifd->endpoint[e].desc;
David Paschal's avatar
David Paschal committed
988 989 990 991 992 993

			if ((epd->bmAttributes&USB_ENDPOINT_XFERTYPE_MASK)!=
			    USB_ENDPOINT_XFER_BULK)
				continue;

			if (!(epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK)) {
994 995
				if (!epwrite)
					epwrite = epd;
David Paschal's avatar
David Paschal committed
996 997

			} else {
998 999
				if (!epread)
					epread = epd;
David Paschal's avatar
David Paschal committed
1000 1001 1002 1003
			}
		}

		/* Ignore buggy hardware without the right endpoints. */
1004
		if (!epwrite || (ifd->desc.bInterfaceProtocol > 1 && !epread))
David Paschal's avatar
David Paschal committed
1005 1006 1007 1008
			continue;

		/* Turn off reads for 7/1/1 (unidirectional) interfaces
		 * and buggy bidirectional printers. */
1009
		if (ifd->desc.bInterfaceProtocol == 1) {
David Paschal's avatar
David Paschal committed
1010 1011 1012 1013 1014 1015 1016
			epread = NULL;
		} else if (usblp->quirks & USBLP_QUIRK_BIDIR) {
			info("Disabling reads from problem bidirectional "
				"printer on usblp%d", usblp->minor);
			epread = NULL;
		}

1017 1018 1019
		usblp->protocol[ifd->desc.bInterfaceProtocol].alt_setting = i;
		usblp->protocol[ifd->desc.bInterfaceProtocol].epwrite = epwrite;
		usblp->protocol[ifd->desc.bInterfaceProtocol].epread = epread;
David Paschal's avatar
David Paschal committed
1020 1021 1022 1023 1024 1025 1026 1027 1028
	}

	/* If our requested protocol is supported, then use it. */
	if (proto_bias >= USBLP_FIRST_PROTOCOL &&
	    proto_bias <= USBLP_LAST_PROTOCOL &&
	    usblp->protocol[proto_bias].alt_setting != -1)
		return proto_bias;

	/* Ordering is important here. */
1029 1030 1031 1032 1033 1034
	if (usblp->protocol[2].alt_setting != -1)
		return 2;
	if (usblp->protocol[1].alt_setting != -1)
		return 1;
	if (usblp->protocol[3].alt_setting != -1)
		return 3;
David Paschal's avatar
David Paschal committed
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047

	/* If nothing is available, then don't bind to this device. */
	return -1;
}

static int usblp_set_protocol(struct usblp *usblp, int protocol)
{
	int r, alts;

	if (protocol < USBLP_FIRST_PROTOCOL || protocol > USBLP_LAST_PROTOCOL)
		return -EINVAL;

	alts = usblp->protocol[protocol].alt_setting;
1048 1049
	if (alts < 0)
		return -EINVAL;
David Paschal's avatar
David Paschal committed
1050 1051 1052 1053 1054 1055 1056
	r = usb_set_interface(usblp->dev, usblp->ifnum, alts);
	if (r < 0) {
		err("can't set desired altsetting %d on interface %d",
			alts, usblp->ifnum);
		return r;
	}

1057
	usb_fill_bulk_urb(usblp->writeurb, usblp->dev,
David Paschal's avatar
David Paschal committed
1058
		usb_sndbulkpipe(usblp->dev,
1059 1060
		  usblp->protocol[protocol].epwrite->bEndpointAddress),
		usblp->writebuf, 0,
David Paschal's avatar
David Paschal committed
1061 1062 1063 1064
		usblp_bulk_write, usblp);

	usblp->bidir = (usblp->protocol[protocol].epread != 0);
	if (usblp->bidir)
1065
		usb_fill_bulk_urb(usblp->readurb, usblp->dev,
David Paschal's avatar
David Paschal committed
1066
			usb_rcvbulkpipe(usblp->dev,
1067 1068
			  usblp->protocol[protocol].epread->bEndpointAddress),
			usblp->readbuf, USBLP_BUF_SIZE,
David Paschal's avatar
David Paschal committed
1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
			usblp_bulk_read, usblp);

	usblp->current_protocol = protocol;
	dbg("usblp%d set protocol %d", usblp->minor, protocol);
	return 0;
}

/* Retrieves and caches device ID string.
 * Returns length, including length bytes but not null terminator.
 * On error, returns a negative errno value. */
static int usblp_cache_device_id_string(struct usblp *usblp)
{
	int err, length;

David S. Miller's avatar
David S. Miller committed
1083
	err = usblp_get_id(usblp, 0, usblp->device_id_string, USBLP_DEVICE_ID_SIZE - 1);
David Paschal's avatar
David Paschal committed
1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
	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';
		return -EIO;
	}

	/* First two bytes are length in big-endian.
	 * They count themselves, and we copy them into
	 * the user's buffer. */
	length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1];
	if (length < 2)
		length = 2;
David S. Miller's avatar
David S. Miller committed
1097 1098
	else if (length >= USBLP_DEVICE_ID_SIZE)
		length = USBLP_DEVICE_ID_SIZE - 1;
David Paschal's avatar
David Paschal committed
1099 1100 1101 1102 1103 1104
	usblp->device_id_string[length] = '\0';

	dbg("usblp%d Device ID string [len=%d]=\"%s\"",
		usblp->minor, length, &usblp->device_id_string[2]);

	return length;
Linus Torvalds's avatar
Linus Torvalds committed
1105 1106
}

1107
static void usblp_disconnect(struct usb_interface *intf)
Linus Torvalds's avatar
Linus Torvalds committed
1108
{
1109
	struct usblp *usblp = usb_get_intfdata (intf);
Linus Torvalds's avatar
Linus Torvalds committed
1110

1111
	usb_deregister_dev(intf, &usblp_class);
1112

Linus Torvalds's avatar
Linus Torvalds committed
1113
	if (!usblp || !usblp->dev) {
Linus Torvalds's avatar
Linus Torvalds committed
1114 1115
		err("bogus disconnect");
		BUG ();
Linus Torvalds's avatar
Linus Torvalds committed
1116 1117
	}

Linus Torvalds's avatar
Linus Torvalds committed
1118
	down (&usblp->sem);
Linus Torvalds's avatar
Linus Torvalds committed
1119
	lock_kernel();
1120
	usblp->present = 0;
1121
	usb_set_intfdata (intf, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
1122

David Paschal's avatar
David Paschal committed
1123
	usblp_unlink_urbs(usblp);
Linus Torvalds's avatar
Linus Torvalds committed
1124

Linus Torvalds's avatar
Linus Torvalds committed
1125 1126
	if (!usblp->used)
		usblp_cleanup (usblp);
1127
	else 	/* cleanup later, on release */
Linus Torvalds's avatar
Linus Torvalds committed
1128
		up (&usblp->sem);
Linus Torvalds's avatar
Linus Torvalds committed
1129
	unlock_kernel();
Linus Torvalds's avatar
Linus Torvalds committed
1130 1131 1132
}

static struct usb_device_id usblp_ids [] = {
Linus Torvalds's avatar
Linus Torvalds committed
1133 1134 1135
	{ USB_DEVICE_INFO(7, 1, 1) },
	{ USB_DEVICE_INFO(7, 1, 2) },
	{ USB_DEVICE_INFO(7, 1, 3) },
Linus Torvalds's avatar
Linus Torvalds committed
1136 1137 1138 1139 1140 1141 1142 1143 1144
	{ USB_INTERFACE_INFO(7, 1, 1) },
	{ USB_INTERFACE_INFO(7, 1, 2) },
	{ USB_INTERFACE_INFO(7, 1, 3) },
	{ }						/* Terminating entry */
};

MODULE_DEVICE_TABLE (usb, usblp_ids);

static struct usb_driver usblp_driver = {
1145 1146 1147 1148 1149
	.owner =	THIS_MODULE,
	.name =		"usblp",
	.probe =	usblp_probe,
	.disconnect =	usblp_disconnect,
	.id_table =	usblp_ids,
Linus Torvalds's avatar
Linus Torvalds committed
1150 1151 1152 1153
};

static int __init usblp_init(void)
{
1154 1155 1156 1157
	int retval;
	retval = usb_register(&usblp_driver);
	if (retval)
		goto out;
David Paschal's avatar
David Paschal committed
1158
	info(DRIVER_VERSION ": " DRIVER_DESC);
1159 1160
out:
	return retval;
Linus Torvalds's avatar
Linus Torvalds committed
1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
}

static void __exit usblp_exit(void)
{
	usb_deregister(&usblp_driver);
}

module_init(usblp_init);
module_exit(usblp_exit);

Linus Torvalds's avatar
Linus Torvalds committed
1171 1172
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
David Paschal's avatar
David Paschal committed
1173 1174
MODULE_PARM(proto_bias, "i");
MODULE_PARM_DESC(proto_bias, "Favourite protocol number");
Linus Torvalds's avatar
Linus Torvalds committed
1175
MODULE_LICENSE("GPL");