root/drivers/usb/misc/usblcd.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. lcd_delete
  2. lcd_open
  3. lcd_release
  4. lcd_read
  5. lcd_ioctl
  6. lcd_write_bulk_callback
  7. lcd_write
  8. lcd_probe
  9. lcd_draw_down
  10. lcd_suspend
  11. lcd_resume
  12. lcd_disconnect

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*****************************************************************************
   3  *                          USBLCD Kernel Driver                             *
   4  *                            Version 1.05                                   *
   5  *             (C) 2005 Georges Toth <g.toth@e-biz.lu>                       *
   6  *                                                                           *
   7  *     This file is licensed under the GPL. See COPYING in the package.      *
   8  * Based on usb-skeleton.c 2.0 by Greg Kroah-Hartman (greg@kroah.com)        *
   9  *                                                                           *
  10  *                                                                           *
  11  * 28.02.05 Complete rewrite of the original usblcd.c driver,                *
  12  *          based on usb_skeleton.c.                                         *
  13  *          This new driver allows more than one USB-LCD to be connected     *
  14  *          and controlled, at once                                          *
  15  *****************************************************************************/
  16 #include <linux/module.h>
  17 #include <linux/kernel.h>
  18 #include <linux/slab.h>
  19 #include <linux/errno.h>
  20 #include <linux/mutex.h>
  21 #include <linux/rwsem.h>
  22 #include <linux/uaccess.h>
  23 #include <linux/usb.h>
  24 
  25 #define DRIVER_VERSION "USBLCD Driver Version 1.05"
  26 
  27 #define USBLCD_MINOR            144
  28 
  29 #define IOCTL_GET_HARD_VERSION  1
  30 #define IOCTL_GET_DRV_VERSION   2
  31 
  32 
  33 static const struct usb_device_id id_table[] = {
  34         { .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, },
  35         { },
  36 };
  37 MODULE_DEVICE_TABLE(usb, id_table);
  38 
  39 struct usb_lcd {
  40         struct usb_device       *udev;                  /* init: probe_lcd */
  41         struct usb_interface    *interface;             /* the interface for
  42                                                            this device */
  43         unsigned char           *bulk_in_buffer;        /* the buffer to receive
  44                                                            data */
  45         size_t                  bulk_in_size;           /* the size of the
  46                                                            receive buffer */
  47         __u8                    bulk_in_endpointAddr;   /* the address of the
  48                                                            bulk in endpoint */
  49         __u8                    bulk_out_endpointAddr;  /* the address of the
  50                                                            bulk out endpoint */
  51         struct kref             kref;
  52         struct semaphore        limit_sem;              /* to stop writes at
  53                                                            full throttle from
  54                                                            using up all RAM */
  55         struct usb_anchor       submitted;              /* URBs to wait for
  56                                                            before suspend */
  57         struct rw_semaphore     io_rwsem;
  58         unsigned long           disconnected:1;
  59 };
  60 #define to_lcd_dev(d) container_of(d, struct usb_lcd, kref)
  61 
  62 #define USB_LCD_CONCURRENT_WRITES       5
  63 
  64 static struct usb_driver lcd_driver;
  65 
  66 
  67 static void lcd_delete(struct kref *kref)
  68 {
  69         struct usb_lcd *dev = to_lcd_dev(kref);
  70 
  71         usb_put_dev(dev->udev);
  72         kfree(dev->bulk_in_buffer);
  73         kfree(dev);
  74 }
  75 
  76 
  77 static int lcd_open(struct inode *inode, struct file *file)
  78 {
  79         struct usb_lcd *dev;
  80         struct usb_interface *interface;
  81         int subminor, r;
  82 
  83         subminor = iminor(inode);
  84 
  85         interface = usb_find_interface(&lcd_driver, subminor);
  86         if (!interface) {
  87                 pr_err("USBLCD: %s - error, can't find device for minor %d\n",
  88                        __func__, subminor);
  89                 return -ENODEV;
  90         }
  91 
  92         dev = usb_get_intfdata(interface);
  93 
  94         /* increment our usage count for the device */
  95         kref_get(&dev->kref);
  96 
  97         /* grab a power reference */
  98         r = usb_autopm_get_interface(interface);
  99         if (r < 0) {
 100                 kref_put(&dev->kref, lcd_delete);
 101                 return r;
 102         }
 103 
 104         /* save our object in the file's private structure */
 105         file->private_data = dev;
 106 
 107         return 0;
 108 }
 109 
 110 static int lcd_release(struct inode *inode, struct file *file)
 111 {
 112         struct usb_lcd *dev;
 113 
 114         dev = file->private_data;
 115         if (dev == NULL)
 116                 return -ENODEV;
 117 
 118         /* decrement the count on our device */
 119         usb_autopm_put_interface(dev->interface);
 120         kref_put(&dev->kref, lcd_delete);
 121         return 0;
 122 }
 123 
 124 static ssize_t lcd_read(struct file *file, char __user * buffer,
 125                         size_t count, loff_t *ppos)
 126 {
 127         struct usb_lcd *dev;
 128         int retval = 0;
 129         int bytes_read;
 130 
 131         dev = file->private_data;
 132 
 133         down_read(&dev->io_rwsem);
 134 
 135         if (dev->disconnected) {
 136                 retval = -ENODEV;
 137                 goto out_up_io;
 138         }
 139 
 140         /* do a blocking bulk read to get data from the device */
 141         retval = usb_bulk_msg(dev->udev,
 142                               usb_rcvbulkpipe(dev->udev,
 143                                               dev->bulk_in_endpointAddr),
 144                               dev->bulk_in_buffer,
 145                               min(dev->bulk_in_size, count),
 146                               &bytes_read, 10000);
 147 
 148         /* if the read was successful, copy the data to userspace */
 149         if (!retval) {
 150                 if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read))
 151                         retval = -EFAULT;
 152                 else
 153                         retval = bytes_read;
 154         }
 155 
 156 out_up_io:
 157         up_read(&dev->io_rwsem);
 158 
 159         return retval;
 160 }
 161 
 162 static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 163 {
 164         struct usb_lcd *dev;
 165         u16 bcdDevice;
 166         char buf[30];
 167 
 168         dev = file->private_data;
 169         if (dev == NULL)
 170                 return -ENODEV;
 171 
 172         switch (cmd) {
 173         case IOCTL_GET_HARD_VERSION:
 174                 bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice);
 175                 sprintf(buf, "%1d%1d.%1d%1d",
 176                         (bcdDevice & 0xF000)>>12,
 177                         (bcdDevice & 0xF00)>>8,
 178                         (bcdDevice & 0xF0)>>4,
 179                         (bcdDevice & 0xF));
 180                 if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0)
 181                         return -EFAULT;
 182                 break;
 183         case IOCTL_GET_DRV_VERSION:
 184                 sprintf(buf, DRIVER_VERSION);
 185                 if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0)
 186                         return -EFAULT;
 187                 break;
 188         default:
 189                 return -ENOTTY;
 190                 break;
 191         }
 192 
 193         return 0;
 194 }
 195 
 196 static void lcd_write_bulk_callback(struct urb *urb)
 197 {
 198         struct usb_lcd *dev;
 199         int status = urb->status;
 200 
 201         dev = urb->context;
 202 
 203         /* sync/async unlink faults aren't errors */
 204         if (status &&
 205             !(status == -ENOENT ||
 206               status == -ECONNRESET ||
 207               status == -ESHUTDOWN)) {
 208                 dev_dbg(&dev->interface->dev,
 209                         "nonzero write bulk status received: %d\n", status);
 210         }
 211 
 212         /* free up our allocated buffer */
 213         usb_free_coherent(urb->dev, urb->transfer_buffer_length,
 214                           urb->transfer_buffer, urb->transfer_dma);
 215         up(&dev->limit_sem);
 216 }
 217 
 218 static ssize_t lcd_write(struct file *file, const char __user * user_buffer,
 219                          size_t count, loff_t *ppos)
 220 {
 221         struct usb_lcd *dev;
 222         int retval = 0, r;
 223         struct urb *urb = NULL;
 224         char *buf = NULL;
 225 
 226         dev = file->private_data;
 227 
 228         /* verify that we actually have some data to write */
 229         if (count == 0)
 230                 goto exit;
 231 
 232         r = down_interruptible(&dev->limit_sem);
 233         if (r < 0)
 234                 return -EINTR;
 235 
 236         down_read(&dev->io_rwsem);
 237 
 238         if (dev->disconnected) {
 239                 retval = -ENODEV;
 240                 goto err_up_io;
 241         }
 242 
 243         /* create a urb, and a buffer for it, and copy the data to the urb */
 244         urb = usb_alloc_urb(0, GFP_KERNEL);
 245         if (!urb) {
 246                 retval = -ENOMEM;
 247                 goto err_up_io;
 248         }
 249 
 250         buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL,
 251                                  &urb->transfer_dma);
 252         if (!buf) {
 253                 retval = -ENOMEM;
 254                 goto error;
 255         }
 256 
 257         if (copy_from_user(buf, user_buffer, count)) {
 258                 retval = -EFAULT;
 259                 goto error;
 260         }
 261 
 262         /* initialize the urb properly */
 263         usb_fill_bulk_urb(urb, dev->udev,
 264                           usb_sndbulkpipe(dev->udev,
 265                           dev->bulk_out_endpointAddr),
 266                           buf, count, lcd_write_bulk_callback, dev);
 267         urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 268 
 269         usb_anchor_urb(urb, &dev->submitted);
 270 
 271         /* send the data out the bulk port */
 272         retval = usb_submit_urb(urb, GFP_KERNEL);
 273         if (retval) {
 274                 dev_err(&dev->udev->dev,
 275                         "%s - failed submitting write urb, error %d\n",
 276                         __func__, retval);
 277                 goto error_unanchor;
 278         }
 279 
 280         /* release our reference to this urb,
 281            the USB core will eventually free it entirely */
 282         usb_free_urb(urb);
 283 
 284         up_read(&dev->io_rwsem);
 285 exit:
 286         return count;
 287 error_unanchor:
 288         usb_unanchor_urb(urb);
 289 error:
 290         usb_free_coherent(dev->udev, count, buf, urb->transfer_dma);
 291         usb_free_urb(urb);
 292 err_up_io:
 293         up_read(&dev->io_rwsem);
 294         up(&dev->limit_sem);
 295         return retval;
 296 }
 297 
 298 static const struct file_operations lcd_fops = {
 299         .owner =        THIS_MODULE,
 300         .read =         lcd_read,
 301         .write =        lcd_write,
 302         .open =         lcd_open,
 303         .unlocked_ioctl = lcd_ioctl,
 304         .release =      lcd_release,
 305         .llseek =        noop_llseek,
 306 };
 307 
 308 /*
 309  * usb class driver info in order to get a minor number from the usb core,
 310  * and to have the device registered with the driver core
 311  */
 312 static struct usb_class_driver lcd_class = {
 313         .name =         "lcd%d",
 314         .fops =         &lcd_fops,
 315         .minor_base =   USBLCD_MINOR,
 316 };
 317 
 318 static int lcd_probe(struct usb_interface *interface,
 319                      const struct usb_device_id *id)
 320 {
 321         struct usb_lcd *dev = NULL;
 322         struct usb_endpoint_descriptor *bulk_in, *bulk_out;
 323         int i;
 324         int retval;
 325 
 326         /* allocate memory for our device state and initialize it */
 327         dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 328         if (!dev)
 329                 return -ENOMEM;
 330 
 331         kref_init(&dev->kref);
 332         sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES);
 333         init_rwsem(&dev->io_rwsem);
 334         init_usb_anchor(&dev->submitted);
 335 
 336         dev->udev = usb_get_dev(interface_to_usbdev(interface));
 337         dev->interface = interface;
 338 
 339         if (le16_to_cpu(dev->udev->descriptor.idProduct) != 0x0001) {
 340                 dev_warn(&interface->dev, "USBLCD model not supported.\n");
 341                 retval = -ENODEV;
 342                 goto error;
 343         }
 344 
 345         /* set up the endpoint information */
 346         /* use only the first bulk-in and bulk-out endpoints */
 347         retval = usb_find_common_endpoints(interface->cur_altsetting,
 348                         &bulk_in, &bulk_out, NULL, NULL);
 349         if (retval) {
 350                 dev_err(&interface->dev,
 351                         "Could not find both bulk-in and bulk-out endpoints\n");
 352                 goto error;
 353         }
 354 
 355         dev->bulk_in_size = usb_endpoint_maxp(bulk_in);
 356         dev->bulk_in_endpointAddr = bulk_in->bEndpointAddress;
 357         dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL);
 358         if (!dev->bulk_in_buffer) {
 359                 retval = -ENOMEM;
 360                 goto error;
 361         }
 362 
 363         dev->bulk_out_endpointAddr = bulk_out->bEndpointAddress;
 364 
 365         /* save our data pointer in this interface device */
 366         usb_set_intfdata(interface, dev);
 367 
 368         /* we can register the device now, as it is ready */
 369         retval = usb_register_dev(interface, &lcd_class);
 370         if (retval) {
 371                 /* something prevented us from registering this driver */
 372                 dev_err(&interface->dev,
 373                         "Not able to get a minor for this device.\n");
 374                 goto error;
 375         }
 376 
 377         i = le16_to_cpu(dev->udev->descriptor.bcdDevice);
 378 
 379         dev_info(&interface->dev, "USBLCD Version %1d%1d.%1d%1d found "
 380                  "at address %d\n", (i & 0xF000)>>12, (i & 0xF00)>>8,
 381                  (i & 0xF0)>>4, (i & 0xF), dev->udev->devnum);
 382 
 383         /* let the user know what node this device is now attached to */
 384         dev_info(&interface->dev, "USB LCD device now attached to USBLCD-%d\n",
 385                  interface->minor);
 386         return 0;
 387 
 388 error:
 389         kref_put(&dev->kref, lcd_delete);
 390         return retval;
 391 }
 392 
 393 static void lcd_draw_down(struct usb_lcd *dev)
 394 {
 395         int time;
 396 
 397         time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
 398         if (!time)
 399                 usb_kill_anchored_urbs(&dev->submitted);
 400 }
 401 
 402 static int lcd_suspend(struct usb_interface *intf, pm_message_t message)
 403 {
 404         struct usb_lcd *dev = usb_get_intfdata(intf);
 405 
 406         if (!dev)
 407                 return 0;
 408         lcd_draw_down(dev);
 409         return 0;
 410 }
 411 
 412 static int lcd_resume(struct usb_interface *intf)
 413 {
 414         return 0;
 415 }
 416 
 417 static void lcd_disconnect(struct usb_interface *interface)
 418 {
 419         struct usb_lcd *dev = usb_get_intfdata(interface);
 420         int minor = interface->minor;
 421 
 422         /* give back our minor */
 423         usb_deregister_dev(interface, &lcd_class);
 424 
 425         down_write(&dev->io_rwsem);
 426         dev->disconnected = 1;
 427         up_write(&dev->io_rwsem);
 428 
 429         usb_kill_anchored_urbs(&dev->submitted);
 430 
 431         /* decrement our usage count */
 432         kref_put(&dev->kref, lcd_delete);
 433 
 434         dev_info(&interface->dev, "USB LCD #%d now disconnected\n", minor);
 435 }
 436 
 437 static struct usb_driver lcd_driver = {
 438         .name =         "usblcd",
 439         .probe =        lcd_probe,
 440         .disconnect =   lcd_disconnect,
 441         .suspend =      lcd_suspend,
 442         .resume =       lcd_resume,
 443         .id_table =     id_table,
 444         .supports_autosuspend = 1,
 445 };
 446 
 447 module_usb_driver(lcd_driver);
 448 
 449 MODULE_AUTHOR("Georges Toth <g.toth@e-biz.lu>");
 450 MODULE_DESCRIPTION(DRIVER_VERSION);
 451 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */