usblcd.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:8k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  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. #define DRIVER_VERSION "USBLCD Driver Version 1.03"
  22. #define USBLCD_MINOR 144
  23. #define IOCTL_GET_HARD_VERSION 1
  24. #define IOCTL_GET_DRV_VERSION 2
  25. /* stall/wait timeout for USBLCD */
  26. #define NAK_TIMEOUT (10*HZ)
  27. #define IBUF_SIZE 0x1000
  28. #define OBUF_SIZE 0x10000
  29. struct lcd_usb_data {
  30. struct usb_device *lcd_dev; /* init: probe_lcd */
  31. unsigned int ifnum; /* Interface number of the USB device */
  32. int isopen; /* nz if open */
  33. int present; /* Device is present on the bus */
  34. char *obuf, *ibuf; /* transfer buffers */
  35. char bulk_in_ep, bulk_out_ep; /* Endpoint assignments */
  36. wait_queue_head_t wait_q; /* for timeouts */
  37. };
  38. static struct lcd_usb_data lcd_instance;
  39. static int open_lcd(struct inode *inode, struct file *file)
  40. {
  41. struct lcd_usb_data *lcd = &lcd_instance;
  42. if (lcd->isopen || !lcd->present) {
  43. return -EBUSY;
  44. }
  45. lcd->isopen = 1;
  46. init_waitqueue_head(&lcd->wait_q);
  47. info("USBLCD opened.");
  48. return 0;
  49. }
  50. static int close_lcd(struct inode *inode, struct file *file)
  51. {
  52. struct lcd_usb_data *lcd = &lcd_instance;
  53. lcd->isopen = 0;
  54. info("USBLCD closed.");
  55. return 0;
  56. }
  57. static int
  58. ioctl_lcd(struct inode *inode, struct file *file, unsigned int cmd,
  59.   unsigned long arg)
  60. {
  61. struct lcd_usb_data *lcd = &lcd_instance;
  62. int i;
  63. char buf[30];
  64. /* Sanity check to make sure lcd is connected, powered, etc */
  65. if (lcd == NULL ||
  66.     lcd->present == 0 ||
  67.     lcd->lcd_dev == NULL)
  68. return -1;
  69. switch (cmd) {
  70. case IOCTL_GET_HARD_VERSION:
  71. i = (lcd->lcd_dev)->descriptor.bcdDevice;
  72. sprintf(buf,"%1d%1d.%1d%1d",(i & 0xF000)>>12,(i & 0xF00)>>8,
  73. (i & 0xF0)>>4,(i & 0xF));
  74. if (copy_to_user((void *)arg,buf,strlen(buf))!=0)
  75. return -EFAULT;
  76. break;
  77. case IOCTL_GET_DRV_VERSION:
  78. sprintf(buf,DRIVER_VERSION);
  79. if (copy_to_user((void *)arg,buf,strlen(buf))!=0)
  80. return -EFAULT;
  81. break;
  82. default:
  83. return -ENOIOCTLCMD;
  84. break;
  85. }
  86. return 0;
  87. }
  88. static ssize_t
  89. write_lcd(struct file *file, const char *buffer,
  90.   size_t count, loff_t * ppos)
  91. {
  92. struct lcd_usb_data *lcd = &lcd_instance;
  93. unsigned long copy_size;
  94. unsigned long bytes_written = 0;
  95. unsigned int partial;
  96. int result = 0;
  97. int maxretry;
  98. /* Sanity check to make sure lcd is connected, powered, etc */
  99. if (lcd == NULL ||
  100.     lcd->present == 0 ||
  101.     lcd->lcd_dev == NULL)
  102. return -1;
  103. do {
  104. unsigned long thistime;
  105. char *obuf = lcd->obuf;
  106. thistime = copy_size =
  107.     (count >= OBUF_SIZE) ? OBUF_SIZE : count;
  108. if (copy_from_user(lcd->obuf, buffer, copy_size))
  109. return -EFAULT;
  110. maxretry = 5;
  111. while (thistime) {
  112. if (!lcd->lcd_dev)
  113. return -ENODEV;
  114. if (signal_pending(current)) {
  115. return bytes_written ? bytes_written : -EINTR;
  116. }
  117. result = usb_bulk_msg(lcd->lcd_dev,
  118.  usb_sndbulkpipe(lcd->lcd_dev, 1),
  119.  obuf, thistime, &partial, 10 * HZ);
  120. dbg("write stats: result:%d thistime:%lu partial:%u",
  121.      result, thistime, partial);
  122. if (result == USB_ST_TIMEOUT) { /* NAK - so hold for a while */
  123. if (!maxretry--) {
  124. return -ETIME;
  125. }
  126. interruptible_sleep_on_timeout(&lcd-> wait_q, NAK_TIMEOUT);
  127. continue;
  128. } else if (!result & partial) {
  129. obuf += partial;
  130. thistime -= partial;
  131. } else
  132. break;
  133. };
  134. if (result) {
  135. err("Write Whoops - %x", result);
  136. return -EIO;
  137. }
  138. bytes_written += copy_size;
  139. count -= copy_size;
  140. buffer += copy_size;
  141. } while (count > 0);
  142. return bytes_written ? bytes_written : -EIO;
  143. }
  144. static ssize_t
  145. read_lcd(struct file *file, char *buffer, size_t count, loff_t * ppos)
  146. {
  147. struct lcd_usb_data *lcd = &lcd_instance;
  148. ssize_t read_count;
  149. unsigned int partial;
  150. int this_read;
  151. int result;
  152. int maxretry = 10;
  153. char *ibuf = lcd->ibuf;
  154. /* Sanity check to make sure lcd is connected, powered, etc */
  155. if (lcd == NULL ||
  156.     lcd->present == 0 ||
  157.     lcd->lcd_dev == NULL)
  158. return -1;
  159. read_count = 0;
  160. while (count > 0) {
  161. if (signal_pending(current)) {
  162. return read_count ? read_count : -EINTR;
  163. }
  164. if (!lcd->lcd_dev)
  165. return -ENODEV;
  166. this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count;
  167. result = usb_bulk_msg(lcd->lcd_dev,
  168.       usb_rcvbulkpipe(lcd->lcd_dev, 0),
  169.       ibuf, this_read, &partial,
  170.       (int) (HZ * 8));
  171. dbg(KERN_DEBUG "read stats: result:%d this_read:%u partial:%u",
  172.        result, this_read, partial);
  173. if (partial) {
  174. count = this_read = partial;
  175. } else if (result == USB_ST_TIMEOUT || result == 15) { /* FIXME: 15 ??? */
  176. if (!maxretry--) {
  177. err("read_lcd: maxretry timeout");
  178. return -ETIME;
  179. }
  180. interruptible_sleep_on_timeout(&lcd->wait_q,
  181.        NAK_TIMEOUT);
  182. continue;
  183. } else if (result != USB_ST_DATAUNDERRUN) {
  184. err("Read Whoops - result:%u partial:%u this_read:%u",
  185.      result, partial, this_read);
  186. return -EIO;
  187. } else {
  188. return (0);
  189. }
  190. if (this_read) {
  191. if (copy_to_user(buffer, ibuf, this_read))
  192. return -EFAULT;
  193. count -= this_read;
  194. read_count += this_read;
  195. buffer += this_read;
  196. }
  197. }
  198. return read_count;
  199. }
  200. static void *probe_lcd(struct usb_device *dev, unsigned int ifnum)
  201. {
  202. struct lcd_usb_data *lcd = &lcd_instance;
  203. int i;
  204. if (dev->descriptor.idProduct != 0x0001  ) {
  205. warn(KERN_INFO "USBLCD model not supported.");
  206. return NULL;
  207. }
  208. if (lcd->present == 1) {
  209. warn(KERN_INFO "Multiple USBLCDs are not supported!");
  210. return NULL;
  211. }
  212. i = dev->descriptor.bcdDevice;
  213. info("USBLCD Version %1d%1d.%1d%1d found at address %d",
  214. (i & 0xF000)>>12,(i & 0xF00)>>8,(i & 0xF0)>>4,(i & 0xF),
  215. dev->devnum);
  216. lcd->present = 1;
  217. lcd->lcd_dev = dev;
  218. if (!(lcd->obuf = (char *) kmalloc(OBUF_SIZE, GFP_KERNEL))) {
  219. err("probe_lcd: Not enough memory for the output buffer");
  220. return NULL;
  221. }
  222. dbg("probe_lcd: obuf address:%p", lcd->obuf);
  223. if (!(lcd->ibuf = (char *) kmalloc(IBUF_SIZE, GFP_KERNEL))) {
  224. err("probe_lcd: Not enough memory for the input buffer");
  225. kfree(lcd->obuf);
  226. return NULL;
  227. }
  228. dbg("probe_lcd: ibuf address:%p", lcd->ibuf);
  229. return lcd;
  230. }
  231. static void disconnect_lcd(struct usb_device *dev, void *ptr)
  232. {
  233. struct lcd_usb_data *lcd = (struct lcd_usb_data *) ptr;
  234. if (lcd->isopen) {
  235. lcd->isopen = 0;
  236. /* better let it finish - the release will do whats needed */
  237. lcd->lcd_dev = NULL;
  238. return;
  239. }
  240. kfree(lcd->ibuf);
  241. kfree(lcd->obuf);
  242. info("USBLCD disconnected.");
  243. lcd->present = 0;
  244. }
  245. static struct usb_device_id id_table [] = {
  246. { .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, },
  247. {},
  248. };
  249. MODULE_DEVICE_TABLE (usb, id_table);
  250. static struct
  251. file_operations usb_lcd_fops = {
  252. .owner = THIS_MODULE,
  253. .read = read_lcd,
  254. .write = write_lcd,
  255. .ioctl = ioctl_lcd,
  256. .open = open_lcd,
  257. .release = close_lcd,
  258. };
  259. static struct
  260. usb_driver lcd_driver = {
  261. .name = "usblcd",
  262. .probe = (void *)probe_lcd,
  263. .disconnect = disconnect_lcd,
  264. .id_table = id_table,
  265. .fops = &usb_lcd_fops,
  266. .minor = USBLCD_MINOR,
  267. };
  268. int usb_lcd_init(void)
  269. {
  270. if (usb_register(&lcd_driver) < 0)
  271. return -1;
  272. info("%s (C) Adams IT Services http://www.usblcd.de", DRIVER_VERSION);
  273. info("USBLCD support registered.");
  274. return 0;
  275. }
  276. void usb_lcd_cleanup(void)
  277. {
  278. struct lcd_usb_data *lcd = &lcd_instance;
  279. lcd->present = 0;
  280. usb_deregister(&lcd_driver);
  281. }
  282. module_init(usb_lcd_init);
  283. module_exit(usb_lcd_cleanup);
  284. MODULE_AUTHOR("Adams IT Services <info@usblcd.de>");
  285. MODULE_DESCRIPTION(DRIVER_VERSION);
  286. MODULE_LICENSE("GPL");