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 
open_lcd(struct inode * inode,struct file * file)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 
close_lcd(struct inode * inode,struct file * file)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
ioctl_lcd(struct inode * inode,struct file * file,unsigned int cmd,unsigned long arg)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
write_lcd(struct file * file,const char * buffer,size_t count,loff_t * ppos)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
read_lcd(struct file * file,char * buffer,size_t count,loff_t * ppos)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 
probe_lcd(struct usb_device * dev,unsigned int ifnum)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 
disconnect_lcd(struct usb_device * dev,void * ptr)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 
usb_lcd_init(void)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 
usb_lcd_cleanup(void)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");
348