0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016 #include <linux/module.h>
0017 #include <linux/kernel.h>
0018 #include <linux/slab.h>
0019 #include <linux/errno.h>
0020 #include <linux/mutex.h>
0021 #include <linux/rwsem.h>
0022 #include <linux/uaccess.h>
0023 #include <linux/usb.h>
0024
0025 #define DRIVER_VERSION "USBLCD Driver Version 1.05"
0026
0027 #define USBLCD_MINOR 144
0028
0029 #define IOCTL_GET_HARD_VERSION 1
0030 #define IOCTL_GET_DRV_VERSION 2
0031
0032
0033 static const struct usb_device_id id_table[] = {
0034 { .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, },
0035 { },
0036 };
0037 MODULE_DEVICE_TABLE(usb, id_table);
0038
0039 struct usb_lcd {
0040 struct usb_device *udev;
0041 struct usb_interface *interface;
0042
0043 unsigned char *bulk_in_buffer;
0044
0045 size_t bulk_in_size;
0046
0047 __u8 bulk_in_endpointAddr;
0048
0049 __u8 bulk_out_endpointAddr;
0050
0051 struct kref kref;
0052 struct semaphore limit_sem;
0053
0054
0055 struct usb_anchor submitted;
0056
0057 struct rw_semaphore io_rwsem;
0058 unsigned long disconnected:1;
0059 };
0060 #define to_lcd_dev(d) container_of(d, struct usb_lcd, kref)
0061
0062 #define USB_LCD_CONCURRENT_WRITES 5
0063
0064 static struct usb_driver lcd_driver;
0065
0066
0067 static void lcd_delete(struct kref *kref)
0068 {
0069 struct usb_lcd *dev = to_lcd_dev(kref);
0070
0071 usb_put_dev(dev->udev);
0072 kfree(dev->bulk_in_buffer);
0073 kfree(dev);
0074 }
0075
0076
0077 static int lcd_open(struct inode *inode, struct file *file)
0078 {
0079 struct usb_lcd *dev;
0080 struct usb_interface *interface;
0081 int subminor, r;
0082
0083 subminor = iminor(inode);
0084
0085 interface = usb_find_interface(&lcd_driver, subminor);
0086 if (!interface) {
0087 pr_err("USBLCD: %s - error, can't find device for minor %d\n",
0088 __func__, subminor);
0089 return -ENODEV;
0090 }
0091
0092 dev = usb_get_intfdata(interface);
0093
0094
0095 kref_get(&dev->kref);
0096
0097
0098 r = usb_autopm_get_interface(interface);
0099 if (r < 0) {
0100 kref_put(&dev->kref, lcd_delete);
0101 return r;
0102 }
0103
0104
0105 file->private_data = dev;
0106
0107 return 0;
0108 }
0109
0110 static int lcd_release(struct inode *inode, struct file *file)
0111 {
0112 struct usb_lcd *dev;
0113
0114 dev = file->private_data;
0115 if (dev == NULL)
0116 return -ENODEV;
0117
0118
0119 usb_autopm_put_interface(dev->interface);
0120 kref_put(&dev->kref, lcd_delete);
0121 return 0;
0122 }
0123
0124 static ssize_t lcd_read(struct file *file, char __user * buffer,
0125 size_t count, loff_t *ppos)
0126 {
0127 struct usb_lcd *dev;
0128 int retval = 0;
0129 int bytes_read;
0130
0131 dev = file->private_data;
0132
0133 down_read(&dev->io_rwsem);
0134
0135 if (dev->disconnected) {
0136 retval = -ENODEV;
0137 goto out_up_io;
0138 }
0139
0140
0141 retval = usb_bulk_msg(dev->udev,
0142 usb_rcvbulkpipe(dev->udev,
0143 dev->bulk_in_endpointAddr),
0144 dev->bulk_in_buffer,
0145 min(dev->bulk_in_size, count),
0146 &bytes_read, 10000);
0147
0148
0149 if (!retval) {
0150 if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read))
0151 retval = -EFAULT;
0152 else
0153 retval = bytes_read;
0154 }
0155
0156 out_up_io:
0157 up_read(&dev->io_rwsem);
0158
0159 return retval;
0160 }
0161
0162 static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
0163 {
0164 struct usb_lcd *dev;
0165 u16 bcdDevice;
0166 char buf[30];
0167
0168 dev = file->private_data;
0169 if (dev == NULL)
0170 return -ENODEV;
0171
0172 switch (cmd) {
0173 case IOCTL_GET_HARD_VERSION:
0174 bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice);
0175 sprintf(buf, "%1d%1d.%1d%1d",
0176 (bcdDevice & 0xF000)>>12,
0177 (bcdDevice & 0xF00)>>8,
0178 (bcdDevice & 0xF0)>>4,
0179 (bcdDevice & 0xF));
0180 if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0)
0181 return -EFAULT;
0182 break;
0183 case IOCTL_GET_DRV_VERSION:
0184 sprintf(buf, DRIVER_VERSION);
0185 if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0)
0186 return -EFAULT;
0187 break;
0188 default:
0189 return -ENOTTY;
0190 }
0191
0192 return 0;
0193 }
0194
0195 static void lcd_write_bulk_callback(struct urb *urb)
0196 {
0197 struct usb_lcd *dev;
0198 int status = urb->status;
0199
0200 dev = urb->context;
0201
0202
0203 if (status &&
0204 !(status == -ENOENT ||
0205 status == -ECONNRESET ||
0206 status == -ESHUTDOWN)) {
0207 dev_dbg(&dev->interface->dev,
0208 "nonzero write bulk status received: %d\n", status);
0209 }
0210
0211
0212 usb_free_coherent(urb->dev, urb->transfer_buffer_length,
0213 urb->transfer_buffer, urb->transfer_dma);
0214 up(&dev->limit_sem);
0215 }
0216
0217 static ssize_t lcd_write(struct file *file, const char __user * user_buffer,
0218 size_t count, loff_t *ppos)
0219 {
0220 struct usb_lcd *dev;
0221 int retval = 0, r;
0222 struct urb *urb = NULL;
0223 char *buf = NULL;
0224
0225 dev = file->private_data;
0226
0227
0228 if (count == 0)
0229 goto exit;
0230
0231 r = down_interruptible(&dev->limit_sem);
0232 if (r < 0)
0233 return -EINTR;
0234
0235 down_read(&dev->io_rwsem);
0236
0237 if (dev->disconnected) {
0238 retval = -ENODEV;
0239 goto err_up_io;
0240 }
0241
0242
0243 urb = usb_alloc_urb(0, GFP_KERNEL);
0244 if (!urb) {
0245 retval = -ENOMEM;
0246 goto err_up_io;
0247 }
0248
0249 buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL,
0250 &urb->transfer_dma);
0251 if (!buf) {
0252 retval = -ENOMEM;
0253 goto error;
0254 }
0255
0256 if (copy_from_user(buf, user_buffer, count)) {
0257 retval = -EFAULT;
0258 goto error;
0259 }
0260
0261
0262 usb_fill_bulk_urb(urb, dev->udev,
0263 usb_sndbulkpipe(dev->udev,
0264 dev->bulk_out_endpointAddr),
0265 buf, count, lcd_write_bulk_callback, dev);
0266 urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
0267
0268 usb_anchor_urb(urb, &dev->submitted);
0269
0270
0271 retval = usb_submit_urb(urb, GFP_KERNEL);
0272 if (retval) {
0273 dev_err(&dev->udev->dev,
0274 "%s - failed submitting write urb, error %d\n",
0275 __func__, retval);
0276 goto error_unanchor;
0277 }
0278
0279
0280
0281 usb_free_urb(urb);
0282
0283 up_read(&dev->io_rwsem);
0284 exit:
0285 return count;
0286 error_unanchor:
0287 usb_unanchor_urb(urb);
0288 error:
0289 usb_free_coherent(dev->udev, count, buf, urb->transfer_dma);
0290 usb_free_urb(urb);
0291 err_up_io:
0292 up_read(&dev->io_rwsem);
0293 up(&dev->limit_sem);
0294 return retval;
0295 }
0296
0297 static const struct file_operations lcd_fops = {
0298 .owner = THIS_MODULE,
0299 .read = lcd_read,
0300 .write = lcd_write,
0301 .open = lcd_open,
0302 .unlocked_ioctl = lcd_ioctl,
0303 .release = lcd_release,
0304 .llseek = noop_llseek,
0305 };
0306
0307
0308
0309
0310
0311 static struct usb_class_driver lcd_class = {
0312 .name = "lcd%d",
0313 .fops = &lcd_fops,
0314 .minor_base = USBLCD_MINOR,
0315 };
0316
0317 static int lcd_probe(struct usb_interface *interface,
0318 const struct usb_device_id *id)
0319 {
0320 struct usb_lcd *dev = NULL;
0321 struct usb_endpoint_descriptor *bulk_in, *bulk_out;
0322 int i;
0323 int retval;
0324
0325
0326 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
0327 if (!dev)
0328 return -ENOMEM;
0329
0330 kref_init(&dev->kref);
0331 sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES);
0332 init_rwsem(&dev->io_rwsem);
0333 init_usb_anchor(&dev->submitted);
0334
0335 dev->udev = usb_get_dev(interface_to_usbdev(interface));
0336 dev->interface = interface;
0337
0338 if (le16_to_cpu(dev->udev->descriptor.idProduct) != 0x0001) {
0339 dev_warn(&interface->dev, "USBLCD model not supported.\n");
0340 retval = -ENODEV;
0341 goto error;
0342 }
0343
0344
0345
0346 retval = usb_find_common_endpoints(interface->cur_altsetting,
0347 &bulk_in, &bulk_out, NULL, NULL);
0348 if (retval) {
0349 dev_err(&interface->dev,
0350 "Could not find both bulk-in and bulk-out endpoints\n");
0351 goto error;
0352 }
0353
0354 dev->bulk_in_size = usb_endpoint_maxp(bulk_in);
0355 dev->bulk_in_endpointAddr = bulk_in->bEndpointAddress;
0356 dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL);
0357 if (!dev->bulk_in_buffer) {
0358 retval = -ENOMEM;
0359 goto error;
0360 }
0361
0362 dev->bulk_out_endpointAddr = bulk_out->bEndpointAddress;
0363
0364
0365 usb_set_intfdata(interface, dev);
0366
0367
0368 retval = usb_register_dev(interface, &lcd_class);
0369 if (retval) {
0370
0371 dev_err(&interface->dev,
0372 "Not able to get a minor for this device.\n");
0373 goto error;
0374 }
0375
0376 i = le16_to_cpu(dev->udev->descriptor.bcdDevice);
0377
0378 dev_info(&interface->dev, "USBLCD Version %1d%1d.%1d%1d found "
0379 "at address %d\n", (i & 0xF000)>>12, (i & 0xF00)>>8,
0380 (i & 0xF0)>>4, (i & 0xF), dev->udev->devnum);
0381
0382
0383 dev_info(&interface->dev, "USB LCD device now attached to USBLCD-%d\n",
0384 interface->minor);
0385 return 0;
0386
0387 error:
0388 kref_put(&dev->kref, lcd_delete);
0389 return retval;
0390 }
0391
0392 static void lcd_draw_down(struct usb_lcd *dev)
0393 {
0394 int time;
0395
0396 time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
0397 if (!time)
0398 usb_kill_anchored_urbs(&dev->submitted);
0399 }
0400
0401 static int lcd_suspend(struct usb_interface *intf, pm_message_t message)
0402 {
0403 struct usb_lcd *dev = usb_get_intfdata(intf);
0404
0405 if (!dev)
0406 return 0;
0407 lcd_draw_down(dev);
0408 return 0;
0409 }
0410
0411 static int lcd_resume(struct usb_interface *intf)
0412 {
0413 return 0;
0414 }
0415
0416 static void lcd_disconnect(struct usb_interface *interface)
0417 {
0418 struct usb_lcd *dev = usb_get_intfdata(interface);
0419 int minor = interface->minor;
0420
0421
0422 usb_deregister_dev(interface, &lcd_class);
0423
0424 down_write(&dev->io_rwsem);
0425 dev->disconnected = 1;
0426 up_write(&dev->io_rwsem);
0427
0428 usb_kill_anchored_urbs(&dev->submitted);
0429
0430
0431 kref_put(&dev->kref, lcd_delete);
0432
0433 dev_info(&interface->dev, "USB LCD #%d now disconnected\n", minor);
0434 }
0435
0436 static struct usb_driver lcd_driver = {
0437 .name = "usblcd",
0438 .probe = lcd_probe,
0439 .disconnect = lcd_disconnect,
0440 .suspend = lcd_suspend,
0441 .resume = lcd_resume,
0442 .id_table = id_table,
0443 .supports_autosuspend = 1,
0444 };
0445
0446 module_usb_driver(lcd_driver);
0447
0448 MODULE_AUTHOR("Georges Toth <g.toth@e-biz.lu>");
0449 MODULE_DESCRIPTION(DRIVER_VERSION);
0450 MODULE_LICENSE("GPL");