Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*****************************************************************************
0003  *                          USBLCD Kernel Driver                             *
0004  *                            Version 1.05                                   *
0005  *             (C) 2005 Georges Toth <g.toth@e-biz.lu>                       *
0006  *                                                                           *
0007  *     This file is licensed under the GPL. See COPYING in the package.      *
0008  * Based on usb-skeleton.c 2.0 by Greg Kroah-Hartman (greg@kroah.com)        *
0009  *                                                                           *
0010  *                                                                           *
0011  * 28.02.05 Complete rewrite of the original usblcd.c driver,                *
0012  *          based on usb_skeleton.c.                                         *
0013  *          This new driver allows more than one USB-LCD to be connected     *
0014  *          and controlled, at once                                          *
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;          /* init: probe_lcd */
0041     struct usb_interface    *interface;     /* the interface for
0042                                this device */
0043     unsigned char       *bulk_in_buffer;    /* the buffer to receive
0044                                data */
0045     size_t          bulk_in_size;       /* the size of the
0046                                receive buffer */
0047     __u8            bulk_in_endpointAddr;   /* the address of the
0048                                bulk in endpoint */
0049     __u8            bulk_out_endpointAddr;  /* the address of the
0050                                bulk out endpoint */
0051     struct kref     kref;
0052     struct semaphore    limit_sem;      /* to stop writes at
0053                                full throttle from
0054                                using up all RAM */
0055     struct usb_anchor   submitted;      /* URBs to wait for
0056                                before suspend */
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     /* increment our usage count for the device */
0095     kref_get(&dev->kref);
0096 
0097     /* grab a power reference */
0098     r = usb_autopm_get_interface(interface);
0099     if (r < 0) {
0100         kref_put(&dev->kref, lcd_delete);
0101         return r;
0102     }
0103 
0104     /* save our object in the file's private structure */
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     /* decrement the count on our device */
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     /* do a blocking bulk read to get data from the device */
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     /* if the read was successful, copy the data to userspace */
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     /* sync/async unlink faults aren't errors */
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     /* free up our allocated buffer */
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     /* verify that we actually have some data to write */
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     /* create a urb, and a buffer for it, and copy the data to the urb */
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     /* initialize the urb properly */
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     /* send the data out the bulk port */
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     /* release our reference to this urb,
0280        the USB core will eventually free it entirely */
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  * usb class driver info in order to get a minor number from the usb core,
0309  * and to have the device registered with the driver core
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     /* allocate memory for our device state and initialize it */
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     /* set up the endpoint information */
0345     /* use only the first bulk-in and bulk-out endpoints */
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     /* save our data pointer in this interface device */
0365     usb_set_intfdata(interface, dev);
0366 
0367     /* we can register the device now, as it is ready */
0368     retval = usb_register_dev(interface, &lcd_class);
0369     if (retval) {
0370         /* something prevented us from registering this driver */
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     /* let the user know what node this device is now attached to */
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     /* give back our minor */
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     /* decrement our usage count */
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");