/* SPDX-License-Identifier: GPL-2.0 */
      #ifndef _LINUX_CLEANCACHE_H
      #define _LINUX_CLEANCACHE_H
      
      #include <linux/fs.h>
      #include <linux/exportfs.h>
      #include <linux/mm.h>
      
      #define CLEANCACHE_NO_POOL                -1
      #define CLEANCACHE_NO_BACKEND                -2
      #define CLEANCACHE_NO_BACKEND_SHARED        -3
      
      #define CLEANCACHE_KEY_MAX 6
      
      /*
       * cleancache requires every file with a page in cleancache to have a
       * unique key unless/until the file is removed/truncated.  For some
       * filesystems, the inode number is unique, but for "modern" filesystems
       * an exportable filehandle is required (see exportfs.h)
       */
      struct cleancache_filekey {
              union {
                      ino_t ino;
                      __u32 fh[CLEANCACHE_KEY_MAX];
                      u32 key[CLEANCACHE_KEY_MAX];
              } u;
      };
      
      struct cleancache_ops {
              int (*init_fs)(size_t);
              int (*init_shared_fs)(uuid_t *uuid, size_t);
              int (*get_page)(int, struct cleancache_filekey,
                              pgoff_t, struct page *);
              void (*put_page)(int, struct cleancache_filekey,
                              pgoff_t, struct page *);
              void (*invalidate_page)(int, struct cleancache_filekey, pgoff_t);
              void (*invalidate_inode)(int, struct cleancache_filekey);
              void (*invalidate_fs)(int);
      };
      
      extern int cleancache_register_ops(const struct cleancache_ops *ops);
      extern void __cleancache_init_fs(struct super_block *);
      extern void __cleancache_init_shared_fs(struct super_block *);
      extern int  __cleancache_get_page(struct page *);
      extern void __cleancache_put_page(struct page *);
      extern void __cleancache_invalidate_page(struct address_space *, struct page *);
      extern void __cleancache_invalidate_inode(struct address_space *);
      extern void __cleancache_invalidate_fs(struct super_block *);
      
      #ifdef CONFIG_CLEANCACHE
      #define cleancache_enabled (1)
      static inline bool cleancache_fs_enabled_mapping(struct address_space *mapping)
      {
              return mapping->host->i_sb->cleancache_poolid >= 0;
      }
      static inline bool cleancache_fs_enabled(struct page *page)
      {
              return cleancache_fs_enabled_mapping(page->mapping);
      }
      #else
      #define cleancache_enabled (0)
      #define cleancache_fs_enabled(_page) (0)
      #define cleancache_fs_enabled_mapping(_page) (0)
      #endif
      
      /*
       * The shim layer provided by these inline functions allows the compiler
       * to reduce all cleancache hooks to nothingness if CONFIG_CLEANCACHE
       * is disabled, to a single global variable check if CONFIG_CLEANCACHE
       * is enabled but no cleancache "backend" has dynamically enabled it,
       * and, for the most frequent cleancache ops, to a single global variable
       * check plus a superblock element comparison if CONFIG_CLEANCACHE is enabled
       * and a cleancache backend has dynamically enabled cleancache, but the
       * filesystem referenced by that cleancache op has not enabled cleancache.
       * As a result, CONFIG_CLEANCACHE can be enabled by default with essentially
       * no measurable performance impact.
       */
      
      static inline void cleancache_init_fs(struct super_block *sb)
      {
              if (cleancache_enabled)
                      __cleancache_init_fs(sb);
      }
      
      static inline void cleancache_init_shared_fs(struct super_block *sb)
      {
              if (cleancache_enabled)
                      __cleancache_init_shared_fs(sb);
      }
      
      static inline int cleancache_get_page(struct page *page)
      {
              if (cleancache_enabled && cleancache_fs_enabled(page))
                      return __cleancache_get_page(page);
              return -1;
      }
      
      static inline void cleancache_put_page(struct page *page)
      {
              if (cleancache_enabled && cleancache_fs_enabled(page))
                      __cleancache_put_page(page);
      }
      
      static inline void cleancache_invalidate_page(struct address_space *mapping,
                                              struct page *page)
      {
              /* careful... page->mapping is NULL sometimes when this is called */
              if (cleancache_enabled && cleancache_fs_enabled_mapping(mapping))
                      __cleancache_invalidate_page(mapping, page);
      }
      
      static inline void cleancache_invalidate_inode(struct address_space *mapping)
      {
  331         if (cleancache_enabled && cleancache_fs_enabled_mapping(mapping))
                      __cleancache_invalidate_inode(mapping);
      }
      
      static inline void cleancache_invalidate_fs(struct super_block *sb)
      {
              if (cleancache_enabled)
                      __cleancache_invalidate_fs(sb);
      }
      
      #endif /* _LINUX_CLEANCACHE_H */
      // SPDX-License-Identifier: GPL-2.0+
      /*
       * KLSI KL5KUSB105 chip RS232 converter driver
       *
       *   Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
       *   Copyright (C) 2001 Utz-Uwe Haus <haus@uuhaus.de>
       *
       * All information about the device was acquired using SniffUSB ans snoopUSB
       * on Windows98.
       * It was written out of frustration with the PalmConnect USB Serial adapter
       * sold by Palm Inc.
       * Neither Palm, nor their contractor (MCCI) or their supplier (KLSI) provided
       * information that was not already available.
       *
       * It seems that KLSI bought some silicon-design information from ScanLogic,
       * whose SL11R processor is at the core of the KL5KUSB chipset from KLSI.
       * KLSI has firmware available for their devices; it is probable that the
       * firmware differs from that used by KLSI in their products. If you have an
       * original KLSI device and can provide some information on it, I would be
       * most interested in adding support for it here. If you have any information
       * on the protocol used (or find errors in my reverse-engineered stuff), please
       * let me know.
       *
       * The code was only tested with a PalmConnect USB adapter; if you
       * are adventurous, try it with any KLSI-based device and let me know how it
       * breaks so that I can fix it!
       */
      
      /* TODO:
       *        check modem line signals
       *        implement handshaking or decide that we do not support it
       */
      
      #include <linux/kernel.h>
      #include <linux/errno.h>
      #include <linux/slab.h>
      #include <linux/tty.h>
      #include <linux/tty_driver.h>
      #include <linux/tty_flip.h>
      #include <linux/module.h>
      #include <linux/uaccess.h>
      #include <asm/unaligned.h>
      #include <linux/usb.h>
      #include <linux/usb/serial.h>
      #include "kl5kusb105.h"
      
      #define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>, Johan Hovold <jhovold@gmail.com>"
      #define DRIVER_DESC "KLSI KL5KUSB105 chipset USB->Serial Converter driver"
      
      
      /*
       * Function prototypes
       */
      static int klsi_105_port_probe(struct usb_serial_port *port);
      static int klsi_105_port_remove(struct usb_serial_port *port);
      static int  klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port);
      static void klsi_105_close(struct usb_serial_port *port);
      static void klsi_105_set_termios(struct tty_struct *tty,
                              struct usb_serial_port *port, struct ktermios *old);
      static int  klsi_105_tiocmget(struct tty_struct *tty);
      static void klsi_105_process_read_urb(struct urb *urb);
      static int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
                                                      void *dest, size_t size);
      
      /*
       * All of the device info needed for the KLSI converters.
       */
      static const struct usb_device_id id_table[] = {
              { USB_DEVICE(PALMCONNECT_VID, PALMCONNECT_PID) },
              { }                /* Terminating entry */
      };
      
      MODULE_DEVICE_TABLE(usb, id_table);
      
      static struct usb_serial_driver kl5kusb105d_device = {
              .driver = {
                      .owner =        THIS_MODULE,
                      .name =                "kl5kusb105d",
              },
              .description =                "KL5KUSB105D / PalmConnect",
              .id_table =                id_table,
              .num_ports =                1,
              .bulk_out_size =        64,
              .open =                        klsi_105_open,
              .close =                klsi_105_close,
              .set_termios =                klsi_105_set_termios,
              .tiocmget =                klsi_105_tiocmget,
              .port_probe =                klsi_105_port_probe,
              .port_remove =                klsi_105_port_remove,
              .throttle =                usb_serial_generic_throttle,
              .unthrottle =                usb_serial_generic_unthrottle,
              .process_read_urb =        klsi_105_process_read_urb,
              .prepare_write_buffer =        klsi_105_prepare_write_buffer,
      };
      
      static struct usb_serial_driver * const serial_drivers[] = {
              &kl5kusb105d_device, NULL
      };
      
      struct klsi_105_port_settings {
              u8        pktlen;                /* always 5, it seems */
              u8        baudrate;
              u8        databits;
              u8        unknown1;
              u8        unknown2;
      };
      
      struct klsi_105_private {
              struct klsi_105_port_settings        cfg;
              unsigned long                        line_state; /* modem line settings */
              spinlock_t                        lock;
      };
      
      
      /*
       * Handle vendor specific USB requests
       */
      
      
      #define KLSI_TIMEOUT         5000 /* default urb timeout */
      
      static int klsi_105_chg_port_settings(struct usb_serial_port *port,
                                            struct klsi_105_port_settings *settings)
      {
              int rc;
      
              rc = usb_control_msg(port->serial->dev,
                              usb_sndctrlpipe(port->serial->dev, 0),
                              KL5KUSB105A_SIO_SET_DATA,
                              USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_INTERFACE,
                              0, /* value */
                              0, /* index */
                              settings,
                              sizeof(struct klsi_105_port_settings),
                              KLSI_TIMEOUT);
              if (rc < 0)
                      dev_err(&port->dev,
                              "Change port settings failed (error = %d)\n", rc);
      
              dev_dbg(&port->dev,
                      "pktlen %u, baudrate 0x%02x, databits %u, u1 %u, u2 %u\n",
                      settings->pktlen, settings->baudrate, settings->databits,
                      settings->unknown1, settings->unknown2);
      
              return rc;
      }
      
      /* translate a 16-bit status value from the device to linux's TIO bits */
      static unsigned long klsi_105_status2linestate(const __u16 status)
      {
              unsigned long res = 0;
      
              res =   ((status & KL5KUSB105A_DSR) ? TIOCM_DSR : 0)
                    | ((status & KL5KUSB105A_CTS) ? TIOCM_CTS : 0)
                    ;
      
              return res;
      }
      
      /*
       * Read line control via vendor command and return result through
       * *line_state_p
       */
      /* It seems that the status buffer has always only 2 bytes length */
      #define KLSI_STATUSBUF_LEN        2
      static int klsi_105_get_line_state(struct usb_serial_port *port,
                                         unsigned long *line_state_p)
      {
              int rc;
              u8 *status_buf;
              __u16 status;
      
              status_buf = kmalloc(KLSI_STATUSBUF_LEN, GFP_KERNEL);
              if (!status_buf)
                      return -ENOMEM;
      
              status_buf[0] = 0xff;
              status_buf[1] = 0xff;
              rc = usb_control_msg(port->serial->dev,
                                   usb_rcvctrlpipe(port->serial->dev, 0),
                                   KL5KUSB105A_SIO_POLL,
                                   USB_TYPE_VENDOR | USB_DIR_IN,
                                   0, /* value */
                                   0, /* index */
                                   status_buf, KLSI_STATUSBUF_LEN,
                                   10000
                                   );
              if (rc != KLSI_STATUSBUF_LEN) {
                      dev_err(&port->dev, "reading line status failed: %d\n", rc);
                      if (rc >= 0)
                              rc = -EIO;
              } else {
                      status = get_unaligned_le16(status_buf);
      
                      dev_dbg(&port->dev, "read status %02x %02x\n",
                              status_buf[0], status_buf[1]);
      
                      *line_state_p = klsi_105_status2linestate(status);
              }
      
              kfree(status_buf);
              return rc;
      }
      
      
      /*
       * Driver's tty interface functions
       */
      
      static int klsi_105_port_probe(struct usb_serial_port *port)
      {
              struct klsi_105_private *priv;
      
    1         priv = kmalloc(sizeof(*priv), GFP_KERNEL);
              if (!priv)
                      return -ENOMEM;
      
              /* set initial values for control structures */
    1         priv->cfg.pktlen    = 5;
              priv->cfg.baudrate  = kl5kusb105a_sio_b9600;
              priv->cfg.databits  = kl5kusb105a_dtb_8;
              priv->cfg.unknown1  = 0;
              priv->cfg.unknown2  = 1;
      
              priv->line_state    = 0;
      
              spin_lock_init(&priv->lock);
      
              usb_set_serial_port_data(port, priv);
      
    1         return 0;
      }
      
      static int klsi_105_port_remove(struct usb_serial_port *port)
      {
              struct klsi_105_private *priv;
      
    1         priv = usb_get_serial_port_data(port);
              kfree(priv);
      
              return 0;
      }
      
      static int  klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
      {
              struct klsi_105_private *priv = usb_get_serial_port_data(port);
              int retval = 0;
              int rc;
              unsigned long line_state;
              struct klsi_105_port_settings *cfg;
              unsigned long flags;
      
              /* Do a defined restart:
               * Set up sane default baud rate and send the 'READ_ON'
               * vendor command.
               * FIXME: set modem line control (how?)
               * Then read the modem line control and store values in
               * priv->line_state.
               */
              cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
              if (!cfg)
                      return -ENOMEM;
      
              cfg->pktlen   = 5;
              cfg->baudrate = kl5kusb105a_sio_b9600;
              cfg->databits = kl5kusb105a_dtb_8;
              cfg->unknown1 = 0;
              cfg->unknown2 = 1;
              klsi_105_chg_port_settings(port, cfg);
      
              spin_lock_irqsave(&priv->lock, flags);
              priv->cfg.pktlen   = cfg->pktlen;
              priv->cfg.baudrate = cfg->baudrate;
              priv->cfg.databits = cfg->databits;
              priv->cfg.unknown1 = cfg->unknown1;
              priv->cfg.unknown2 = cfg->unknown2;
              spin_unlock_irqrestore(&priv->lock, flags);
      
              /* READ_ON and urb submission */
              rc = usb_serial_generic_open(tty, port);
              if (rc) {
                      retval = rc;
                      goto err_free_cfg;
              }
      
              rc = usb_control_msg(port->serial->dev,
                                   usb_sndctrlpipe(port->serial->dev, 0),
                                   KL5KUSB105A_SIO_CONFIGURE,
                                   USB_TYPE_VENDOR|USB_DIR_OUT|USB_RECIP_INTERFACE,
                                   KL5KUSB105A_SIO_CONFIGURE_READ_ON,
                                   0, /* index */
                                   NULL,
                                   0,
                                   KLSI_TIMEOUT);
              if (rc < 0) {
                      dev_err(&port->dev, "Enabling read failed (error = %d)\n", rc);
                      retval = rc;
                      goto err_generic_close;
              } else
                      dev_dbg(&port->dev, "%s - enabled reading\n", __func__);
      
              rc = klsi_105_get_line_state(port, &line_state);
              if (rc < 0) {
                      retval = rc;
                      goto err_disable_read;
              }
      
              spin_lock_irqsave(&priv->lock, flags);
              priv->line_state = line_state;
              spin_unlock_irqrestore(&priv->lock, flags);
              dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__,
                              line_state);
      
              return 0;
      
      err_disable_read:
              usb_control_msg(port->serial->dev,
                                   usb_sndctrlpipe(port->serial->dev, 0),
                                   KL5KUSB105A_SIO_CONFIGURE,
                                   USB_TYPE_VENDOR | USB_DIR_OUT,
                                   KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
                                   0, /* index */
                                   NULL, 0,
                                   KLSI_TIMEOUT);
      err_generic_close:
              usb_serial_generic_close(port);
      err_free_cfg:
              kfree(cfg);
      
              return retval;
      }
      
      static void klsi_105_close(struct usb_serial_port *port)
      {
              int rc;
      
              /* send READ_OFF */
              rc = usb_control_msg(port->serial->dev,
                                   usb_sndctrlpipe(port->serial->dev, 0),
                                   KL5KUSB105A_SIO_CONFIGURE,
                                   USB_TYPE_VENDOR | USB_DIR_OUT,
                                   KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
                                   0, /* index */
                                   NULL, 0,
                                   KLSI_TIMEOUT);
              if (rc < 0)
                      dev_err(&port->dev, "failed to disable read: %d\n", rc);
      
              /* shutdown our bulk reads and writes */
              usb_serial_generic_close(port);
      }
      
      /* We need to write a complete 64-byte data block and encode the
       * number actually sent in the first double-byte, LSB-order. That
       * leaves at most 62 bytes of payload.
       */
      #define KLSI_HDR_LEN                2
      static int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
                                                      void *dest, size_t size)
      {
              unsigned char *buf = dest;
              int count;
      
              count = kfifo_out_locked(&port->write_fifo, buf + KLSI_HDR_LEN, size,
                                                                      &port->lock);
              put_unaligned_le16(count, buf);
      
              return count + KLSI_HDR_LEN;
      }
      
      /* The data received is preceded by a length double-byte in LSB-first order.
       */
      static void klsi_105_process_read_urb(struct urb *urb)
      {
              struct usb_serial_port *port = urb->context;
              unsigned char *data = urb->transfer_buffer;
              unsigned len;
      
              /* empty urbs seem to happen, we ignore them */
              if (!urb->actual_length)
                      return;
      
              if (urb->actual_length <= KLSI_HDR_LEN) {
                      dev_dbg(&port->dev, "%s - malformed packet\n", __func__);
                      return;
              }
      
              len = get_unaligned_le16(data);
              if (len > urb->actual_length - KLSI_HDR_LEN) {
                      dev_dbg(&port->dev, "%s - packet length mismatch\n", __func__);
                      len = urb->actual_length - KLSI_HDR_LEN;
              }
      
              tty_insert_flip_string(&port->port, data + KLSI_HDR_LEN, len);
              tty_flip_buffer_push(&port->port);
      }
      
      static void klsi_105_set_termios(struct tty_struct *tty,
                                       struct usb_serial_port *port,
                                       struct ktermios *old_termios)
      {
              struct klsi_105_private *priv = usb_get_serial_port_data(port);
              struct device *dev = &port->dev;
              unsigned int iflag = tty->termios.c_iflag;
              unsigned int old_iflag = old_termios->c_iflag;
              unsigned int cflag = tty->termios.c_cflag;
              unsigned int old_cflag = old_termios->c_cflag;
              struct klsi_105_port_settings *cfg;
              unsigned long flags;
              speed_t baud;
      
              cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
              if (!cfg)
                      return;
      
              /* lock while we are modifying the settings */
              spin_lock_irqsave(&priv->lock, flags);
      
              /*
               * Update baud rate
               */
              baud = tty_get_baud_rate(tty);
      
              switch (baud) {
              case 0: /* handled below */
                      break;
              case 1200:
                      priv->cfg.baudrate = kl5kusb105a_sio_b1200;
                      break;
              case 2400:
                      priv->cfg.baudrate = kl5kusb105a_sio_b2400;
                      break;
              case 4800:
                      priv->cfg.baudrate = kl5kusb105a_sio_b4800;
                      break;
              case 9600:
                      priv->cfg.baudrate = kl5kusb105a_sio_b9600;
                      break;
              case 19200:
                      priv->cfg.baudrate = kl5kusb105a_sio_b19200;
                      break;
              case 38400:
                      priv->cfg.baudrate = kl5kusb105a_sio_b38400;
                      break;
              case 57600:
                      priv->cfg.baudrate = kl5kusb105a_sio_b57600;
                      break;
              case 115200:
                      priv->cfg.baudrate = kl5kusb105a_sio_b115200;
                      break;
              default:
                      dev_dbg(dev, "unsupported baudrate, using 9600\n");
                      priv->cfg.baudrate = kl5kusb105a_sio_b9600;
                      baud = 9600;
                      break;
              }
      
              /*
               * FIXME: implement B0 handling
               *
               * Maybe this should be simulated by sending read disable and read
               * enable messages?
               */
      
              tty_encode_baud_rate(tty, baud, baud);
      
              if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
                      /* set the number of data bits */
                      switch (cflag & CSIZE) {
                      case CS5:
                              dev_dbg(dev, "%s - 5 bits/byte not supported\n", __func__);
                              spin_unlock_irqrestore(&priv->lock, flags);
                              goto err;
                      case CS6:
                              dev_dbg(dev, "%s - 6 bits/byte not supported\n", __func__);
                              spin_unlock_irqrestore(&priv->lock, flags);
                              goto err;
                      case CS7:
                              priv->cfg.databits = kl5kusb105a_dtb_7;
                              break;
                      case CS8:
                              priv->cfg.databits = kl5kusb105a_dtb_8;
                              break;
                      default:
                              dev_err(dev, "CSIZE was not CS5-CS8, using default of 8\n");
                              priv->cfg.databits = kl5kusb105a_dtb_8;
                              break;
                      }
              }
      
              /*
               * Update line control register (LCR)
               */
              if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD))
                  || (cflag & CSTOPB) != (old_cflag & CSTOPB)) {
                      /* Not currently supported */
                      tty->termios.c_cflag &= ~(PARENB|PARODD|CSTOPB);
              }
              /*
               * Set flow control: well, I do not really now how to handle DTR/RTS.
               * Just do what we have seen with SniffUSB on Win98.
               */
              if ((iflag & IXOFF) != (old_iflag & IXOFF)
                  || (iflag & IXON) != (old_iflag & IXON)
                  ||  (cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
                      /* Not currently supported */
                      tty->termios.c_cflag &= ~CRTSCTS;
              }
              memcpy(cfg, &priv->cfg, sizeof(*cfg));
              spin_unlock_irqrestore(&priv->lock, flags);
      
              /* now commit changes to device */
              klsi_105_chg_port_settings(port, cfg);
      err:
              kfree(cfg);
      }
      
      static int klsi_105_tiocmget(struct tty_struct *tty)
      {
              struct usb_serial_port *port = tty->driver_data;
              struct klsi_105_private *priv = usb_get_serial_port_data(port);
              unsigned long flags;
              int rc;
              unsigned long line_state;
      
              rc = klsi_105_get_line_state(port, &line_state);
              if (rc < 0) {
                      dev_err(&port->dev,
                              "Reading line control failed (error = %d)\n", rc);
                      /* better return value? EAGAIN? */
                      return rc;
              }
      
              spin_lock_irqsave(&priv->lock, flags);
              priv->line_state = line_state;
              spin_unlock_irqrestore(&priv->lock, flags);
              dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, line_state);
              return (int)line_state;
      }
      
      module_usb_serial_driver(serial_drivers, id_table);
      
      MODULE_AUTHOR(DRIVER_AUTHOR);
      MODULE_DESCRIPTION(DRIVER_DESC);
      MODULE_LICENSE("GPL");
      // SPDX-License-Identifier: GPL-2.0
      /*
       * Greybus Host Device
       *
       * Copyright 2014-2015 Google Inc.
       * Copyright 2014-2015 Linaro Ltd.
       */
      
      #include <linux/kernel.h>
      #include <linux/slab.h>
      #include <linux/greybus.h>
      
      #include "greybus_trace.h"
      
      EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_create);
      EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_release);
      EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_add);
      EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_del);
      EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_in);
      EXPORT_TRACEPOINT_SYMBOL_GPL(gb_message_submit);
      
      static struct ida gb_hd_bus_id_map;
      
      int gb_hd_output(struct gb_host_device *hd, void *req, u16 size, u8 cmd,
                       bool async)
      {
              if (!hd || !hd->driver || !hd->driver->output)
                      return -EINVAL;
              return hd->driver->output(hd, req, size, cmd, async);
      }
      EXPORT_SYMBOL_GPL(gb_hd_output);
      
      static ssize_t bus_id_show(struct device *dev,
                                 struct device_attribute *attr, char *buf)
      {
              struct gb_host_device *hd = to_gb_host_device(dev);
      
              return sprintf(buf, "%d\n", hd->bus_id);
      }
      static DEVICE_ATTR_RO(bus_id);
      
      static struct attribute *bus_attrs[] = {
              &dev_attr_bus_id.attr,
              NULL
      };
      ATTRIBUTE_GROUPS(bus);
      
      int gb_hd_cport_reserve(struct gb_host_device *hd, u16 cport_id)
      {
    2         struct ida *id_map = &hd->cport_id_map;
              int ret;
      
              ret = ida_simple_get(id_map, cport_id, cport_id + 1, GFP_KERNEL);
              if (ret < 0) {
                      dev_err(&hd->dev, "failed to reserve cport %u\n", cport_id);
                      return ret;
              }
      
    2         return 0;
      }
      EXPORT_SYMBOL_GPL(gb_hd_cport_reserve);
      
      void gb_hd_cport_release_reserved(struct gb_host_device *hd, u16 cport_id)
      {
    2         struct ida *id_map = &hd->cport_id_map;
      
              ida_simple_remove(id_map, cport_id);
      }
      EXPORT_SYMBOL_GPL(gb_hd_cport_release_reserved);
      
      /* Locking: Caller guarantees serialisation */
      int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id,
                               unsigned long flags)
      {
              struct ida *id_map = &hd->cport_id_map;
              int ida_start, ida_end;
      
    2         if (hd->driver->cport_allocate)
    2                 return hd->driver->cport_allocate(hd, cport_id, flags);
      
              if (cport_id < 0) {
                      ida_start = 0;
                      ida_end = hd->num_cports;
              } else if (cport_id < hd->num_cports) {
                      ida_start = cport_id;
                      ida_end = cport_id + 1;
              } else {
                      dev_err(&hd->dev, "cport %d not available\n", cport_id);
                      return -EINVAL;
              }
      
              return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL);
      }
      
      /* Locking: Caller guarantees serialisation */
      void gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id)
      {
    2         if (hd->driver->cport_release) {
    2                 hd->driver->cport_release(hd, cport_id);
                      return;
              }
      
              ida_simple_remove(&hd->cport_id_map, cport_id);
      }
      
      static void gb_hd_release(struct device *dev)
      {
              struct gb_host_device *hd = to_gb_host_device(dev);
      
    2         trace_gb_hd_release(hd);
      
    2         if (hd->svc)
    2                 gb_svc_put(hd->svc);
    2         ida_simple_remove(&gb_hd_bus_id_map, hd->bus_id);
              ida_destroy(&hd->cport_id_map);
              kfree(hd);
      }
      
      struct device_type greybus_hd_type = {
              .name                = "greybus_host_device",
              .release        = gb_hd_release,
      };
      
      struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver,
                                          struct device *parent,
                                          size_t buffer_size_max,
                                          size_t num_cports)
      {
              struct gb_host_device *hd;
              int ret;
      
              /*
               * Validate that the driver implements all of the callbacks
               * so that we don't have to every time we make them.
               */
    2         if ((!driver->message_send) || (!driver->message_cancel)) {
                      dev_err(parent, "mandatory hd-callbacks missing\n");
                      return ERR_PTR(-EINVAL);
              }
      
    2         if (buffer_size_max < GB_OPERATION_MESSAGE_SIZE_MIN) {
                      dev_err(parent, "greybus host-device buffers too small\n");
                      return ERR_PTR(-EINVAL);
              }
      
    2         if (num_cports == 0 || num_cports > CPORT_ID_MAX + 1) {
                      dev_err(parent, "Invalid number of CPorts: %zu\n", num_cports);
                      return ERR_PTR(-EINVAL);
              }
      
              /*
               * Make sure to never allocate messages larger than what the Greybus
               * protocol supports.
               */
    2         if (buffer_size_max > GB_OPERATION_MESSAGE_SIZE_MAX) {
                      dev_warn(parent, "limiting buffer size to %u\n",
                               GB_OPERATION_MESSAGE_SIZE_MAX);
                      buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX;
              }
      
    2         hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
              if (!hd)
                      return ERR_PTR(-ENOMEM);
      
    2         ret = ida_simple_get(&gb_hd_bus_id_map, 1, 0, GFP_KERNEL);
              if (ret < 0) {
                      kfree(hd);
                      return ERR_PTR(ret);
              }
    2         hd->bus_id = ret;
      
              hd->driver = driver;
              INIT_LIST_HEAD(&hd->modules);
              INIT_LIST_HEAD(&hd->connections);
              ida_init(&hd->cport_id_map);
              hd->buffer_size_max = buffer_size_max;
              hd->num_cports = num_cports;
      
              hd->dev.parent = parent;
              hd->dev.bus = &greybus_bus_type;
              hd->dev.type = &greybus_hd_type;
              hd->dev.groups = bus_groups;
              hd->dev.dma_mask = hd->dev.parent->dma_mask;
              device_initialize(&hd->dev);
              dev_set_name(&hd->dev, "greybus%d", hd->bus_id);
      
    2         trace_gb_hd_create(hd);
      
    2         hd->svc = gb_svc_create(hd);
    2         if (!hd->svc) {
                      dev_err(&hd->dev, "failed to create svc\n");
                      put_device(&hd->dev);
                      return ERR_PTR(-ENOMEM);
              }
      
              return hd;
      }
      EXPORT_SYMBOL_GPL(gb_hd_create);
      
      int gb_hd_add(struct gb_host_device *hd)
      {
              int ret;
      
              ret = device_add(&hd->dev);
              if (ret)
                      return ret;
      
              ret = gb_svc_add(hd->svc);
              if (ret) {
                      device_del(&hd->dev);
                      return ret;
              }
      
              trace_gb_hd_add(hd);
      
              return 0;
      }
      EXPORT_SYMBOL_GPL(gb_hd_add);
      
      void gb_hd_del(struct gb_host_device *hd)
      {
              trace_gb_hd_del(hd);
      
              /*
               * Tear down the svc and flush any on-going hotplug processing before
               * removing the remaining interfaces.
               */
              gb_svc_del(hd->svc);
      
              device_del(&hd->dev);
      }
      EXPORT_SYMBOL_GPL(gb_hd_del);
      
      void gb_hd_shutdown(struct gb_host_device *hd)
      {
              gb_svc_del(hd->svc);
      }
      EXPORT_SYMBOL_GPL(gb_hd_shutdown);
      
      void gb_hd_put(struct gb_host_device *hd)
      {
    2         put_device(&hd->dev);
      }
      EXPORT_SYMBOL_GPL(gb_hd_put);
      
      int __init gb_hd_init(void)
      {
              ida_init(&gb_hd_bus_id_map);
      
              return 0;
      }
      
      void gb_hd_exit(void)
      {
              ida_destroy(&gb_hd_bus_id_map);
      }
      // SPDX-License-Identifier: GPL-2.0+
      /*
       * MCT (Magic Control Technology Corp.) USB RS232 Converter Driver
       *
       *   Copyright (C) 2000 Wolfgang Grandegger (wolfgang@ces.ch)
       *
       * This program is largely derived from the Belkin USB Serial Adapter Driver
       * (see belkin_sa.[ch]). All of the information about the device was acquired
       * by using SniffUSB on Windows98. For technical details see mct_u232.h.
       *
       * William G. Greathouse and Greg Kroah-Hartman provided great help on how to
       * do the reverse engineering and how to write a USB serial device driver.
       *
       * TO BE DONE, TO BE CHECKED:
       *   DTR/RTS signal handling may be incomplete or incorrect. I have mainly
       *   implemented what I have seen with SniffUSB or found in belkin_sa.c.
       *   For further TODOs check also belkin_sa.c.
       */
      
      #include <linux/kernel.h>
      #include <linux/errno.h>
      #include <linux/slab.h>
      #include <linux/tty.h>
      #include <linux/tty_driver.h>
      #include <linux/tty_flip.h>
      #include <linux/module.h>
      #include <linux/spinlock.h>
      #include <linux/uaccess.h>
      #include <asm/unaligned.h>
      #include <linux/usb.h>
      #include <linux/usb/serial.h>
      #include <linux/serial.h>
      #include "mct_u232.h"
      
      #define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>"
      #define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver"
      
      /*
       * Function prototypes
       */
      static int  mct_u232_port_probe(struct usb_serial_port *port);
      static int  mct_u232_port_remove(struct usb_serial_port *remove);
      static int  mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port);
      static void mct_u232_close(struct usb_serial_port *port);
      static void mct_u232_dtr_rts(struct usb_serial_port *port, int on);
      static void mct_u232_read_int_callback(struct urb *urb);
      static void mct_u232_set_termios(struct tty_struct *tty,
                              struct usb_serial_port *port, struct ktermios *old);
      static void mct_u232_break_ctl(struct tty_struct *tty, int break_state);
      static int  mct_u232_tiocmget(struct tty_struct *tty);
      static int  mct_u232_tiocmset(struct tty_struct *tty,
                              unsigned int set, unsigned int clear);
      static void mct_u232_throttle(struct tty_struct *tty);
      static void mct_u232_unthrottle(struct tty_struct *tty);
      
      
      /*
       * All of the device info needed for the MCT USB-RS232 converter.
       */
      static const struct usb_device_id id_table[] = {
              { USB_DEVICE(MCT_U232_VID, MCT_U232_PID) },
              { USB_DEVICE(MCT_U232_VID, MCT_U232_SITECOM_PID) },
              { USB_DEVICE(MCT_U232_VID, MCT_U232_DU_H3SP_PID) },
              { USB_DEVICE(MCT_U232_BELKIN_F5U109_VID, MCT_U232_BELKIN_F5U109_PID) },
              { }                /* Terminating entry */
      };
      MODULE_DEVICE_TABLE(usb, id_table);
      
      static struct usb_serial_driver mct_u232_device = {
              .driver = {
                      .owner =        THIS_MODULE,
                      .name =                "mct_u232",
              },
              .description =             "MCT U232",
              .id_table =             id_table,
              .num_ports =             1,
              .open =                     mct_u232_open,
              .close =             mct_u232_close,
              .dtr_rts =             mct_u232_dtr_rts,
              .throttle =             mct_u232_throttle,
              .unthrottle =             mct_u232_unthrottle,
              .read_int_callback = mct_u232_read_int_callback,
              .set_termios =             mct_u232_set_termios,
              .break_ctl =             mct_u232_break_ctl,
              .tiocmget =             mct_u232_tiocmget,
              .tiocmset =             mct_u232_tiocmset,
              .tiocmiwait =        usb_serial_generic_tiocmiwait,
              .port_probe =        mct_u232_port_probe,
              .port_remove =       mct_u232_port_remove,
              .get_icount =        usb_serial_generic_get_icount,
      };
      
      static struct usb_serial_driver * const serial_drivers[] = {
              &mct_u232_device, NULL
      };
      
      struct mct_u232_private {
              struct urb *read_urb;
              spinlock_t lock;
              unsigned int             control_state; /* Modem Line Setting (TIOCM) */
              unsigned char        last_lcr;      /* Line Control Register */
              unsigned char             last_lsr;      /* Line Status Register */
              unsigned char             last_msr;      /* Modem Status Register */
              unsigned int             rx_flags;      /* Throttling flags */
      };
      
      #define THROTTLED                0x01
      
      /*
       * Handle vendor specific USB requests
       */
      
      #define WDR_TIMEOUT 5000 /* default urb timeout */
      
      /*
       * Later day 2.6.0-test kernels have new baud rates like B230400 which
       * we do not know how to support. We ignore them for the moment.
       */
      static int mct_u232_calculate_baud_rate(struct usb_serial *serial,
                                              speed_t value, speed_t *result)
      {
              *result = value;
      
              if (le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_SITECOM_PID
                      || le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_BELKIN_F5U109_PID) {
                      switch (value) {
                      case 300:
                              return 0x01;
                      case 600:
                              return 0x02; /* this one not tested */
                      case 1200:
                              return 0x03;
                      case 2400:
                              return 0x04;
                      case 4800:
                              return 0x06;
                      case 9600:
                              return 0x08;
                      case 19200:
                              return 0x09;
                      case 38400:
                              return 0x0a;
                      case 57600:
                              return 0x0b;
                      case 115200:
                              return 0x0c;
                      default:
                              *result = 9600;
                              return 0x08;
                      }
              } else {
                      /* FIXME: Can we use any divider - should we do
                         divider = 115200/value;
                         real baud = 115200/divider */
                      switch (value) {
                      case 300: break;
                      case 600: break;
                      case 1200: break;
                      case 2400: break;
                      case 4800: break;
                      case 9600: break;
                      case 19200: break;
                      case 38400: break;
                      case 57600: break;
                      case 115200: break;
                      default:
                              value = 9600;
                              *result = 9600;
                      }
                      return 115200/value;
              }
      }
      
      static int mct_u232_set_baud_rate(struct tty_struct *tty,
              struct usb_serial *serial, struct usb_serial_port *port, speed_t value)
      {
              unsigned int divisor;
              int rc;
              unsigned char *buf;
              unsigned char cts_enable_byte = 0;
              speed_t speed;
      
              buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
              if (buf == NULL)
                      return -ENOMEM;
      
              divisor = mct_u232_calculate_baud_rate(serial, value, &speed);
              put_unaligned_le32(divisor, buf);
              rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
                                      MCT_U232_SET_BAUD_RATE_REQUEST,
                                      MCT_U232_SET_REQUEST_TYPE,
                                      0, 0, buf, MCT_U232_SET_BAUD_RATE_SIZE,
                                      WDR_TIMEOUT);
              if (rc < 0)        /*FIXME: What value speed results */
                      dev_err(&port->dev, "Set BAUD RATE %d failed (error = %d)\n",
                              value, rc);
              else
                      tty_encode_baud_rate(tty, speed, speed);
              dev_dbg(&port->dev, "set_baud_rate: value: 0x%x, divisor: 0x%x\n", value, divisor);
      
              /* Mimic the MCT-supplied Windows driver (version 1.21P.0104), which
                 always sends two extra USB 'device request' messages after the
                 'baud rate change' message.  The actual functionality of the
                 request codes in these messages is not fully understood but these
                 particular codes are never seen in any operation besides a baud
                 rate change.  Both of these messages send a single byte of data.
                 In the first message, the value of this byte is always zero.
      
                 The second message has been determined experimentally to control
                 whether data will be transmitted to a device which is not asserting
                 the 'CTS' signal.  If the second message's data byte is zero, data
                 will be transmitted even if 'CTS' is not asserted (i.e. no hardware
                 flow control).  if the second message's data byte is nonzero (a
                 value of 1 is used by this driver), data will not be transmitted to
                 a device which is not asserting 'CTS'.
              */
      
              buf[0] = 0;
              rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
                                      MCT_U232_SET_UNKNOWN1_REQUEST,
                                      MCT_U232_SET_REQUEST_TYPE,
                                      0, 0, buf, MCT_U232_SET_UNKNOWN1_SIZE,
                                      WDR_TIMEOUT);
              if (rc < 0)
                      dev_err(&port->dev, "Sending USB device request code %d "
                              "failed (error = %d)\n", MCT_U232_SET_UNKNOWN1_REQUEST,
                              rc);
      
              if (port && C_CRTSCTS(tty))
                 cts_enable_byte = 1;
      
              dev_dbg(&port->dev, "set_baud_rate: send second control message, data = %02X\n",
                      cts_enable_byte);
              buf[0] = cts_enable_byte;
              rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
                              MCT_U232_SET_CTS_REQUEST,
                              MCT_U232_SET_REQUEST_TYPE,
                              0, 0, buf, MCT_U232_SET_CTS_SIZE,
                              WDR_TIMEOUT);
              if (rc < 0)
                      dev_err(&port->dev, "Sending USB device request code %d "
                              "failed (error = %d)\n", MCT_U232_SET_CTS_REQUEST, rc);
      
              kfree(buf);
              return rc;
      } /* mct_u232_set_baud_rate */
      
      static int mct_u232_set_line_ctrl(struct usb_serial_port *port,
                                        unsigned char lcr)
      {
              int rc;
              unsigned char *buf;
      
              buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
              if (buf == NULL)
                      return -ENOMEM;
      
              buf[0] = lcr;
              rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
                              MCT_U232_SET_LINE_CTRL_REQUEST,
                              MCT_U232_SET_REQUEST_TYPE,
                              0, 0, buf, MCT_U232_SET_LINE_CTRL_SIZE,
                              WDR_TIMEOUT);
              if (rc < 0)
                      dev_err(&port->dev, "Set LINE CTRL 0x%x failed (error = %d)\n", lcr, rc);
              dev_dbg(&port->dev, "set_line_ctrl: 0x%x\n", lcr);
              kfree(buf);
              return rc;
      } /* mct_u232_set_line_ctrl */
      
      static int mct_u232_set_modem_ctrl(struct usb_serial_port *port,
                                         unsigned int control_state)
      {
              int rc;
              unsigned char mcr;
              unsigned char *buf;
      
              buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
              if (buf == NULL)
                      return -ENOMEM;
      
              mcr = MCT_U232_MCR_NONE;
              if (control_state & TIOCM_DTR)
                      mcr |= MCT_U232_MCR_DTR;
              if (control_state & TIOCM_RTS)
                      mcr |= MCT_U232_MCR_RTS;
      
              buf[0] = mcr;
              rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
                              MCT_U232_SET_MODEM_CTRL_REQUEST,
                              MCT_U232_SET_REQUEST_TYPE,
                              0, 0, buf, MCT_U232_SET_MODEM_CTRL_SIZE,
                              WDR_TIMEOUT);
              kfree(buf);
      
              dev_dbg(&port->dev, "set_modem_ctrl: state=0x%x ==> mcr=0x%x\n", control_state, mcr);
      
              if (rc < 0) {
                      dev_err(&port->dev, "Set MODEM CTRL 0x%x failed (error = %d)\n", mcr, rc);
                      return rc;
              }
              return 0;
      } /* mct_u232_set_modem_ctrl */
      
      static int mct_u232_get_modem_stat(struct usb_serial_port *port,
                                         unsigned char *msr)
      {
              int rc;
              unsigned char *buf;
      
              buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
              if (buf == NULL) {
                      *msr = 0;
                      return -ENOMEM;
              }
              rc = usb_control_msg(port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0),
                              MCT_U232_GET_MODEM_STAT_REQUEST,
                              MCT_U232_GET_REQUEST_TYPE,
                              0, 0, buf, MCT_U232_GET_MODEM_STAT_SIZE,
                              WDR_TIMEOUT);
              if (rc < MCT_U232_GET_MODEM_STAT_SIZE) {
                      dev_err(&port->dev, "Get MODEM STATus failed (error = %d)\n", rc);
      
                      if (rc >= 0)
                              rc = -EIO;
      
                      *msr = 0;
              } else {
                      *msr = buf[0];
              }
              dev_dbg(&port->dev, "get_modem_stat: 0x%x\n", *msr);
              kfree(buf);
              return rc;
      } /* mct_u232_get_modem_stat */
      
      static void mct_u232_msr_to_icount(struct async_icount *icount,
                                                      unsigned char msr)
      {
              /* Translate Control Line states */
              if (msr & MCT_U232_MSR_DDSR)
                      icount->dsr++;
              if (msr & MCT_U232_MSR_DCTS)
                      icount->cts++;
              if (msr & MCT_U232_MSR_DRI)
                      icount->rng++;
              if (msr & MCT_U232_MSR_DCD)
                      icount->dcd++;
      } /* mct_u232_msr_to_icount */
      
      static void mct_u232_msr_to_state(struct usb_serial_port *port,
                                        unsigned int *control_state, unsigned char msr)
      {
              /* Translate Control Line states */
              if (msr & MCT_U232_MSR_DSR)
                      *control_state |=  TIOCM_DSR;
              else
                      *control_state &= ~TIOCM_DSR;
              if (msr & MCT_U232_MSR_CTS)
                      *control_state |=  TIOCM_CTS;
              else
                      *control_state &= ~TIOCM_CTS;
              if (msr & MCT_U232_MSR_RI)
                      *control_state |=  TIOCM_RI;
              else
                      *control_state &= ~TIOCM_RI;
              if (msr & MCT_U232_MSR_CD)
                      *control_state |=  TIOCM_CD;
              else
                      *control_state &= ~TIOCM_CD;
              dev_dbg(&port->dev, "msr_to_state: msr=0x%x ==> state=0x%x\n", msr, *control_state);
      } /* mct_u232_msr_to_state */
      
      /*
       * Driver's tty interface functions
       */
      
      static int mct_u232_port_probe(struct usb_serial_port *port)
      {
    1         struct usb_serial *serial = port->serial;
              struct mct_u232_private *priv;
      
              /* check first to simplify error handling */
    1         if (!serial->port[1] || !serial->port[1]->interrupt_in_urb) {
                      dev_err(&port->dev, "expected endpoint missing\n");
                      return -ENODEV;
              }
      
    1         priv = kzalloc(sizeof(*priv), GFP_KERNEL);
              if (!priv)
                      return -ENOMEM;
      
              /* Use second interrupt-in endpoint for reading. */
    1         priv->read_urb = serial->port[1]->interrupt_in_urb;
              priv->read_urb->context = port;
      
              spin_lock_init(&priv->lock);
      
              usb_set_serial_port_data(port, priv);
      
    1         return 0;
      }
      
      static int mct_u232_port_remove(struct usb_serial_port *port)
      {
              struct mct_u232_private *priv;
      
    1         priv = usb_get_serial_port_data(port);
              kfree(priv);
      
              return 0;
      }
      
      static int  mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port)
      {
              struct usb_serial *serial = port->serial;
              struct mct_u232_private *priv = usb_get_serial_port_data(port);
              int retval = 0;
              unsigned int control_state;
              unsigned long flags;
              unsigned char last_lcr;
              unsigned char last_msr;
      
              /* Compensate for a hardware bug: although the Sitecom U232-P25
               * device reports a maximum output packet size of 32 bytes,
               * it seems to be able to accept only 16 bytes (and that's what
               * SniffUSB says too...)
               */
              if (le16_to_cpu(serial->dev->descriptor.idProduct)
                                                      == MCT_U232_SITECOM_PID)
                      port->bulk_out_size = 16;
      
              /* Do a defined restart: the normal serial device seems to
               * always turn on DTR and RTS here, so do the same. I'm not
               * sure if this is really necessary. But it should not harm
               * either.
               */
              spin_lock_irqsave(&priv->lock, flags);
              if (tty && C_BAUD(tty))
                      priv->control_state = TIOCM_DTR | TIOCM_RTS;
              else
                      priv->control_state = 0;
      
              priv->last_lcr = (MCT_U232_DATA_BITS_8 |
                                MCT_U232_PARITY_NONE |
                                MCT_U232_STOP_BITS_1);
              control_state = priv->control_state;
              last_lcr = priv->last_lcr;
              spin_unlock_irqrestore(&priv->lock, flags);
              mct_u232_set_modem_ctrl(port, control_state);
              mct_u232_set_line_ctrl(port, last_lcr);
      
              /* Read modem status and update control state */
              mct_u232_get_modem_stat(port, &last_msr);
              spin_lock_irqsave(&priv->lock, flags);
              priv->last_msr = last_msr;
              mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr);
              spin_unlock_irqrestore(&priv->lock, flags);
      
              retval = usb_submit_urb(priv->read_urb, GFP_KERNEL);
              if (retval) {
                      dev_err(&port->dev,
                              "usb_submit_urb(read) failed pipe 0x%x err %d\n",
                              port->read_urb->pipe, retval);
                      goto error;
              }
      
              retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
              if (retval) {
                      usb_kill_urb(priv->read_urb);
                      dev_err(&port->dev,
                              "usb_submit_urb(read int) failed pipe 0x%x err %d",
                              port->interrupt_in_urb->pipe, retval);
                      goto error;
              }
              return 0;
      
      error:
              return retval;
      } /* mct_u232_open */
      
      static void mct_u232_dtr_rts(struct usb_serial_port *port, int on)
      {
              unsigned int control_state;
              struct mct_u232_private *priv = usb_get_serial_port_data(port);
      
              spin_lock_irq(&priv->lock);
              if (on)
                      priv->control_state |= TIOCM_DTR | TIOCM_RTS;
              else
                      priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
              control_state = priv->control_state;
              spin_unlock_irq(&priv->lock);
      
              mct_u232_set_modem_ctrl(port, control_state);
      }
      
      static void mct_u232_close(struct usb_serial_port *port)
      {
              struct mct_u232_private *priv = usb_get_serial_port_data(port);
      
              usb_kill_urb(priv->read_urb);
              usb_kill_urb(port->interrupt_in_urb);
      
              usb_serial_generic_close(port);
      } /* mct_u232_close */
      
      
      static void mct_u232_read_int_callback(struct urb *urb)
      {
              struct usb_serial_port *port = urb->context;
              struct mct_u232_private *priv = usb_get_serial_port_data(port);
              unsigned char *data = urb->transfer_buffer;
              int retval;
              int status = urb->status;
              unsigned long flags;
      
              switch (status) {
              case 0:
                      /* success */
                      break;
              case -ECONNRESET:
              case -ENOENT:
              case -ESHUTDOWN:
                      /* this urb is terminated, clean up */
                      dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
                              __func__, status);
                      return;
              default:
                      dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
                              __func__, status);
                      goto exit;
              }
      
              usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
      
              /*
               * Work-a-round: handle the 'usual' bulk-in pipe here
               */
              if (urb->transfer_buffer_length > 2) {
                      if (urb->actual_length) {
                              tty_insert_flip_string(&port->port, data,
                                              urb->actual_length);
                              tty_flip_buffer_push(&port->port);
                      }
                      goto exit;
              }
      
              /*
               * The interrupt-in pipe signals exceptional conditions (modem line
               * signal changes and errors). data[0] holds MSR, data[1] holds LSR.
               */
              spin_lock_irqsave(&priv->lock, flags);
              priv->last_msr = data[MCT_U232_MSR_INDEX];
      
              /* Record Control Line states */
              mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr);
      
              mct_u232_msr_to_icount(&port->icount, priv->last_msr);
      
      #if 0
              /* Not yet handled. See belkin_sa.c for further information */
              /* Now to report any errors */
              priv->last_lsr = data[MCT_U232_LSR_INDEX];
              /*
               * fill in the flip buffer here, but I do not know the relation
               * to the current/next receive buffer or characters.  I need
               * to look in to this before committing any code.
               */
              if (priv->last_lsr & MCT_U232_LSR_ERR) {
                      tty = tty_port_tty_get(&port->port);
                      /* Overrun Error */
                      if (priv->last_lsr & MCT_U232_LSR_OE) {
                      }
                      /* Parity Error */
                      if (priv->last_lsr & MCT_U232_LSR_PE) {
                      }
                      /* Framing Error */
                      if (priv->last_lsr & MCT_U232_LSR_FE) {
                      }
                      /* Break Indicator */
                      if (priv->last_lsr & MCT_U232_LSR_BI) {
                      }
                      tty_kref_put(tty);
              }
      #endif
              wake_up_interruptible(&port->port.delta_msr_wait);
              spin_unlock_irqrestore(&priv->lock, flags);
      exit:
              retval = usb_submit_urb(urb, GFP_ATOMIC);
              if (retval)
                      dev_err(&port->dev,
                              "%s - usb_submit_urb failed with result %d\n",
                              __func__, retval);
      } /* mct_u232_read_int_callback */
      
      static void mct_u232_set_termios(struct tty_struct *tty,
                                       struct usb_serial_port *port,
                                       struct ktermios *old_termios)
      {
              struct usb_serial *serial = port->serial;
              struct mct_u232_private *priv = usb_get_serial_port_data(port);
              struct ktermios *termios = &tty->termios;
              unsigned int cflag = termios->c_cflag;
              unsigned int old_cflag = old_termios->c_cflag;
              unsigned long flags;
              unsigned int control_state;
              unsigned char last_lcr;
      
              /* get a local copy of the current port settings */
              spin_lock_irqsave(&priv->lock, flags);
              control_state = priv->control_state;
              spin_unlock_irqrestore(&priv->lock, flags);
              last_lcr = 0;
      
              /*
               * Update baud rate.
               * Do not attempt to cache old rates and skip settings,
               * disconnects screw such tricks up completely.
               * Premature optimization is the root of all evil.
               */
      
              /* reassert DTR and RTS on transition from B0 */
              if ((old_cflag & CBAUD) == B0) {
                      dev_dbg(&port->dev, "%s: baud was B0\n", __func__);
                      control_state |= TIOCM_DTR | TIOCM_RTS;
                      mct_u232_set_modem_ctrl(port, control_state);
              }
      
              mct_u232_set_baud_rate(tty, serial, port, tty_get_baud_rate(tty));
      
              if ((cflag & CBAUD) == B0) {
                      dev_dbg(&port->dev, "%s: baud is B0\n", __func__);
                      /* Drop RTS and DTR */
                      control_state &= ~(TIOCM_DTR | TIOCM_RTS);
                      mct_u232_set_modem_ctrl(port, control_state);
              }
      
              /*
               * Update line control register (LCR)
               */
      
              /* set the parity */
              if (cflag & PARENB)
                      last_lcr |= (cflag & PARODD) ?
                              MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN;
              else
                      last_lcr |= MCT_U232_PARITY_NONE;
      
              /* set the number of data bits */
              switch (cflag & CSIZE) {
              case CS5:
                      last_lcr |= MCT_U232_DATA_BITS_5; break;
              case CS6:
                      last_lcr |= MCT_U232_DATA_BITS_6; break;
              case CS7:
                      last_lcr |= MCT_U232_DATA_BITS_7; break;
              case CS8:
                      last_lcr |= MCT_U232_DATA_BITS_8; break;
              default:
                      dev_err(&port->dev,
                              "CSIZE was not CS5-CS8, using default of 8\n");
                      last_lcr |= MCT_U232_DATA_BITS_8;
                      break;
              }
      
              termios->c_cflag &= ~CMSPAR;
      
              /* set the number of stop bits */
              last_lcr |= (cflag & CSTOPB) ?
                      MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1;
      
              mct_u232_set_line_ctrl(port, last_lcr);
      
              /* save off the modified port settings */
              spin_lock_irqsave(&priv->lock, flags);
              priv->control_state = control_state;
              priv->last_lcr = last_lcr;
              spin_unlock_irqrestore(&priv->lock, flags);
      } /* mct_u232_set_termios */
      
      static void mct_u232_break_ctl(struct tty_struct *tty, int break_state)
      {
              struct usb_serial_port *port = tty->driver_data;
              struct mct_u232_private *priv = usb_get_serial_port_data(port);
              unsigned char lcr;
              unsigned long flags;
      
              spin_lock_irqsave(&priv->lock, flags);
              lcr = priv->last_lcr;
      
              if (break_state)
                      lcr |= MCT_U232_SET_BREAK;
              spin_unlock_irqrestore(&priv->lock, flags);
      
              mct_u232_set_line_ctrl(port, lcr);
      } /* mct_u232_break_ctl */
      
      
      static int mct_u232_tiocmget(struct tty_struct *tty)
      {
              struct usb_serial_port *port = tty->driver_data;
              struct mct_u232_private *priv = usb_get_serial_port_data(port);
              unsigned int control_state;
              unsigned long flags;
      
              spin_lock_irqsave(&priv->lock, flags);
              control_state = priv->control_state;
              spin_unlock_irqrestore(&priv->lock, flags);
      
              return control_state;
      }
      
      static int mct_u232_tiocmset(struct tty_struct *tty,
                                    unsigned int set, unsigned int clear)
      {
              struct usb_serial_port *port = tty->driver_data;
              struct mct_u232_private *priv = usb_get_serial_port_data(port);
              unsigned int control_state;
              unsigned long flags;
      
              spin_lock_irqsave(&priv->lock, flags);
              control_state = priv->control_state;
      
              if (set & TIOCM_RTS)
                      control_state |= TIOCM_RTS;
              if (set & TIOCM_DTR)
                      control_state |= TIOCM_DTR;
              if (clear & TIOCM_RTS)
                      control_state &= ~TIOCM_RTS;
              if (clear & TIOCM_DTR)
                      control_state &= ~TIOCM_DTR;
      
              priv->control_state = control_state;
              spin_unlock_irqrestore(&priv->lock, flags);
              return mct_u232_set_modem_ctrl(port, control_state);
      }
      
      static void mct_u232_throttle(struct tty_struct *tty)
      {
              struct usb_serial_port *port = tty->driver_data;
              struct mct_u232_private *priv = usb_get_serial_port_data(port);
              unsigned int control_state;
      
              spin_lock_irq(&priv->lock);
              priv->rx_flags |= THROTTLED;
              if (C_CRTSCTS(tty)) {
                      priv->control_state &= ~TIOCM_RTS;
                      control_state = priv->control_state;
                      spin_unlock_irq(&priv->lock);
                      mct_u232_set_modem_ctrl(port, control_state);
              } else {
                      spin_unlock_irq(&priv->lock);
              }
      }
      
      static void mct_u232_unthrottle(struct tty_struct *tty)
      {
              struct usb_serial_port *port = tty->driver_data;
              struct mct_u232_private *priv = usb_get_serial_port_data(port);
              unsigned int control_state;
      
              spin_lock_irq(&priv->lock);
              if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) {
                      priv->rx_flags &= ~THROTTLED;
                      priv->control_state |= TIOCM_RTS;
                      control_state = priv->control_state;
                      spin_unlock_irq(&priv->lock);
                      mct_u232_set_modem_ctrl(port, control_state);
              } else {
                      spin_unlock_irq(&priv->lock);
              }
      }
      
      module_usb_serial_driver(serial_drivers, id_table);
      
      MODULE_AUTHOR(DRIVER_AUTHOR);
      MODULE_DESCRIPTION(DRIVER_DESC);
      MODULE_LICENSE("GPL");
      // SPDX-License-Identifier: GPL-2.0-only
      /* Linux driver for devices based on the DiBcom DiB0700 USB bridge
       *
       *  Copyright (C) 2005-6 DiBcom, SA
       */
      #include "dib0700.h"
      
      /* debug */
      int dvb_usb_dib0700_debug;
      module_param_named(debug,dvb_usb_dib0700_debug, int, 0644);
      MODULE_PARM_DESC(debug, "set debugging level (1=info,2=fw,4=fwdata,8=data (or-able))." DVB_USB_DEBUG_STATUS);
      
      static int nb_packet_buffer_size = 21;
      module_param(nb_packet_buffer_size, int, 0644);
      MODULE_PARM_DESC(nb_packet_buffer_size,
              "Set the dib0700 driver data buffer size. This parameter corresponds to the number of TS packets. The actual size of the data buffer corresponds to this parameter multiplied by 188 (default: 21)");
      
      DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
      
      
      int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion,
                              u32 *romversion, u32 *ramversion, u32 *fwtype)
      {
              struct dib0700_state *st = d->priv;
              int ret;
      
              if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
                      err("could not acquire lock");
                      return -EINTR;
              }
      
              ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0),
                                        REQUEST_GET_VERSION,
                                        USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
                                        st->buf, 16, USB_CTRL_GET_TIMEOUT);
              if (hwversion != NULL)
                      *hwversion  = (st->buf[0] << 24)  | (st->buf[1] << 16)  |
                              (st->buf[2] << 8)  | st->buf[3];
              if (romversion != NULL)
                      *romversion = (st->buf[4] << 24)  | (st->buf[5] << 16)  |
                              (st->buf[6] << 8)  | st->buf[7];
              if (ramversion != NULL)
                      *ramversion = (st->buf[8] << 24)  | (st->buf[9] << 16)  |
                              (st->buf[10] << 8) | st->buf[11];
              if (fwtype != NULL)
                      *fwtype     = (st->buf[12] << 24) | (st->buf[13] << 16) |
                              (st->buf[14] << 8) | st->buf[15];
              mutex_unlock(&d->usb_mutex);
              return ret;
      }
      
      /* expecting rx buffer: request data[0] data[1] ... data[2] */
      static int dib0700_ctrl_wr(struct dvb_usb_device *d, u8 *tx, u8 txlen)
      {
              int status;
      
              deb_data(">>> ");
              debug_dump(tx, txlen, deb_data);
      
              status = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev,0),
                      tx[0], USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, tx, txlen,
                      USB_CTRL_GET_TIMEOUT);
      
              if (status != txlen)
                      deb_data("ep 0 write error (status = %d, len: %d)\n",status,txlen);
      
              return status < 0 ? status : 0;
      }
      
      /* expecting tx buffer: request data[0] ... data[n] (n <= 4) */
      int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen)
      {
              u16 index, value;
              int status;
      
              if (txlen < 2) {
                      err("tx buffer length is smaller than 2. Makes no sense.");
                      return -EINVAL;
              }
              if (txlen > 4) {
                      err("tx buffer length is larger than 4. Not supported.");
                      return -EINVAL;
              }
      
              deb_data(">>> ");
              debug_dump(tx,txlen,deb_data);
      
              value = ((txlen - 2) << 8) | tx[1];
              index = 0;
              if (txlen > 2)
                      index |= (tx[2] << 8);
              if (txlen > 3)
                      index |= tx[3];
      
              status = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev,0), tx[0],
                              USB_TYPE_VENDOR | USB_DIR_IN, value, index, rx, rxlen,
                              USB_CTRL_GET_TIMEOUT);
      
              if (status < 0)
                      deb_info("ep 0 read error (status = %d)\n",status);
      
              deb_data("<<< ");
              debug_dump(rx, rxlen, deb_data);
      
              return status; /* length in case of success */
      }
      
      int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val)
      {
              struct dib0700_state *st = d->priv;
              int ret;
      
              if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
                      err("could not acquire lock");
                      return -EINTR;
              }
      
              st->buf[0] = REQUEST_SET_GPIO;
              st->buf[1] = gpio;
              st->buf[2] = ((gpio_dir & 0x01) << 7) | ((gpio_val & 0x01) << 6);
      
              ret = dib0700_ctrl_wr(d, st->buf, 3);
      
              mutex_unlock(&d->usb_mutex);
              return ret;
      }
      
      static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets)
      {
              struct dib0700_state *st = d->priv;
              int ret;
      
              if (st->fw_version >= 0x10201) {
                      if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
                              err("could not acquire lock");
                              return -EINTR;
                      }
      
                      st->buf[0] = REQUEST_SET_USB_XFER_LEN;
                      st->buf[1] = (nb_ts_packets >> 8) & 0xff;
                      st->buf[2] = nb_ts_packets & 0xff;
      
                      deb_info("set the USB xfer len to %i Ts packet\n", nb_ts_packets);
      
                      ret = dib0700_ctrl_wr(d, st->buf, 3);
                      mutex_unlock(&d->usb_mutex);
              } else {
                      deb_info("this firmware does not allow to change the USB xfer len\n");
                      ret = -EIO;
              }
      
              return ret;
      }
      
      /*
       * I2C master xfer function (supported in 1.20 firmware)
       */
      static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
                                      int num)
      {
              /* The new i2c firmware messages are more reliable and in particular
                 properly support i2c read calls not preceded by a write */
      
              struct dvb_usb_device *d = i2c_get_adapdata(adap);
              struct dib0700_state *st = d->priv;
              uint8_t bus_mode = 1;  /* 0=eeprom bus, 1=frontend bus */
              uint8_t gen_mode = 0; /* 0=master i2c, 1=gpio i2c */
              uint8_t en_start = 0;
              uint8_t en_stop = 0;
              int result, i;
      
              /* Ensure nobody else hits the i2c bus while we're sending our
                 sequence of messages, (such as the remote control thread) */
              if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
                      return -EINTR;
      
              for (i = 0; i < num; i++) {
                      if (i == 0) {
                              /* First message in the transaction */
                              en_start = 1;
                      } else if (!(msg[i].flags & I2C_M_NOSTART)) {
                              /* Device supports repeated-start */
                              en_start = 1;
                      } else {
                              /* Not the first packet and device doesn't support
                                 repeated start */
                              en_start = 0;
                      }
                      if (i == (num - 1)) {
                              /* Last message in the transaction */
                              en_stop = 1;
                      }
      
                      if (msg[i].flags & I2C_M_RD) {
                              /* Read request */
                              u16 index, value;
                              uint8_t i2c_dest;
      
                              i2c_dest = (msg[i].addr << 1);
                              value = ((en_start << 7) | (en_stop << 6) |
                                       (msg[i].len & 0x3F)) << 8 | i2c_dest;
                              /* I2C ctrl + FE bus; */
                              index = ((gen_mode << 6) & 0xC0) |
                                      ((bus_mode << 4) & 0x30);
      
                              result = usb_control_msg(d->udev,
                                                       usb_rcvctrlpipe(d->udev, 0),
                                                       REQUEST_NEW_I2C_READ,
                                                       USB_TYPE_VENDOR | USB_DIR_IN,
                                                       value, index, st->buf,
                                                       msg[i].len,
                                                       USB_CTRL_GET_TIMEOUT);
                              if (result < 0) {
                                      deb_info("i2c read error (status = %d)\n", result);
                                      goto unlock;
                              }
      
                              if (msg[i].len > sizeof(st->buf)) {
                                      deb_info("buffer too small to fit %d bytes\n",
                                               msg[i].len);
                                      result = -EIO;
                                      goto unlock;
                              }
      
                              memcpy(msg[i].buf, st->buf, msg[i].len);
      
                              deb_data("<<< ");
                              debug_dump(msg[i].buf, msg[i].len, deb_data);
      
                      } else {
                              /* Write request */
                              if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
                                      err("could not acquire lock");
                                      result = -EINTR;
                                      goto unlock;
                              }
                              st->buf[0] = REQUEST_NEW_I2C_WRITE;
                              st->buf[1] = msg[i].addr << 1;
                              st->buf[2] = (en_start << 7) | (en_stop << 6) |
                                      (msg[i].len & 0x3F);
                              /* I2C ctrl + FE bus; */
                              st->buf[3] = ((gen_mode << 6) & 0xC0) |
                                       ((bus_mode << 4) & 0x30);
      
                              if (msg[i].len > sizeof(st->buf) - 4) {
                                      deb_info("i2c message to big: %d\n",
                                               msg[i].len);
                                      mutex_unlock(&d->usb_mutex);
                                      result = -EIO;
                                      goto unlock;
                              }
      
                              /* The Actual i2c payload */
                              memcpy(&st->buf[4], msg[i].buf, msg[i].len);
      
                              deb_data(">>> ");
                              debug_dump(st->buf, msg[i].len + 4, deb_data);
      
                              result = usb_control_msg(d->udev,
                                                       usb_sndctrlpipe(d->udev, 0),
                                                       REQUEST_NEW_I2C_WRITE,
                                                       USB_TYPE_VENDOR | USB_DIR_OUT,
                                                       0, 0, st->buf, msg[i].len + 4,
                                                       USB_CTRL_GET_TIMEOUT);
                              mutex_unlock(&d->usb_mutex);
                              if (result < 0) {
                                      deb_info("i2c write error (status = %d)\n", result);
                                      break;
                              }
                      }
              }
              result = i;
      
      unlock:
              mutex_unlock(&d->i2c_mutex);
              return result;
      }
      
      /*
       * I2C master xfer function (pre-1.20 firmware)
       */
      static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
                                         struct i2c_msg *msg, int num)
      {
              struct dvb_usb_device *d = i2c_get_adapdata(adap);
              struct dib0700_state *st = d->priv;
              int i, len, result;
      
              if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
                      return -EINTR;
              if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
                      err("could not acquire lock");
                      mutex_unlock(&d->i2c_mutex);
                      return -EINTR;
              }
      
              for (i = 0; i < num; i++) {
                      /* fill in the address */
                      st->buf[1] = msg[i].addr << 1;
                      /* fill the buffer */
                      if (msg[i].len > sizeof(st->buf) - 2) {
                              deb_info("i2c xfer to big: %d\n",
                                      msg[i].len);
                              result = -EIO;
                              goto unlock;
                      }
                      memcpy(&st->buf[2], msg[i].buf, msg[i].len);
      
                      /* write/read request */
                      if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
                              st->buf[0] = REQUEST_I2C_READ;
                              st->buf[1] |= 1;
      
                              /* special thing in the current firmware: when length is zero the read-failed */
                              len = dib0700_ctrl_rd(d, st->buf, msg[i].len + 2,
                                                    st->buf, msg[i + 1].len);
                              if (len <= 0) {
                                      deb_info("I2C read failed on address 0x%02x\n",
                                                      msg[i].addr);
                                      result = -EIO;
                                      goto unlock;
                              }
      
                              if (msg[i + 1].len > sizeof(st->buf)) {
                                      deb_info("i2c xfer buffer to small for %d\n",
                                              msg[i].len);
                                      result = -EIO;
                                      goto unlock;
                              }
                              memcpy(msg[i + 1].buf, st->buf, msg[i + 1].len);
      
                              msg[i+1].len = len;
      
                              i++;
                      } else {
                              st->buf[0] = REQUEST_I2C_WRITE;
                              result = dib0700_ctrl_wr(d, st->buf, msg[i].len + 2);
                              if (result < 0)
                                      goto unlock;
                      }
              }
              result = i;
      unlock:
              mutex_unlock(&d->usb_mutex);
              mutex_unlock(&d->i2c_mutex);
      
              return result;
      }
      
      static int dib0700_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
                                  int num)
      {
              struct dvb_usb_device *d = i2c_get_adapdata(adap);
              struct dib0700_state *st = d->priv;
      
              if (st->fw_use_new_i2c_api == 1) {
                      /* User running at least fw 1.20 */
                      return dib0700_i2c_xfer_new(adap, msg, num);
              } else {
                      /* Use legacy calls */
                      return dib0700_i2c_xfer_legacy(adap, msg, num);
              }
      }
      
      static u32 dib0700_i2c_func(struct i2c_adapter *adapter)
      {
              return I2C_FUNC_I2C;
      }
      
      struct i2c_algorithm dib0700_i2c_algo = {
              .master_xfer   = dib0700_i2c_xfer,
              .functionality = dib0700_i2c_func,
      };
      
      int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props,
                              struct dvb_usb_device_description **desc, int *cold)
      {
              s16 ret;
              u8 *b;
      
    1         b = kmalloc(16, GFP_KERNEL);
              if (!b)
                      return        -ENOMEM;
      
      
    1         ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
                      REQUEST_GET_VERSION, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, b, 16, USB_CTRL_GET_TIMEOUT);
      
              deb_info("FW GET_VERSION length: %d\n",ret);
      
              *cold = ret <= 0;
              deb_info("cold: %d\n", *cold);
      
              kfree(b);
    1         return 0;
      }
      
      static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll,
              u8 pll_src, u8 pll_range, u8 clock_gpio3, u16 pll_prediv,
              u16 pll_loopdiv, u16 free_div, u16 dsuScaler)
      {
              struct dib0700_state *st = d->priv;
              int ret;
      
              if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
                      err("could not acquire lock");
                      return -EINTR;
              }
      
              st->buf[0] = REQUEST_SET_CLOCK;
              st->buf[1] = (en_pll << 7) | (pll_src << 6) |
                      (pll_range << 5) | (clock_gpio3 << 4);
              st->buf[2] = (pll_prediv >> 8)  & 0xff; /* MSB */
              st->buf[3] =  pll_prediv        & 0xff; /* LSB */
              st->buf[4] = (pll_loopdiv >> 8) & 0xff; /* MSB */
              st->buf[5] =  pll_loopdiv       & 0xff; /* LSB */
              st->buf[6] = (free_div >> 8)    & 0xff; /* MSB */
              st->buf[7] =  free_div          & 0xff; /* LSB */
              st->buf[8] = (dsuScaler >> 8)   & 0xff; /* MSB */
              st->buf[9] =  dsuScaler         & 0xff; /* LSB */
      
              ret = dib0700_ctrl_wr(d, st->buf, 10);
              mutex_unlock(&d->usb_mutex);
      
              return ret;
      }
      
      int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz)
      {
              struct dib0700_state *st = d->priv;
              u16 divider;
              int ret;
      
              if (scl_kHz == 0)
                      return -EINVAL;
      
              if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
                      err("could not acquire lock");
                      return -EINTR;
              }
      
              st->buf[0] = REQUEST_SET_I2C_PARAM;
              divider = (u16) (30000 / scl_kHz);
              st->buf[1] = 0;
              st->buf[2] = (u8) (divider >> 8);
              st->buf[3] = (u8) (divider & 0xff);
              divider = (u16) (72000 / scl_kHz);
              st->buf[4] = (u8) (divider >> 8);
              st->buf[5] = (u8) (divider & 0xff);
              divider = (u16) (72000 / scl_kHz); /* clock: 72MHz */
              st->buf[6] = (u8) (divider >> 8);
              st->buf[7] = (u8) (divider & 0xff);
      
              deb_info("setting I2C speed: %04x %04x %04x (%d kHz).",
                      (st->buf[2] << 8) | (st->buf[3]), (st->buf[4] << 8) |
                      st->buf[5], (st->buf[6] << 8) | st->buf[7], scl_kHz);
      
              ret = dib0700_ctrl_wr(d, st->buf, 8);
              mutex_unlock(&d->usb_mutex);
      
              return ret;
      }
      
      
      int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3)
      {
              switch (clk_MHz) {
                      case 72: dib0700_set_clock(d, 1, 0, 1, clock_out_gp3, 2, 24, 0, 0x4c); break;
                      default: return -EINVAL;
              }
              return 0;
      }
      
      static int dib0700_jumpram(struct usb_device *udev, u32 address)
      {
              int ret = 0, actlen;
              u8 *buf;
      
              buf = kmalloc(8, GFP_KERNEL);
              if (!buf)
                      return -ENOMEM;
              buf[0] = REQUEST_JUMPRAM;
              buf[1] = 0;
              buf[2] = 0;
              buf[3] = 0;
              buf[4] = (address >> 24) & 0xff;
              buf[5] = (address >> 16) & 0xff;
              buf[6] = (address >> 8)  & 0xff;
              buf[7] =  address        & 0xff;
      
              if ((ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x01),buf,8,&actlen,1000)) < 0) {
                      deb_fw("jumpram to 0x%x failed\n",address);
                      goto out;
              }
              if (actlen != 8) {
                      deb_fw("jumpram to 0x%x failed\n",address);
                      ret = -EIO;
                      goto out;
              }
      out:
              kfree(buf);
              return ret;
      }
      
      int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw)
      {
              struct hexline hx;
              int pos = 0, ret, act_len, i, adap_num;
              u8 *buf;
              u32 fw_version;
      
              buf = kmalloc(260, GFP_KERNEL);
              if (!buf)
                      return -ENOMEM;
      
              while ((ret = dvb_usb_get_hexline(fw, &hx, &pos)) > 0) {
                      deb_fwdata("writing to address 0x%08x (buffer: 0x%02x %02x)\n",
                                      hx.addr, hx.len, hx.chk);
      
                      buf[0] = hx.len;
                      buf[1] = (hx.addr >> 8) & 0xff;
                      buf[2] =  hx.addr       & 0xff;
                      buf[3] = hx.type;
                      memcpy(&buf[4],hx.data,hx.len);
                      buf[4+hx.len] = hx.chk;
      
                      ret = usb_bulk_msg(udev,
                              usb_sndbulkpipe(udev, 0x01),
                              buf,
                              hx.len + 5,
                              &act_len,
                              1000);
      
                      if (ret < 0) {
                              err("firmware download failed at %d with %d",pos,ret);
                              goto out;
                      }
              }
      
              if (ret == 0) {
                      /* start the firmware */
                      if ((ret = dib0700_jumpram(udev, 0x70000000)) == 0) {
                              info("firmware started successfully.");
                              msleep(500);
                      }
              } else
                      ret = -EIO;
      
              /* the number of ts packet has to be at least 1 */
              if (nb_packet_buffer_size < 1)
                      nb_packet_buffer_size = 1;
      
              /* get the firmware version */
              usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
                                        REQUEST_GET_VERSION,
                                        USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
                                        buf, 16, USB_CTRL_GET_TIMEOUT);
              fw_version = (buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11];
      
              /* set the buffer size - DVB-USB is allocating URB buffers
               * only after the firwmare download was successful */
              for (i = 0; i < dib0700_device_count; i++) {
                      for (adap_num = 0; adap_num < dib0700_devices[i].num_adapters;
                                      adap_num++) {
                              if (fw_version >= 0x10201) {
                                      dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 188*nb_packet_buffer_size;
                              } else {
                                      /* for fw version older than 1.20.1,
                                       * the buffersize has to be n times 512 */
                                      dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = ((188*nb_packet_buffer_size+188/2)/512)*512;
                                      if (dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize < 512)
                                              dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 512;
                              }
                      }
              }
      out:
              kfree(buf);
              return ret;
      }
      
      int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
      {
              struct dib0700_state *st = adap->dev->priv;
              int ret;
      
              if ((onoff != 0) && (st->fw_version >= 0x10201)) {
                      /* for firmware later than 1.20.1,
                       * the USB xfer length can be set  */
                      ret = dib0700_set_usb_xfer_len(adap->dev,
                              st->nb_packet_buffer_size);
                      if (ret < 0) {
                              deb_info("can not set the USB xfer len\n");
                              return ret;
                      }
              }
      
              mutex_lock(&adap->dev->usb_mutex);
      
              st->buf[0] = REQUEST_ENABLE_VIDEO;
              /* this bit gives a kind of command,
               * rather than enabling something or not */
              st->buf[1] = (onoff << 4) | 0x00;
      
              if (st->disable_streaming_master_mode == 1)
                      st->buf[2] = 0x00;
              else
                      st->buf[2] = 0x01 << 4; /* Master mode */
      
              st->buf[3] = 0x00;
      
              deb_info("modifying (%d) streaming state for %d\n", onoff, adap->id);
      
              st->channel_state &= ~0x3;
              if ((adap->fe_adap[0].stream.props.endpoint != 2)
                              && (adap->fe_adap[0].stream.props.endpoint != 3)) {
                      deb_info("the endpoint number (%i) is not correct, use the adapter id instead", adap->fe_adap[0].stream.props.endpoint);
                      if (onoff)
                              st->channel_state |=        1 << (adap->id);
                      else
                              st->channel_state |=        1 << ~(adap->id);
              } else {
                      if (onoff)
                              st->channel_state |=        1 << (adap->fe_adap[0].stream.props.endpoint-2);
                      else
                              st->channel_state |=        1 << (3-adap->fe_adap[0].stream.props.endpoint);
              }
      
              st->buf[2] |= st->channel_state;
      
              deb_info("data for streaming: %x %x\n", st->buf[1], st->buf[2]);
      
              ret = dib0700_ctrl_wr(adap->dev, st->buf, 4);
              mutex_unlock(&adap->dev->usb_mutex);
      
              return ret;
      }
      
      int dib0700_change_protocol(struct rc_dev *rc, u64 *rc_proto)
      {
              struct dvb_usb_device *d = rc->priv;
              struct dib0700_state *st = d->priv;
              int new_proto, ret;
      
              if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
                      err("could not acquire lock");
                      return -EINTR;
              }
      
              st->buf[0] = REQUEST_SET_RC;
              st->buf[1] = 0;
              st->buf[2] = 0;
      
              /* Set the IR mode */
              if (*rc_proto & RC_PROTO_BIT_RC5) {
                      new_proto = 1;
                      *rc_proto = RC_PROTO_BIT_RC5;
              } else if (*rc_proto & RC_PROTO_BIT_NEC) {
                      new_proto = 0;
                      *rc_proto = RC_PROTO_BIT_NEC;
              } else if (*rc_proto & RC_PROTO_BIT_RC6_MCE) {
                      if (st->fw_version < 0x10200) {
                              ret = -EINVAL;
                              goto out;
                      }
                      new_proto = 2;
                      *rc_proto = RC_PROTO_BIT_RC6_MCE;
              } else {
                      ret = -EINVAL;
                      goto out;
              }
      
              st->buf[1] = new_proto;
      
              ret = dib0700_ctrl_wr(d, st->buf, 3);
              if (ret < 0) {
                      err("ir protocol setup failed");
                      goto out;
              }
      
              d->props.rc.core.protocol = *rc_proto;
      
      out:
              mutex_unlock(&d->usb_mutex);
              return ret;
      }
      
      /* This is the structure of the RC response packet starting in firmware 1.20 */
      struct dib0700_rc_response {
              u8 report_id;
              u8 data_state;
              union {
                      struct {
                              u8 system;
                              u8 not_system;
                              u8 data;
                              u8 not_data;
                      } nec;
                      struct {
                              u8 not_used;
                              u8 system;
                              u8 data;
                              u8 not_data;
                      } rc5;
              };
      };
      #define RC_MSG_SIZE_V1_20 6
      
      static void dib0700_rc_urb_completion(struct urb *purb)
      {
              struct dvb_usb_device *d = purb->context;
              struct dib0700_rc_response *poll_reply;
              enum rc_proto protocol;
              u32 keycode;
              u8 toggle;
      
              deb_info("%s()\n", __func__);
              if (d->rc_dev == NULL) {
                      /* This will occur if disable_rc_polling=1 */
                      kfree(purb->transfer_buffer);
                      usb_free_urb(purb);
                      return;
              }
      
              poll_reply = purb->transfer_buffer;
      
              if (purb->status < 0) {
                      deb_info("discontinuing polling\n");
                      kfree(purb->transfer_buffer);
                      usb_free_urb(purb);
                      return;
              }
      
              if (purb->actual_length != RC_MSG_SIZE_V1_20) {
                      deb_info("malformed rc msg size=%d\n", purb->actual_length);
                      goto resubmit;
              }
      
              deb_data("IR ID = %02X state = %02X System = %02X %02X Cmd = %02X %02X (len %d)\n",
                       poll_reply->report_id, poll_reply->data_state,
                       poll_reply->nec.system, poll_reply->nec.not_system,
                       poll_reply->nec.data, poll_reply->nec.not_data,
                       purb->actual_length);
      
              switch (d->props.rc.core.protocol) {
              case RC_PROTO_BIT_NEC:
                      toggle = 0;
      
                      /* NEC protocol sends repeat code as 0 0 0 FF */
                      if (poll_reply->nec.system     == 0x00 &&
                          poll_reply->nec.not_system == 0x00 &&
                          poll_reply->nec.data       == 0x00 &&
                          poll_reply->nec.not_data   == 0xff) {
                              poll_reply->data_state = 2;
                              rc_repeat(d->rc_dev);
                              goto resubmit;
                      }
      
                      if ((poll_reply->nec.data ^ poll_reply->nec.not_data) != 0xff) {
                              deb_data("NEC32 protocol\n");
                              keycode = RC_SCANCODE_NEC32(poll_reply->nec.system     << 24 |
                                                           poll_reply->nec.not_system << 16 |
                                                           poll_reply->nec.data       << 8  |
                                                           poll_reply->nec.not_data);
                              protocol = RC_PROTO_NEC32;
                      } else if ((poll_reply->nec.system ^ poll_reply->nec.not_system) != 0xff) {
                              deb_data("NEC extended protocol\n");
                              keycode = RC_SCANCODE_NECX(poll_reply->nec.system << 8 |
                                                          poll_reply->nec.not_system,
                                                          poll_reply->nec.data);
      
                              protocol = RC_PROTO_NECX;
                      } else {
                              deb_data("NEC normal protocol\n");
                              keycode = RC_SCANCODE_NEC(poll_reply->nec.system,
                                                         poll_reply->nec.data);
                              protocol = RC_PROTO_NEC;
                      }
      
                      break;
              default:
                      deb_data("RC5 protocol\n");
                      protocol = RC_PROTO_RC5;
                      toggle = poll_reply->report_id;
                      keycode = RC_SCANCODE_RC5(poll_reply->rc5.system, poll_reply->rc5.data);
      
                      if ((poll_reply->rc5.data ^ poll_reply->rc5.not_data) != 0xff) {
                              /* Key failed integrity check */
                              err("key failed integrity check: %02x %02x %02x %02x",
                                  poll_reply->rc5.not_used, poll_reply->rc5.system,
                                  poll_reply->rc5.data, poll_reply->rc5.not_data);
                              goto resubmit;
                      }
      
                      break;
              }
      
              rc_keydown(d->rc_dev, protocol, keycode, toggle);
      
      resubmit:
              /* Clean the buffer before we requeue */
              memset(purb->transfer_buffer, 0, RC_MSG_SIZE_V1_20);
      
              /* Requeue URB */
              usb_submit_urb(purb, GFP_ATOMIC);
      }
      
      int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf)
      {
              struct dib0700_state *st = d->priv;
              struct urb *purb;
              const struct usb_endpoint_descriptor *e;
              int ret, rc_ep = 1;
              unsigned int pipe = 0;
      
              /* Poll-based. Don't initialize bulk mode */
              if (st->fw_version < 0x10200 || !intf)
                      return 0;
      
              /* Starting in firmware 1.20, the RC info is provided on a bulk pipe */
      
              if (intf->altsetting[0].desc.bNumEndpoints < rc_ep + 1)
                      return -ENODEV;
      
              purb = usb_alloc_urb(0, GFP_KERNEL);
              if (purb == NULL)
                      return -ENOMEM;
      
              purb->transfer_buffer = kzalloc(RC_MSG_SIZE_V1_20, GFP_KERNEL);
              if (purb->transfer_buffer == NULL) {
                      err("rc kzalloc failed");
                      usb_free_urb(purb);
                      return -ENOMEM;
              }
      
              purb->status = -EINPROGRESS;
      
              /*
               * Some devices like the Hauppauge NovaTD model 52009 use an interrupt
               * endpoint, while others use a bulk one.
               */
              e = &intf->altsetting[0].endpoint[rc_ep].desc;
              if (usb_endpoint_dir_in(e)) {
                      if (usb_endpoint_xfer_bulk(e)) {
                              pipe = usb_rcvbulkpipe(d->udev, rc_ep);
                              usb_fill_bulk_urb(purb, d->udev, pipe,
                                                purb->transfer_buffer,
                                                RC_MSG_SIZE_V1_20,
                                                dib0700_rc_urb_completion, d);
      
                      } else if (usb_endpoint_xfer_int(e)) {
                              pipe = usb_rcvintpipe(d->udev, rc_ep);
                              usb_fill_int_urb(purb, d->udev, pipe,
                                                purb->transfer_buffer,
                                                RC_MSG_SIZE_V1_20,
                                                dib0700_rc_urb_completion, d, 1);
                      }
              }
      
              if (!pipe) {
                      err("There's no endpoint for remote controller");
                      kfree(purb->transfer_buffer);
                      usb_free_urb(purb);
                      return 0;
              }
      
              ret = usb_submit_urb(purb, GFP_ATOMIC);
              if (ret) {
                      err("rc submit urb failed");
                      kfree(purb->transfer_buffer);
                      usb_free_urb(purb);
              }
      
              return ret;
      }
      
      static int dib0700_probe(struct usb_interface *intf,
                      const struct usb_device_id *id)
      {
              int i;
              struct dvb_usb_device *dev;
      
    1         for (i = 0; i < dib0700_device_count; i++)
    1                 if (dvb_usb_device_init(intf, &dib0700_devices[i], THIS_MODULE,
                          &dev, adapter_nr) == 0) {
                              struct dib0700_state *st = dev->priv;
                              u32 hwversion, romversion, fw_version, fwtype;
      
                              dib0700_get_version(dev, &hwversion, &romversion,
                                      &fw_version, &fwtype);
      
                              deb_info("Firmware version: %x, %d, 0x%x, %d\n",
                                      hwversion, romversion, fw_version, fwtype);
      
                              st->fw_version = fw_version;
                              st->nb_packet_buffer_size = (u32)nb_packet_buffer_size;
      
                              /* Disable polling mode on newer firmwares */
                              if (st->fw_version >= 0x10200)
                                      dev->props.rc.core.bulk_mode = true;
                              else
                                      dev->props.rc.core.bulk_mode = false;
      
                              dib0700_rc_setup(dev, intf);
      
                              return 0;
                      }
      
    1         return -ENODEV;
      }
      
      static void dib0700_disconnect(struct usb_interface *intf)
      {
              struct dvb_usb_device *d = usb_get_intfdata(intf);
              struct dib0700_state *st = d->priv;
              struct i2c_client *client;
      
              /* remove I2C client for tuner */
              client = st->i2c_client_tuner;
              if (client) {
                      module_put(client->dev.driver->owner);
                      i2c_unregister_device(client);
              }
      
              /* remove I2C client for demodulator */
              client = st->i2c_client_demod;
              if (client) {
                      module_put(client->dev.driver->owner);
                      i2c_unregister_device(client);
              }
      
              dvb_usb_device_exit(intf);
      }
      
      
      static struct usb_driver dib0700_driver = {
              .name       = "dvb_usb_dib0700",
              .probe      = dib0700_probe,
              .disconnect = dib0700_disconnect,
              .id_table   = dib0700_usb_id_table,
      };
      
      module_usb_driver(dib0700_driver);
      
      MODULE_FIRMWARE("dvb-usb-dib0700-1.20.fw");
      MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
      MODULE_DESCRIPTION("Driver for devices based on DiBcom DiB0700 - USB bridge");
      MODULE_VERSION("1.0");
      MODULE_LICENSE("GPL");
      #include <linux/compiler.h>
      #include <linux/debugfs.h>
      #include <linux/delay.h>
      #include <linux/module.h>
      #include <linux/refcount.h>
      #include <linux/semaphore.h>
      #include <linux/sched.h>
      #include <linux/slab.h>
      #include <linux/uaccess.h>
      #include <linux/wait.h>
      
      #include <linux/usb/ch9.h>
      #include <linux/usb/ch11.h>
      #include <linux/usb/cdc.h>
      #include <linux/hid.h>
      
      #include <linux/usb/gadgetfs.h>
      #include <linux/usb/gadget.h>
      
      #include <uapi/linux/usb/fuzzer.h>
      
      #define        DRIVER_DESC "USB fuzzer"
      #define DRIVER_NAME "usb-fuzzer-gadget"
      
      MODULE_DESCRIPTION(DRIVER_DESC);
      MODULE_AUTHOR("Andrey Konovalov");
      MODULE_LICENSE("GPL");
      
      #define DEBUG 0
      
      #if DEBUG
      #define print_debug(fmt, args...) pr_err(fmt, ##args)
      #else
      #define print_debug(fmt, args...)
      #endif
      
      /*----------------------------------------------------------------------*/
      
      #define FUZZER_EVENT_QUEUE_SIZE 5
      
      struct fuzzer_event_queue {
              spinlock_t                lock;
              struct semaphore        sema;
              struct usb_fuzzer_event        *events[FUZZER_EVENT_QUEUE_SIZE];
              int                        size;
      };
      
      static void fuzzer_event_queue_init(struct fuzzer_event_queue *queue)
      {
              spin_lock_init(&queue->lock);
              sema_init(&queue->sema, 0);
              queue->size = 0;
      }
      
      static int fuzzer_event_queue_add(struct fuzzer_event_queue *queue,
              enum usb_fuzzer_event_type type, size_t length, const void *data)
      {
              unsigned long flags;
              struct usb_fuzzer_event *event;
              int i;
      
              print_debug("uf: fuzzer_event_queue_add: type = %d\n", (int)type);
      
              spin_lock_irqsave(&queue->lock, flags);
      
              event = kmalloc(sizeof(*event) + length, GFP_ATOMIC);
              if (!event) {
                      spin_unlock_irqrestore(&queue->lock, flags);
                      return -ENOMEM;
              }
              event->type = type;
              event->length = length;
              if (event->length)
                      memcpy(&event->data[0], data, length);
      
              switch (event->type) {
              case USB_FUZZER_EVENT_DISCONNECT:
              case USB_FUZZER_EVENT_CONNECT:
                      for (i = 0; i < queue->size; i++)
                              kfree(queue->events[i]);
                      queue->size = 0;
                      break;
              case USB_FUZZER_EVENT_CONTROL:
              case USB_FUZZER_EVENT_SUSPEND:
                      for (i = 0; i < queue->size; i++) {
                              if (queue->events[i]->type != event->type)
                                      continue;
                              print_debug("uf: fuzzer_event_queue_add: dropping event[%d]\n", i);
                              kfree(queue->events[i]);
                              queue->size--;
                              memmove(&queue->events[i], &queue->events[i + 1],
                                              queue->size - i);
                              break;
                      }
                      break;
              case USB_FUZZER_EVENT_RESUME:
                      break;
              default:
                      kfree(event);
                      spin_unlock_irqrestore(&queue->lock, flags);
                      return -EINVAL;
              }
      
              print_debug("uf: fuzzer_event_queue_add: adding event[%d]\n", (int)queue->size);
      
              BUG_ON(queue->size >= FUZZER_EVENT_QUEUE_SIZE);
              queue->events[queue->size] = event;
              queue->size++;
              up(&queue->sema);
      
              spin_unlock_irqrestore(&queue->lock, flags);
      
              return 0;
      }
      
      static struct usb_fuzzer_event *fuzzer_event_queue_fetch(
                                      struct fuzzer_event_queue *queue)
      {
              unsigned long flags;
              struct usb_fuzzer_event *event;
      
              if (down_interruptible(&queue->sema) != 0)
                      return NULL;
              spin_lock_irqsave(&queue->lock, flags);
              BUG_ON(queue->size == 0);
  436         event = queue->events[0];
              queue->size--;
              memmove(&queue->events[0], &queue->events[1], queue->size);
              spin_unlock_irqrestore(&queue->lock, flags);
              return event;
      }
      
      static void fuzzer_event_queue_destroy(struct fuzzer_event_queue *queue)
      {
              int i;
      
   14         for (i = 0; i < queue->size; i++) {
    4                 kfree(queue->events[i]);
              }
   14         queue->size = 0;
              return;
      }
      
      /*----------------------------------------------------------------------*/
      
      #define USB_FUZZER_MAX_ENDPOINTS 32
      
      enum ep_state {
              STATE_EP_DISABLED,
              STATE_EP_ENABLED,
      };
      
      struct fuzzer_ep {
              enum ep_state                state;
              struct usb_ep                *ep;
              struct usb_request        *req;
              bool                        busy;
              ssize_t                        status;
      };
      
      enum dev_state {
              STATE_DEV_INVALID = 0,
              STATE_DEV_OPENED,
              STATE_DEV_INITIALIZED,
              STATE_DEV_RUNNING,
              STATE_DEV_CLOSED,
              STATE_DEV_FAILED
      };
      
      struct fuzzer_dev {
              refcount_t                        count;
              spinlock_t                        lock;
      
              const char                        *udc_name;
              struct usb_gadget_driver        driver;
      
              /* Protected by lock: */
              enum dev_state                        state;
              struct usb_gadget                *gadget;
              struct usb_request                *req;
              bool                                setup_in_pending;
              bool                                setup_out_pending;
              bool                                setup_urb_queued;
              struct fuzzer_ep                eps[USB_FUZZER_MAX_ENDPOINTS];
      
              struct completion                setup_done;
      
              struct fuzzer_event_queue        queue;
      };
      
      static struct fuzzer_dev *dev_new(void)
      {
              struct fuzzer_dev *dev;
  423         dev = kzalloc(sizeof(*dev), GFP_KERNEL);
              if (!dev)
                      return NULL;
  423         refcount_set(&dev->count, 1);
              spin_lock_init(&dev->lock);
              init_completion(&dev->setup_done);
              fuzzer_event_queue_init(&dev->queue);
              return dev;
      }
      
      static inline void dev_get(struct fuzzer_dev *dev)
      {
              refcount_inc(&dev->count);
      }
      
   14 static void dev_put(struct fuzzer_dev *dev)
      {
              int i;
      
   14         if (likely(!refcount_dec_and_test(&dev->count)))
                      return;
   14         if (dev->udc_name)
   14                 kfree(dev->udc_name);
   14         if (dev->driver.udc_name)
   14                 kfree(dev->driver.udc_name);
   14         if (dev->req) {
   13                 if (dev->setup_urb_queued)
                              usb_ep_dequeue(dev->gadget->ep0, dev->req);
   13                 usb_ep_free_request(dev->gadget->ep0, dev->req);
              }
   14         fuzzer_event_queue_destroy(&dev->queue);
   14         for (i = 0; i < USB_FUZZER_MAX_ENDPOINTS; i++) {
   14                 if (dev->eps[i].state != STATE_EP_ENABLED)
                              continue;
    3                 usb_ep_disable(dev->eps[i].ep);
                      usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
                      kfree(dev->eps[i].ep->desc);
                      dev->eps[i].state = STATE_EP_DISABLED;
              }
   14         kfree(dev);
              print_debug("uf: dev_put freed the device\n");
      }
      
      /*----------------------------------------------------------------------*/
      
      static void gadget_ep0_complete(struct usb_ep *ep, struct usb_request *req)
      {
              struct fuzzer_dev *dev = req->context;
      
              print_debug("uf: gadget_ep0_complete: len: %d, status: %d\n",
                              req->length, req->status);
      
      
              complete(&dev->setup_done);
      
              print_debug("uf: gadget_ep0_complete = void\n");
      }
      
      static int gadget_bind(struct usb_gadget *gadget,
                              struct usb_gadget_driver *driver)
      {
              int ret = 0;
              struct fuzzer_dev *dev =
  420                 container_of(driver, struct fuzzer_dev, driver);
              struct usb_request *req;
              unsigned long flags;
      
              print_debug("uf: gadget_bind: %s vs %s\n", gadget->name, dev->udc_name);
      
              if (strcmp(gadget->name, dev->udc_name) != 0) {
                      ret = -ENODEV;
                      goto out;
              }
  420         set_gadget_data(gadget, dev);
              req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
              if (!req) {
                      set_gadget_data(gadget, NULL);
                      ret = -ENOMEM;
                      goto out;
              }
      
  420         spin_lock_irqsave(&dev->lock, flags);
              dev->req = req;
              dev->req->context = dev;
              dev->req->complete = gadget_ep0_complete;
              dev->gadget = gadget;
              spin_unlock_irqrestore(&dev->lock, flags);
      
              dev_get(dev);
      
      out:
              print_debug("uf: gadget_bind = %d\n", ret);
  420         return ret;
      }
      
      static void gadget_unbind(struct usb_gadget *gadget)
      {
   13         struct fuzzer_dev *dev = get_gadget_data(gadget);
              unsigned long flags;
      
              print_debug("uf: gadget_unbind\n");
      
              BUG_ON(!dev);
      
   13         spin_lock_irqsave(&dev->lock, flags);
              set_gadget_data(gadget, NULL);
              spin_unlock_irqrestore(&dev->lock, flags);
      
              dev_put(dev);
      
              print_debug("uf: gadget_unbind = void\n");
      }
      
      static int gadget_setup(struct usb_gadget *gadget,
                              const struct usb_ctrlrequest *ctrl)
      {
              int ret = 0;
              struct fuzzer_dev *dev = get_gadget_data(gadget);
              unsigned long flags;
      
              print_debug(
                      "uf: gadget_setup: bRequestType: 0x%x (%s), bRequest: 0x%x,\n"
                      "                  wValue: 0x%x, wIndex: 0x%x, wLength: %d\n",
                      ctrl->bRequestType,
                      (ctrl->bRequestType & USB_DIR_IN) ? "IN" : "OUT",
                      ctrl->bRequest, ctrl->wValue, ctrl->wIndex, ctrl->wLength);
      
              if (!dev) {
                      ret = -ENODEV;
                      goto out;
              }
      
              print_debug("uf: gadget_setup: device exists\n");
      
              spin_lock_irqsave(&dev->lock, flags);
              if (dev->state == STATE_DEV_FAILED) {
                      ret = -ENODEV;
                      goto out_unlock;
              }
              if (dev->setup_urb_queued) {
                      ret = -EBUSY;
                      goto out_unlock;
              }
              if (dev->setup_in_pending || dev->setup_out_pending) {
                      ret = -EBUSY;
                      goto out_unlock;
              }
              if ((ctrl->bRequestType & USB_DIR_IN) && ctrl->wLength) {
              print_debug("uf: gadget_setup: IN message pending\n");
                      dev->setup_in_pending = true;
              } else {
              print_debug("uf: gadget_setup: OUT message pending\n");
                      dev->setup_out_pending = true;
              }
      
              ret = fuzzer_event_queue_add(&dev->queue,
                      USB_FUZZER_EVENT_CONTROL, sizeof(*ctrl), ctrl);
              if (ret < 0) {
                      dev->state = STATE_DEV_FAILED;
                      goto out_unlock;
              }
      
      out_unlock:
              spin_unlock_irqrestore(&dev->lock, flags);
      out:
              print_debug("uf: gadget_setup = %d\n", ret);
              return ret;
      }
      
      static void gadget_disconnect(struct usb_gadget *gadget)
      {
              print_debug("uf: gadget_disconnect\n");
              return;
      }
      
      static void gadget_suspend(struct usb_gadget *gadget)
      {
              print_debug("uf: gadget_suspend\n");
              return;
      }
      
      static struct usb_gadget_driver gadget_driver = {
              .function        = DRIVER_DESC,
              .bind                = gadget_bind,
              .unbind                = gadget_unbind,
              .setup                = gadget_setup,
              .reset                = gadget_disconnect,
              .disconnect        = gadget_disconnect,
              .suspend        = gadget_suspend,
      
              .driver        = {
                      .name        = DRIVER_NAME,
              },
      
              .match_existing_only = 1,
      };
      
      /*----------------------------------------------------------------------*/
      
      static int fuzzer_open(struct inode *inode, struct file *fd)
      {
              int ret = 0;
              struct fuzzer_dev *dev;
      
              print_debug("uf: fuzzer_open\n");
      
  423         dev = dev_new();
              if (!dev) {
                      ret = -ENOMEM;
                      goto out;
              }
              dev->state = STATE_DEV_OPENED;
              fd->private_data = dev;
      
      out:
              print_debug("uf: fuzzer_open = %d\n", ret);
  423         return ret;
      }
      
      static int fuzzer_release(struct inode *inode, struct file *fd)
      {
              int ret = 0;
   14         struct fuzzer_dev *dev = fd->private_data;
              unsigned long flags;
              bool unregister = false;
      
              print_debug("uf: fuzzer_release\n");
      
              if (!dev) {
                      ret = -EBUSY;
                      goto out;
              }
      
              print_debug("uf: fuzzer_release: dev->state = %d\n", (int)dev->state);
      
   14         spin_lock_irqsave(&dev->lock, flags);
      
              // TODO: we might need to unregister with STATE_DEV_FAILED.
              // TODO: set STATE_DEV_FAILED consistently
              if (dev->state == STATE_DEV_RUNNING)
                      unregister = true;
   14         dev->state = STATE_DEV_CLOSED;
      
              if (!dev->gadget) {
    1                 spin_unlock_irqrestore(&dev->lock, flags);
                      ret = -EBUSY;
                      goto out_put;
              }
      
   13         spin_unlock_irqrestore(&dev->lock, flags);
      
              if (!unregister)
                      goto out_put;
      
              ret = usb_gadget_unregister_driver(&dev->driver);
              print_debug("uf: usb_gadget_unregister_driver: %d\n", ret);
              WARN_ON(ret != 0);
   13         dev_put(dev);
      
      out_put:
   14         dev_put(dev);
      out:
              print_debug("uf: fuzzer_release = %d\n", ret);
   14         return ret;
      }
      
      /*----------------------------------------------------------------------*/
      
      #define UDC_NAME_LENGTH_MAX 128
      
      static int fuzzer_ioctl_init(struct fuzzer_dev *dev, unsigned long value)
      {
              int ret = 0;
              struct usb_fuzzer_init arg;
              char *udc_driver_name;
              char *udc_device_name;
              unsigned long flags;
      
              print_debug("uf: fuzzer_ioctl_init\n");
      
              print_debug("uf: fuzzer_ioctl_init: getting arg\n");
  423         ret = copy_from_user(&arg, (void __user *)value, sizeof(arg));
              if (ret)
                      goto out;
              print_debug("uf: fuzzer_ioctl_init: got arg\n");
      
  423         switch (arg.speed) {
              case USB_SPEED_LOW:
              case USB_SPEED_FULL:
              case USB_SPEED_HIGH:
              case USB_SPEED_SUPER:
                      break;
              default:
  401                 arg.speed = USB_SPEED_HIGH;
              }
      
  423         udc_driver_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
              if (!udc_driver_name) {
                      ret = -ENOMEM;
                      goto out;
              }
  423         ret = strncpy_from_user(udc_driver_name, arg.driver_name,
                                              UDC_NAME_LENGTH_MAX);
              if (ret < 0) {
                      kfree(udc_driver_name);
                      goto out;
              }
              ret = 0;
              print_debug("uf: fuzzer_ioctl_init: udc_driver_name: %s\n",
                              udc_driver_name);
      
  423         udc_device_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
              if (!udc_device_name) {
                      kfree(udc_driver_name);
                      ret = -ENOMEM;
                      goto out;
              }
  423         ret = strncpy_from_user(udc_device_name, arg.device_name,
                                              UDC_NAME_LENGTH_MAX);
              if (ret < 0) {
                      kfree(udc_driver_name);
                      kfree(udc_device_name);
                      goto out;
              }
              ret = 0;
              print_debug("uf: fuzzer_ioctl_init: udc_device_name: %s\n",
                              udc_device_name);
      
  423         spin_lock_irqsave(&dev->lock, flags);
      
              if (dev->state != STATE_DEV_OPENED) {
                      kfree(udc_driver_name);
                      kfree(udc_device_name);
                      ret = -EINVAL;
  134                 goto out_unlock;
              }
      
  423         dev->udc_name = udc_driver_name;
              memcpy(&dev->driver, &gadget_driver, sizeof(gadget_driver));
              dev->driver.udc_name = udc_device_name;
              dev->driver.max_speed = arg.speed;
              dev->state = STATE_DEV_INITIALIZED;
      
      out_unlock:
  423         spin_unlock_irqrestore(&dev->lock, flags);
      out:
              print_debug("uf: fuzzer_ioctl_init = %d\n", ret);
              return ret;
      }
      
      static int fuzzer_ioctl_run(struct fuzzer_dev *dev, unsigned long value)
      {
              int ret = 0;
              unsigned long flags;
      
              print_debug("uf: fuzzer_ioctl_run\n");
      
  423         if (value != 0) {
                      ret = -EINVAL;
                      goto out;
              }
      
  423         spin_lock_irqsave(&dev->lock, flags);
              if (dev->state != STATE_DEV_INITIALIZED) {
                      ret = -EINVAL;
                      goto out_unlock;
              }
  423         spin_unlock_irqrestore(&dev->lock, flags);
      
              ret = usb_gadget_probe_driver(&dev->driver);
      
              spin_lock_irqsave(&dev->lock, flags);
              if (ret != 0) {
    4                 dev->state = STATE_DEV_FAILED;
                      goto out_unlock;
              }
  420         dev->state = STATE_DEV_RUNNING;
              dev_get(dev);
      
      out_unlock:
              spin_unlock_irqrestore(&dev->lock, flags);
      out:
              print_debug("uf: fuzzer_ioctl_run = %d\n", ret);
              return ret;
      }
      
      static int fuzzer_ioctl_event_fetch(struct fuzzer_dev *dev, unsigned long value)
      {
              int ret = 0;
              struct usb_fuzzer_event arg;
              unsigned long flags;
              struct usb_fuzzer_event *event;
              uint32_t length;
      
              print_debug("uf: fuzzer_ioctl_event_fetch\n");
      
  437         ret = copy_from_user(&arg, (void __user *)value, sizeof(arg));
    1         if (ret)
                      goto out;
      
  437         spin_lock_irqsave(&dev->lock, flags);
              if (dev->state != STATE_DEV_RUNNING) {
                      ret = -EINVAL;
    1                 spin_unlock_irqrestore(&dev->lock, flags);
                      goto out;
              }
  436         if (!dev->gadget) {
                      ret = -EBUSY;
                      spin_unlock_irqrestore(&dev->lock, flags);
                      goto out;
              }
  436         spin_unlock_irqrestore(&dev->lock, flags);
      
  436         event = fuzzer_event_queue_fetch(&dev->queue);
              if (!event) {
                      ret = -EINTR;
                      goto out;
              }
      
  436         length = min(arg.length, event->length);
              ret = copy_to_user((void __user *)value, event,
                                      sizeof(*event) + length);
              if (ret)
                      goto out;
      
              print_debug("uf: fuzzer_ioctl_event_fetch: length: %u\n", length);
      
      out:
              print_debug("uf: fuzzer_ioctl_event_fetch = %d\n", ret);
              return ret;
      }
      
      static int fuzzer_ioctl_ep0_write(struct fuzzer_dev *dev, unsigned long value)
      {
              int ret = 0;
              struct usb_fuzzer_ep_io io;
              void *data;
              unsigned long flags;
      
              print_debug("uf: fuzzer_ioctl_ep0_write\n");
      
  425         ret = copy_from_user(&io, (void __user *)value, sizeof(io));
              if (ret)
                      goto out;
  425         if (io.ep != 0) {
                      ret = -EINVAL;
                      goto out;
              }
  425         if (!usb_fuzzer_io_flags_valid(io.flags)) {
                      ret = -EINVAL;
                      goto out;
              }
  425         if (io.length > PAGE_SIZE) {
                      ret = -EINVAL;
                      goto out;
              }
  425         data = memdup_user((void __user *)(value + sizeof(io)), io.length);
              if (IS_ERR(data)) {
                      ret = PTR_ERR(data);
                      goto out;
              }
      
      #if DEBUG
              print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET,
                              16, 1, data, io.length, false);
      #endif
      
  425         spin_lock_irqsave(&dev->lock, flags);
              if (dev->state != STATE_DEV_RUNNING) {
                      ret = -EINVAL;
                      goto out_unlock;
              }
  425         if (!dev->gadget) {
                      ret = -EBUSY;
                      goto out_unlock;
              }
  425         if (!dev->setup_in_pending) {
                      ret = -EBUSY;
                      goto out_unlock;
              }
  425         if (dev->setup_urb_queued) {
                      ret = -EBUSY;
                      goto out_unlock;
              }
  425         BUG_ON(dev->setup_out_pending);
  425         dev->req->buf = data;
              dev->req->length = io.length;
              dev->req->zero = usb_fuzzer_io_flags_zero(io.flags);
              ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_ATOMIC);
              if (ret != 0) {
                      dev->state = STATE_DEV_FAILED;
                      goto out_unlock;
              }
  425         dev->setup_urb_queued = true;
              spin_unlock_irqrestore(&dev->lock, flags);
      
              print_debug("uf: fuzzer_ioctl_ep0_write: urb queued, length: %u\n",
                              io.length);
      
              ret = wait_for_completion_interruptible(&dev->setup_done);
              if (ret != 0) {
                      print_debug("uf: fuzzer_ioctl_ep0_write: urb interrupted\n");
      
                      spin_lock_irqsave(&dev->lock, flags);
                      usb_ep_dequeue(dev->gadget->ep0, dev->req);
                      spin_unlock_irqrestore(&dev->lock, flags);
      
                      wait_for_completion(&dev->setup_done);
      
                      spin_lock_irqsave(&dev->lock, flags);
      
                      goto out_flags;
              }
      
              print_debug("uf: fuzzer_ioctl_ep0_write: urb completed\n");
      
  425         spin_lock_irqsave(&dev->lock, flags);
              ret = dev->req->status;
      
      out_flags:
  425         dev->setup_in_pending = false;
              dev->setup_urb_queued = false;
      out_unlock:
  425         spin_unlock_irqrestore(&dev->lock, flags);
              kfree(data);
      out:
              print_debug("uf: fuzzer_ioctl_ep0_write = %d\n", ret);
              return ret;
      }
      
      static int fuzzer_ioctl_ep0_read(struct fuzzer_dev *dev, unsigned long value)
      {
              int ret = 0;
              struct usb_fuzzer_ep_io io;
              void *data;
              unsigned length;
              unsigned long flags;
      
              print_debug("uf: fuzzer_ioctl_ep0_read\n");
      
  427         ret = copy_from_user(&io, (void __user *)value, sizeof(io));
              if (ret)
                      goto out;
  427         if (io.ep != 0) {
                      ret = -EINVAL;
                      goto out;
              }
  427         if (!usb_fuzzer_io_flags_valid(io.flags)) {
                      ret = -EINVAL;
                      goto out;
              }
  427         if (io.length > PAGE_SIZE) {
                      ret = -EINVAL;
                      goto out;
              }
  427         data = kmalloc(io.length, GFP_KERNEL);
              if (!data) {
                      ret = -ENOMEM;
                      goto out;
              }
      
  427         spin_lock_irqsave(&dev->lock, flags);
              if (dev->state != STATE_DEV_RUNNING) {
                      ret = -EINVAL;
                      goto out_unlock;
              }
  426         if (!dev->gadget) {
                      ret = -EBUSY;
                      goto out_unlock;
              }
  426         if (!dev->setup_out_pending) {
                      ret = -EBUSY;
                      goto out_unlock;
              }
  426         if (dev->setup_urb_queued) {
                      ret = -EBUSY;
                      goto out_unlock;
              }
  426         BUG_ON(dev->setup_in_pending);
  426         dev->req->buf = data;
              dev->req->length = io.length;
              dev->req->zero = usb_fuzzer_io_flags_zero(io.flags);
              ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_ATOMIC);
              if (ret != 0) {
                      dev->state = STATE_DEV_FAILED;
                      goto out_unlock;
              }
  426         dev->setup_urb_queued = true;
              spin_unlock_irqrestore(&dev->lock, flags);
      
              print_debug("uf: fuzzer_ioctl_ep0_read: urb queued, length: %u\n",
                              io.length);
      
              ret = wait_for_completion_interruptible(&dev->setup_done);
              if (ret != 0) {
                      print_debug("uf: fuzzer_ioctl_ep0_read: urb interrupted\n");
      
                      spin_lock_irqsave(&dev->lock, flags);
                      usb_ep_dequeue(dev->gadget->ep0, dev->req);
                      spin_unlock_irqrestore(&dev->lock, flags);
      
                      wait_for_completion(&dev->setup_done);
      
                      spin_lock_irqsave(&dev->lock, flags);
      
                      goto out_flags;
              }
      
  426         spin_lock_irqsave(&dev->lock, flags);
              length = min(io.length, dev->req->actual);
              spin_unlock_irqrestore(&dev->lock, flags);
      
              print_debug("uf: fuzzer_ioctl_ep0_read: urb completed, length: %u\n",
                              length);
      #if DEBUG
              print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET,
                              16, 1, data, length, false);
      #endif
      
              ret = copy_to_user((void __user *)(value + sizeof(io)), data, length);
              if (ret) {
                      spin_lock_irqsave(&dev->lock, flags);
                      goto out_flags;
              }
      
  426         spin_lock_irqsave(&dev->lock, flags);
              ret = dev->req->status;
      
      out_flags:
  426         dev->setup_urb_queued = false;
              dev->setup_out_pending = false;
      out_unlock:
  427         spin_unlock_irqrestore(&dev->lock, flags);
              kfree(data);
      out:
              print_debug("uf: fuzzer_ioctl_ep0_read = %d\n", ret);
              return ret;
      }
      
      static int check_ep_caps(struct usb_ep *ep, struct usb_endpoint_descriptor *desc)
      {
              switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
              case USB_ENDPOINT_XFER_ISOC:
    4                 if (!ep->caps.type_iso)
                              return 0;
                      break;
              case USB_ENDPOINT_XFER_BULK:
   77                 if (!ep->caps.type_bulk)
                              return 0;
                      break;
              case USB_ENDPOINT_XFER_INT:
  146                 if (!ep->caps.type_int)
                              return 0;
                      break;
              default:
                      return -EINVAL;
              }
      
              switch (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
              case USB_DIR_IN:
  202                 if (!ep->caps.dir_in)
                              return 0;
                      break;
              case USB_DIR_OUT:
   85                 if (!ep->caps.dir_out)
                              return 0;
                      break;
              default:
                      return -EINVAL;
              }
      
              return 1;
      }
      
      static int fuzzer_ioctl_ep_enable(struct fuzzer_dev *dev, unsigned long value)
      {
              int ret = 0, i;
              unsigned long flags;
              struct usb_endpoint_descriptor *desc;
              struct usb_ep *ep = NULL;
      
              print_debug("uf: fuzzer_ioctl_ep_enable\n");
      
  262         desc = memdup_user((void __user *)value, sizeof(*desc));
              if (IS_ERR(desc)) {
                      ret = PTR_ERR(desc);
                      goto out;
              }
      
  262         spin_lock_irqsave(&dev->lock, flags);
      
              print_debug("uf: fuzzer_ioctl_ep_enable: dev->state = %d\n",
                              dev->state);
      
              if (dev->state != STATE_DEV_RUNNING) {
                      ret = -EINVAL;
                      goto out_unlock;
              }
  262         if (!dev->gadget) {
                      ret = -EBUSY;
                      goto out_unlock;
              }
      
   26         for (i = 0; i < USB_FUZZER_MAX_ENDPOINTS; i++) {
  262                 if (dev->eps[i].state == STATE_EP_ENABLED)
                              continue;
                      break;
              }
              if (i == USB_FUZZER_MAX_ENDPOINTS) {
                      ret = -EBUSY;
                      goto out_unlock;
              }
      
  262         gadget_for_each_ep(ep, dev->gadget) {
  262                 if (ep->enabled)
                              continue;
  262                 if (!check_ep_caps(ep, desc))
                              continue;
  260                 ep->desc = desc;
                      ret = usb_ep_enable(ep);
                      if (ret < 0)
                              goto out_unlock;
  135                 dev->eps[i].req = usb_ep_alloc_request(ep, GFP_ATOMIC);
                      if (!dev->eps[i].req) {
                              ret = -ENOMEM;
                              goto out_unlock;
                      }
  135                 dev->eps[i].ep = ep;
                      dev->eps[i].state = STATE_EP_ENABLED;
                      ep->driver_data = &dev->eps[i];
                      ret = i;
                      goto out_unlock;
              }
      
    4         kfree(desc);
              ret = -EBUSY;
      
      out_unlock:
  262         spin_unlock_irqrestore(&dev->lock, flags);
      out:
              print_debug("uf: fuzzer_ioctl_ep_enable = %d\n", ret);
              return ret;
      }
      
      static void gadget_ep_io_complete(struct usb_ep *ep, struct usb_request *req)
      {
    3         struct fuzzer_ep *fep = (struct fuzzer_ep *)ep->driver_data;
      
              if (req->status)
    3                 fep->status = req->status;
              else
    3                 fep->status = req->actual;
              complete((struct completion *)req->context);
      }
      
      static int fuzzer_ioctl_ep_write(struct fuzzer_dev *dev, unsigned long value)
      {
              int ret = 0;
              unsigned long flags;
              struct usb_fuzzer_ep_io io;
              char *data;
    8         DECLARE_COMPLETION_ONSTACK(done);
      
              print_debug("uf: fuzzer_ioctl_ep_write\n");
      
              ret = copy_from_user(&io, (void __user *)value, sizeof(io));
              if (ret)
                      goto out;
    8         if (io.ep >= USB_FUZZER_MAX_ENDPOINTS) {
                      ret = -EINVAL;
                      goto out;
              }