more changes on original files
[linux-2.4.git] / drivers / usb / usblcd.c
1 /***************************************************************************** 
2  *                          USBLCD Kernel Driver                             *
3  *        See http://www.usblcd.de for Hardware and Documentation.           *
4  *                            Version 1.03                                   *
5  *             (C) 2002 Adams IT Services <info@usblcd.de>                   *
6  *                                                                           *
7  *     This file is licensed under the GPL. See COPYING in the package.      *
8  * Based on rio500.c by Cesar Miquel (miquel@df.uba.ar) which is based on    *
9  * hp_scanner.c by David E. Nelson (dnelson@jump.net)                        *
10  *                                                                           *
11  * 23.7.02 RA changed minor device number to the official assigned one       *
12  * 18.9.02 RA Vendor ID change, longer timeouts                              *
13  *****************************************************************************/
14 #include <linux/module.h>
15 #include <linux/kernel.h>
16 #include <linux/init.h>
17 #include <linux/slab.h>
18 #include <linux/errno.h>
19 #include <asm/uaccess.h>
20 #include <linux/usb.h>
21
22 #define DRIVER_VERSION "USBLCD Driver Version 1.03"
23
24 #define USBLCD_MINOR            144
25
26 #define IOCTL_GET_HARD_VERSION  1
27 #define IOCTL_GET_DRV_VERSION   2
28
29 /* stall/wait timeout for USBLCD */
30 #define NAK_TIMEOUT     (10*HZ)
31
32 #define IBUF_SIZE       0x1000
33 #define OBUF_SIZE       0x10000
34
35 struct lcd_usb_data {
36         struct usb_device *lcd_dev;     /* init: probe_lcd */
37         unsigned int ifnum;             /* Interface number of the USB device */
38         int isopen;                     /* nz if open */
39         int present;                    /* Device is present on the bus */
40         char *obuf, *ibuf;              /* transfer buffers */
41         char bulk_in_ep, bulk_out_ep;   /* Endpoint assignments */
42         wait_queue_head_t wait_q;       /* for timeouts */
43 };
44
45 static struct lcd_usb_data lcd_instance;
46
47 static int open_lcd(struct inode *inode, struct file *file)
48 {
49         struct lcd_usb_data *lcd = &lcd_instance;
50
51         if (lcd->isopen || !lcd->present) {
52                 return -EBUSY;
53         }
54         lcd->isopen = 1;
55
56         init_waitqueue_head(&lcd->wait_q);
57
58         info("USBLCD opened.");
59
60         return 0;
61 }
62
63 static int close_lcd(struct inode *inode, struct file *file)
64 {
65         struct lcd_usb_data *lcd = &lcd_instance;
66
67         lcd->isopen = 0;
68
69         info("USBLCD closed.");
70         return 0;
71 }
72
73 static int
74 ioctl_lcd(struct inode *inode, struct file *file, unsigned int cmd,
75           unsigned long arg)
76 {
77         struct lcd_usb_data *lcd = &lcd_instance;
78         int i;
79         char buf[30];
80
81         /* Sanity check to make sure lcd is connected, powered, etc */
82         if (lcd == NULL ||
83             lcd->present == 0 ||
84             lcd->lcd_dev == NULL)
85                 return -1;
86
87         switch (cmd) {
88         case IOCTL_GET_HARD_VERSION:
89                 i = (lcd->lcd_dev)->descriptor.bcdDevice;
90                 sprintf(buf,"%1d%1d.%1d%1d",(i & 0xF000)>>12,(i & 0xF00)>>8,
91                         (i & 0xF0)>>4,(i & 0xF));
92                 if (copy_to_user((void *)arg,buf,strlen(buf))!=0)
93                         return -EFAULT;
94                 break;
95         case IOCTL_GET_DRV_VERSION:
96                 sprintf(buf,DRIVER_VERSION);
97                 if (copy_to_user((void *)arg,buf,strlen(buf))!=0)
98                         return -EFAULT;
99                 break;
100         default:
101                 return -ENOIOCTLCMD;
102                 break;
103         }
104
105         return 0;
106 }
107
108 static ssize_t
109 write_lcd(struct file *file, const char *buffer,
110           size_t count, loff_t * ppos)
111 {
112         struct lcd_usb_data *lcd = &lcd_instance;
113
114         unsigned long copy_size;
115         unsigned long bytes_written = 0;
116         unsigned int partial;
117
118         int result = 0;
119         int maxretry;
120
121         /* Sanity check to make sure lcd is connected, powered, etc */
122         if (lcd == NULL ||
123             lcd->present == 0 ||
124             lcd->lcd_dev == NULL)
125                 return -1;
126
127         do {
128                 unsigned long thistime;
129                 char *obuf = lcd->obuf;
130
131                 thistime = copy_size =
132                     (count >= OBUF_SIZE) ? OBUF_SIZE : count;
133                 if (copy_from_user(lcd->obuf, buffer, copy_size))
134                         return -EFAULT;
135                 maxretry = 5;
136                 while (thistime) {
137                         if (!lcd->lcd_dev)
138                                 return -ENODEV;
139                         if (signal_pending(current)) {
140                                 return bytes_written ? bytes_written : -EINTR;
141                         }
142
143                         result = usb_bulk_msg(lcd->lcd_dev,
144                                          usb_sndbulkpipe(lcd->lcd_dev, 1),
145                                          obuf, thistime, &partial, 10 * HZ);
146
147                         dbg("write stats: result:%d thistime:%lu partial:%u",
148                              result, thistime, partial);
149
150                         if (result == USB_ST_TIMEOUT) { /* NAK - so hold for a while */
151                                 if (!maxretry--) {
152                                         return -ETIME;
153                                 }
154                                 interruptible_sleep_on_timeout(&lcd-> wait_q, NAK_TIMEOUT);
155                                 continue;
156                         } else if (!result & partial) {
157                                 obuf += partial;
158                                 thistime -= partial;
159                         } else
160                                 break;
161                 };
162                 if (result) {
163                         err("Write Whoops - %x", result);
164                         return -EIO;
165                 }
166                 bytes_written += copy_size;
167                 count -= copy_size;
168                 buffer += copy_size;
169         } while (count > 0);
170
171         return bytes_written ? bytes_written : -EIO;
172 }
173
174 static ssize_t
175 read_lcd(struct file *file, char *buffer, size_t count, loff_t * ppos)
176 {
177         struct lcd_usb_data *lcd = &lcd_instance;
178         ssize_t read_count;
179         unsigned int partial;
180         int this_read;
181         int result;
182         int maxretry = 10;
183         char *ibuf = lcd->ibuf;
184
185         /* Sanity check to make sure lcd is connected, powered, etc */
186         if (lcd == NULL ||
187             lcd->present == 0 ||
188             lcd->lcd_dev == NULL)
189                 return -1;
190
191         read_count = 0;
192
193         while (count > 0) {
194                 if (signal_pending(current)) {
195                         return read_count ? read_count : -EINTR;
196                 }
197                 if (!lcd->lcd_dev)
198                         return -ENODEV;
199                 this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count;
200
201                 result = usb_bulk_msg(lcd->lcd_dev,
202                                       usb_rcvbulkpipe(lcd->lcd_dev, 0),
203                                       ibuf, this_read, &partial,
204                                       (int) (HZ * 8));
205
206                 dbg(KERN_DEBUG "read stats: result:%d this_read:%u partial:%u",
207                        result, this_read, partial);
208
209                 if (partial) {
210                         count = this_read = partial;
211                 } else if (result == USB_ST_TIMEOUT || result == 15) {  /* FIXME: 15 ??? */
212                         if (!maxretry--) {
213                                 err("read_lcd: maxretry timeout");
214                                 return -ETIME;
215                         }
216                         interruptible_sleep_on_timeout(&lcd->wait_q,
217                                                        NAK_TIMEOUT);
218                         continue;
219                 } else if (result != USB_ST_DATAUNDERRUN) {
220                         err("Read Whoops - result:%u partial:%u this_read:%u",
221                              result, partial, this_read);
222                         return -EIO;
223                 } else {
224                         return (0);
225                 }
226
227                 if (this_read) {
228                         if (copy_to_user(buffer, ibuf, this_read))
229                                 return -EFAULT;
230                         count -= this_read;
231                         read_count += this_read;
232                         buffer += this_read;
233                 }
234         }
235         return read_count;
236 }
237
238 static void *probe_lcd(struct usb_device *dev, unsigned int ifnum)
239 {
240         struct lcd_usb_data *lcd = &lcd_instance;
241         int i;
242         
243         if (dev->descriptor.idProduct != 0x0001  ) {
244                 warn(KERN_INFO "USBLCD model not supported.");
245                 return NULL;
246         }
247
248         if (lcd->present == 1) {
249                 warn(KERN_INFO "Multiple USBLCDs are not supported!");
250                 return NULL;
251         }
252
253         i = dev->descriptor.bcdDevice;
254
255         info("USBLCD Version %1d%1d.%1d%1d found at address %d",
256                 (i & 0xF000)>>12,(i & 0xF00)>>8,(i & 0xF0)>>4,(i & 0xF),
257                 dev->devnum);
258
259         lcd->present = 1;
260         lcd->lcd_dev = dev;
261
262         if (!(lcd->obuf = (char *) kmalloc(OBUF_SIZE, GFP_KERNEL))) {
263                 err("probe_lcd: Not enough memory for the output buffer");
264                 return NULL;
265         }
266         dbg("probe_lcd: obuf address:%p", lcd->obuf);
267
268         if (!(lcd->ibuf = (char *) kmalloc(IBUF_SIZE, GFP_KERNEL))) {
269                 err("probe_lcd: Not enough memory for the input buffer");
270                 kfree(lcd->obuf);
271                 return NULL;
272         }
273         dbg("probe_lcd: ibuf address:%p", lcd->ibuf);
274
275         return lcd;
276 }
277
278 static void disconnect_lcd(struct usb_device *dev, void *ptr)
279 {
280         struct lcd_usb_data *lcd = (struct lcd_usb_data *) ptr;
281
282         if (lcd->isopen) {
283                 lcd->isopen = 0;
284                 /* better let it finish - the release will do whats needed */
285                 lcd->lcd_dev = NULL;
286                 return;
287         }
288         kfree(lcd->ibuf);
289         kfree(lcd->obuf);
290
291         info("USBLCD disconnected.");
292
293         lcd->present = 0;
294 }
295
296 static struct usb_device_id id_table [] = {
297         { .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, },
298         {},
299 };
300
301 MODULE_DEVICE_TABLE (usb, id_table);
302
303 static struct
304 file_operations usb_lcd_fops = {
305         .owner =        THIS_MODULE,
306         .read =         read_lcd,
307         .write =        write_lcd,
308         .ioctl =        ioctl_lcd,
309         .open =         open_lcd,
310         .release =      close_lcd,
311 };
312
313 static struct
314 usb_driver lcd_driver = {
315         .name =         "usblcd",
316         .probe =        (void *)probe_lcd,
317         .disconnect =   disconnect_lcd,
318         .id_table =     id_table,
319         .fops =         &usb_lcd_fops,
320         .minor =        USBLCD_MINOR,
321 };
322
323 int usb_lcd_init(void)
324 {
325         if (usb_register(&lcd_driver) < 0)
326                 return -1;
327
328         info("%s (C) Adams IT Services http://www.usblcd.de", DRIVER_VERSION);
329         info("USBLCD support registered.");
330         return 0;
331 }
332
333
334 void usb_lcd_cleanup(void)
335 {
336         struct lcd_usb_data *lcd = &lcd_instance;
337
338         lcd->present = 0;
339         usb_deregister(&lcd_driver);
340 }
341
342 module_init(usb_lcd_init);
343 module_exit(usb_lcd_cleanup);
344
345 MODULE_AUTHOR("Adams IT Services <info@usblcd.de>");
346 MODULE_DESCRIPTION(DRIVER_VERSION);
347 MODULE_LICENSE("GPL");