summaryrefslogtreecommitdiffstats
path: root/drivers/usb/printer.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/printer.c')
-rw-r--r--drivers/usb/printer.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c
new file mode 100644
index 000000000..88723c6ff
--- /dev/null
+++ b/drivers/usb/printer.c
@@ -0,0 +1,413 @@
+
+/* Driver for USB Printers
+ *
+ * (C) Michael Gee (michael@linuxspecific.com) 1999
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/lp.h>
+
+#include <asm/spinlock.h>
+
+#include "usb.h"
+
+#define NAK_TIMEOUT (HZ) /* stall wait for printer */
+#define MAX_RETRY_COUNT ((60*60*HZ)/NAK_TIMEOUT) /* should not take 1 minute a page! */
+
+#ifndef USB_PRINTER_MAJOR
+#define USB_PRINTER_MAJOR 0
+#endif
+
+static int mymajor = USB_PRINTER_MAJOR;
+
+#define MAX_PRINTERS 8
+
+struct pp_usb_data {
+ struct usb_device *pusb_dev;
+ __u8 isopen; /* nz if open */
+ __u8 noinput; /* nz if no input stream */
+ __u8 minor; /* minor number of device */
+ __u8 status; /* last status from device */
+ int maxin, maxout; /* max transfer size in and out */
+ char *obuf; /* transfer buffer (out only) */
+ wait_queue_head_t wait_q; /* for timeouts */
+ unsigned int last_error; /* save for checking */
+};
+
+static struct pp_usb_data *minor_data[MAX_PRINTERS];
+
+#define PPDATA(x) ((struct pp_usb_data *)(x))
+
+unsigned char printer_read_status(struct pp_usb_data *p)
+{
+ __u8 status;
+ devrequest dr;
+ struct usb_device *dev = p->pusb_dev;
+
+ dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE | 0x80;
+ dr.request = 1;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 1;
+ if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, &status, 1)) {
+ return 0;
+ }
+ return status;
+}
+
+static int printer_check_status(struct pp_usb_data *p)
+{
+ unsigned int last = p->last_error;
+ unsigned char status = printer_read_status(p);
+
+ if (status & LP_PERRORP)
+ /* No error. */
+ last = 0;
+ else if ((status & LP_POUTPA)) {
+ if (last != LP_POUTPA) {
+ last = LP_POUTPA;
+ printk(KERN_INFO "usblp%d out of paper\n", p->minor);
+ }
+ } else if (!(status & LP_PSELECD)) {
+ if (last != LP_PSELECD) {
+ last = LP_PSELECD;
+ printk(KERN_INFO "usblp%d off-line\n", p->minor);
+ }
+ } else {
+ if (last != LP_PERRORP) {
+ last = LP_PERRORP;
+ printk(KERN_INFO "usblp%d on fire\n", p->minor);
+ }
+ }
+
+ p->last_error = last;
+
+ return status;
+}
+
+void printer_reset(struct pp_usb_data *p)
+{
+ devrequest dr;
+ struct usb_device *dev = p->pusb_dev;
+
+ dr.requesttype = USB_TYPE_CLASS | USB_RECIP_OTHER;
+ dr.request = 2;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 0;
+ dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+static int open_printer(struct inode * inode, struct file * file)
+{
+ struct pp_usb_data *p;
+
+ if(MINOR(inode->i_rdev) >= MAX_PRINTERS ||
+ !minor_data[MINOR(inode->i_rdev)]) {
+ return -ENODEV;
+ }
+
+ p = minor_data[MINOR(inode->i_rdev)];
+ p->minor = MINOR(inode->i_rdev);
+
+ if (p->isopen++) {
+ return -EBUSY;
+ }
+ if (!(p->obuf = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ printer_check_status(p);
+
+
+ file->private_data = p;
+// printer_reset(p);
+ init_waitqueue_head(&p->wait_q);
+ return 0;
+}
+
+static int close_printer(struct inode * inode, struct file * file)
+{
+ struct pp_usb_data *p = file->private_data;
+
+ free_page((unsigned long)p->obuf);
+ p->isopen = 0;
+ file->private_data = NULL;
+ if(!p->pusb_dev) {
+ minor_data[p->minor] = NULL;
+ kfree(p);
+
+ MOD_DEC_USE_COUNT;
+
+ }
+ return 0;
+}
+
+static ssize_t write_printer(struct file * file,
+ const char * buffer, size_t count, loff_t *ppos)
+{
+ struct pp_usb_data *p = file->private_data;
+ unsigned long copy_size;
+ unsigned long bytes_written = 0;
+ unsigned long partial;
+ int result;
+ int maxretry;
+
+ do {
+ char *obuf = p->obuf;
+ unsigned long thistime;
+
+ thistime = copy_size = (count > p->maxout) ? p->maxout : count;
+ if (copy_from_user(p->obuf, buffer, copy_size))
+ return -EFAULT;
+ maxretry = MAX_RETRY_COUNT;
+ while (thistime) {
+ if (!p->pusb_dev)
+ return -ENODEV;
+ if (signal_pending(current)) {
+ return bytes_written ? bytes_written : -EINTR;
+ }
+ result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev,
+ usb_sndbulkpipe(p->pusb_dev, 1), obuf, thistime, &partial);
+ if (result == USB_ST_TIMEOUT) { /* NAK - so hold for a while */
+ if(!maxretry--)
+ return -ETIME;
+ interruptible_sleep_on_timeout(&p->wait_q, NAK_TIMEOUT);
+ continue;
+ } else if (!result & partial) {
+ obuf += partial;
+ thistime -= partial;
+ } else
+ break;
+ };
+ if (result) {
+ /* whoops - let's reset and fail the request */
+// printk("Whoops - %x\n", result);
+ printer_reset(p);
+ interruptible_sleep_on_timeout(&p->wait_q, 5*HZ); /* let reset do its stuff */
+ return -EIO;
+ }
+ bytes_written += copy_size;
+ count -= copy_size;
+ buffer += copy_size;
+ } while ( count > 0 );
+
+ return bytes_written ? bytes_written : -EIO;
+}
+
+static ssize_t read_printer(struct file * file,
+ char * buffer, size_t count, loff_t *ppos)
+{
+ struct pp_usb_data *p = file->private_data;
+ int read_count;
+ int this_read;
+ char buf[64];
+ unsigned long partial;
+ int result;
+
+ if (p->noinput)
+ return -EINVAL;
+
+ read_count = 0;
+ while (count) {
+ if (signal_pending(current)) {
+ return read_count ? read_count : -EINTR;
+ }
+ if (!p->pusb_dev)
+ return -ENODEV;
+ this_read = (count > sizeof(buf)) ? sizeof(buf) : count;
+
+ result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev,
+ usb_rcvbulkpipe(p->pusb_dev, 2), buf, this_read, &partial);
+
+ /* unlike writes, we don't retry a NAK, just stop now */
+ if (!result & partial)
+ count = this_read = partial;
+ else if (result)
+ return -EIO;
+
+ if (this_read) {
+ if (copy_to_user(buffer, p->obuf, this_read))
+ return -EFAULT;
+ count -= this_read;
+ read_count += this_read;
+ buffer += this_read;
+ }
+ }
+ return read_count;
+}
+
+static int printer_probe(struct usb_device *dev)
+{
+ struct usb_interface_descriptor *interface;
+ int i;
+
+ /*
+ * FIXME - this will not cope with combined printer/scanners
+ */
+ if (dev->descriptor.bDeviceClass != 7 ||
+ dev->descriptor.bNumConfigurations != 1 ||
+ dev->config[0].bNumInterfaces != 1) {
+ return -1;
+ }
+
+ interface = dev->config->altsetting->interface;
+
+ /* Lets be paranoid (for the moment)*/
+ if (interface->bInterfaceClass != 7 ||
+ interface->bInterfaceSubClass != 1 ||
+ (interface->bInterfaceProtocol != 2 && interface->bInterfaceProtocol != 1)||
+ interface->bNumEndpoints > 2) {
+ return -1;
+ }
+
+ if (interface->endpoint[0].bEndpointAddress != 0x01 ||
+ interface->endpoint[0].bmAttributes != 0x02 ||
+ (interface->bNumEndpoints > 1 && (
+ interface->endpoint[1].bEndpointAddress != 0x82 ||
+ interface->endpoint[1].bmAttributes != 0x02))) {
+ return -1;
+ }
+
+ for (i=0; i<MAX_PRINTERS; i++) {
+ if (!minor_data[i])
+ break;
+ }
+ if (i >= MAX_PRINTERS) {
+ return -1;
+ }
+
+ printk(KERN_INFO "USB Printer found at address %d\n", dev->devnum);
+
+ if (!(dev->private = kmalloc(sizeof(struct pp_usb_data), GFP_KERNEL))) {
+ printk( KERN_DEBUG "usb_printer: no memory!\n");
+ return -1;
+ }
+
+ memset(dev->private, 0, sizeof(struct pp_usb_data));
+ minor_data[i] = PPDATA(dev->private);
+ minor_data[i]->minor = i;
+ minor_data[i]->pusb_dev = dev;
+ minor_data[i]->maxout = interface->endpoint[0].wMaxPacketSize * 16;
+ if (minor_data[i]->maxout > PAGE_SIZE) {
+ minor_data[i]->maxout = PAGE_SIZE;
+ }
+ if (interface->bInterfaceProtocol != 2)
+ minor_data[i]->noinput = 1;
+ else {
+ minor_data[i]->maxin = interface->endpoint[1].wMaxPacketSize;
+ }
+
+ if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
+ printk(KERN_INFO " Failed to set configuration\n");
+ return -1;
+ }
+#if 0
+ {
+ __u8 status;
+ __u8 ieee_id[64];
+ devrequest dr;
+
+ /* Lets get the device id if possible */
+ dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE | 0x80;
+ dr.request = 0;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = sizeof(ieee_id) - 1;
+ if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, ieee_id, sizeof(ieee_id)-1) == 0) {
+ if (ieee_id[1] < sizeof(ieee_id) - 1)
+ ieee_id[ieee_id[1]+2] = '\0';
+ else
+ ieee_id[sizeof(ieee_id)-1] = '\0';
+ printk(KERN_INFO " Printer ID is %s\n", &ieee_id[2]);
+ }
+ status = printer_read_status(PPDATA(dev->private));
+ printk(KERN_INFO " Status is %s,%s,%s\n",
+ (status & 0x10) ? "Selected" : "Not Selected",
+ (status & 0x20) ? "No Paper" : "Paper",
+ (status & 0x08) ? "No Error" : "Error");
+ }
+#endif
+ return 0;
+}
+
+static void printer_disconnect(struct usb_device *dev)
+{
+ struct pp_usb_data *pp = dev->private;
+
+ if (pp->isopen) {
+ /* better let it finish - the release will do whats needed */
+ pp->pusb_dev = NULL;
+ return;
+ }
+ minor_data[pp->minor] = NULL;
+ kfree(pp);
+ dev->private = NULL; /* just in case */
+ MOD_DEC_USE_COUNT;
+}
+
+static struct usb_driver printer_driver = {
+ "printer",
+ printer_probe,
+ printer_disconnect,
+ { NULL, NULL }
+};
+
+static struct file_operations usb_printer_fops = {
+ NULL, /* seek */
+ read_printer,
+ write_printer,
+ NULL, /* readdir */
+ NULL, /* poll - out for the moment */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ open_printer,
+ NULL, /* flush ? */
+ close_printer,
+ NULL,
+ NULL
+};
+
+int usb_printer_init(void)
+{
+ int result;
+
+ MOD_INC_USE_COUNT;
+
+ if ((result = register_chrdev(USB_PRINTER_MAJOR, "usblp", &usb_printer_fops)) < 0) {
+ printk(KERN_WARNING "usbprinter: Cannot register device\n");
+ return result;
+ }
+ if (mymajor == 0) {
+ mymajor = result;
+ }
+ usb_register(&printer_driver);
+ printk(KERN_INFO "USB Printer support registered.\n");
+ return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+
+ return usb_printer_init();
+}
+
+void cleanup_module(void)
+{
+ unsigned int offset;
+
+ usb_deregister(&printer_driver);
+ unregister_chrdev(mymajor, "usblplp");
+}
+#endif