]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
usb:class:nv-usb: driver to measure usb throughput
authorPreetham Chandru R <pchandru@nvidia.com>
Mon, 22 Oct 2012 09:55:45 +0000 (15:25 +0530)
committerDan Willemsen <dwillemsen@nvidia.com>
Sat, 14 Sep 2013 19:42:46 +0000 (12:42 -0700)
a class driver to measure usb throughput

Change-Id: Ibadfb7f5722e6969c93064fcf4ca79139fd08728
Signed-off-by: Preetham Chandru R <pchandru@nvidia.com>
Reviewed-on: http://git-master/r/138994
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>
drivers/usb/Makefile
drivers/usb/class/Kconfig
drivers/usb/class/Makefile
drivers/usb/class/nv-usb.c [new file with mode: 0644]
drivers/usb/class/nv-usb.h [new file with mode: 0644]

index c41feba8d5c0737c926aa6a576f50b0c290a626a..43ac96d19154f79d334d0a40a470d89d9d2dd186 100644 (file)
@@ -34,6 +34,7 @@ obj-$(CONFIG_USB_ACM)         += class/
 obj-$(CONFIG_USB_PRINTER)      += class/
 obj-$(CONFIG_USB_WDM)          += class/
 obj-$(CONFIG_USB_TMC)          += class/
+obj-$(CONFIG_USB_KPI)          += class/
 
 obj-$(CONFIG_USB_STORAGE)      += storage/
 obj-$(CONFIG_USB)              += storage/
index bb8b73682a70ccf28dc7669368c892a187594007..84e424d564a21c4440038c9fdf26828ef7a9bec2 100644 (file)
@@ -46,3 +46,13 @@ config USB_TMC
 
          To compile this driver as a module, choose M here: the
          module will be called usbtmc.
+
+config USB_KPI
+       tristate "USB KPI Measurement"
+       depends on USB
+       help
+         Say Y here if you want to measure the usb write and read
+         throughput.
+
+         To compile this driver as a module, choose M here: the
+         module will be called nv-usb.
index 32e85277b5cf2fd042344a5e442a23601b65a96c..49d0913a241b99af84a1702061894748959b3416 100644 (file)
@@ -7,3 +7,4 @@ obj-$(CONFIG_USB_ACM)           += cdc-acm.o
 obj-$(CONFIG_USB_PRINTER)      += usblp.o
 obj-$(CONFIG_USB_WDM)          += cdc-wdm.o
 obj-$(CONFIG_USB_TMC)          += usbtmc.o
+obj-$(CONFIG_USB_KPI)          += nv-usb.o
diff --git a/drivers/usb/class/nv-usb.c b/drivers/usb/class/nv-usb.c
new file mode 100644 (file)
index 0000000..96b64bb
--- /dev/null
@@ -0,0 +1,564 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/time.h>
+
+#include "nv-usb.h"
+
+#ifdef DEBUG
+#define INFO(stuff...) pr_info("nv-usb: " stuff)
+#else
+#define INFO(stuff...) do {} while (0)
+#endif
+
+
+#define NV_USB_BULK_VENDOR_ID 0x0955
+#define NV_USB_BULK_PRODUCT_ID 0xffff
+
+/* table of devices that work with this driver */
+static const struct usb_device_id nv_usb_table[] = {
+       { USB_DEVICE(NV_USB_BULK_VENDOR_ID, NV_USB_BULK_PRODUCT_ID) },
+       {}
+};
+
+MODULE_DEVICE_TABLE(usb, nv_usb_table);
+
+/* Need to check if the below minor id is ok */
+#define NV_USB_BULK_MINOR_BASE 192
+
+static struct usb_driver nv_usb_driver;
+
+static void nv_usb_delete(struct kref *kref)
+{
+       struct nv_usb *dev = to_nv_usb_dev(kref);
+
+       usb_put_dev(dev->udev);
+}
+
+static int nv_usb_open(struct inode *inode, struct file *file)
+{
+       struct nv_usb *dev;
+       struct usb_interface *interface;
+       int subminor;
+
+       subminor = iminor(inode);
+
+       interface = usb_find_interface(&nv_usb_driver, subminor);
+       if (interface == NULL) {
+               INFO("%s(%d) failed to get interface\n", __func__, __LINE__);
+               return -ENODEV;
+       }
+
+       dev = usb_get_intfdata(interface);
+       if (dev == NULL) {
+               INFO("%s(%d) failed to get interface\n", __func__, __LINE__);
+               return -ENODEV;
+       }
+
+       kref_get(&dev->kref);
+
+       mutex_lock(&dev->mutex);
+       file->private_data = dev;
+       mutex_unlock(&dev->mutex);
+
+       return 0;
+}
+
+static int nv_usb_release(struct inode *inode, struct file *file)
+{
+       struct nv_usb *dev;
+
+       dev = file->private_data;
+       if (dev == NULL) {
+               INFO("%s(%d) failed to get interface\n", __func__, __LINE__);
+               return -ENODEV;
+       }
+
+       kref_put(&dev->kref, nv_usb_delete);
+       return 0;
+}
+
+static int nv_usb_flush(struct file *file, fl_owner_t id)
+{
+       return 0;
+}
+
+static int nv_usb_transfer(struct bulk_data *data);
+
+static ssize_t nv_usb_read(struct file *file, char *buffer, size_t count,
+               loff_t *ppos)
+{
+       return 0;
+}
+
+unsigned long
+nv_usb_calc_time(struct timeval g_sttime, struct timeval g_entime)
+{
+
+       unsigned int num_sec = g_entime.tv_sec - g_sttime.tv_sec;
+
+       if (num_sec)
+               return (g_entime.tv_usec +
+                       (1000000 - g_sttime.tv_usec) +
+                       ((num_sec - 1) * 1000000));
+       else
+               return g_entime.tv_usec - g_sttime.tv_usec;
+
+}
+
+static void nv_usb_callback(struct urb *urb)
+{
+       struct completion *urb_done_ptr = urb->context;
+
+       complete(urb_done_ptr);
+}
+
+static int nv_usb_transfer(struct bulk_data *data)
+{
+       struct nv_usb *dev = data->dev;
+       struct nvusb_cb_wrap *bcb = NULL;
+       struct nvusb_cs_wrap *bcs = NULL;
+       struct urb *urb = NULL;
+       unsigned int transfer_length = data->length;
+       struct completion urb_done;
+       int retval = 0;
+       unsigned int cbwlen = US_BULK_CB_WRAP_LEN;
+       unsigned int cswlen = US_BULK_CS_WRAP_LEN;
+       char *buf = NULL;
+       struct timeval g_sttime, g_entime;
+       unsigned long data_transfer_time = 0;
+
+       init_completion(&urb_done);
+
+       mutex_lock(&dev->mutex);
+
+       urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!urb) {
+               INFO("%s(%d) failed to alloc urb\n", __func__, __LINE__);
+               return -ENOMEM;
+       }
+
+       bcb = usb_alloc_coherent(dev->udev, cbwlen, GFP_KERNEL,
+                       &urb->transfer_dma);
+
+       if (!bcb) {
+               INFO("%s(%d) failed to alloc bcb\n", __func__, __LINE__);
+               retval = -ENOMEM;
+               goto out;
+       }
+
+       bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
+       bcb->DataTransferLength = cpu_to_le32(transfer_length);
+       /*0 --> write 1 --> read*/
+       bcb->Flags = data->data_direction ? 1 << 7 : 0;
+       bcb->Tag = ++dev->tag;
+       bcb->Length = data->sub_cmd_length;
+
+       memset(bcb->CDB, 0, sizeof(bcb->CDB));
+       memcpy(bcb->CDB, data->sub_cmd, bcb->Length);
+
+       if (!dev->interface) {
+               INFO("%s(%d) interface disconnected\n", __func__, __LINE__);
+               usb_free_coherent(dev->udev, cbwlen, bcb, urb->transfer_dma);
+               retval = -ENODEV;
+               goto out;
+       }
+
+       INFO("cbwlen = %d\n", cbwlen);
+       usb_fill_bulk_urb(urb, dev->udev,
+               usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
+                bcb, cbwlen, nv_usb_callback, dev);
+
+       urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+       urb->context = &urb_done;
+       retval = usb_submit_urb(urb, GFP_KERNEL);
+
+       if (retval) {
+               usb_free_coherent(dev->udev, cbwlen, bcb, urb->transfer_dma);
+               INFO("%s(%d) urb submit failed\n", __func__, __LINE__);
+               goto out;
+       }
+
+       wait_for_completion_interruptible_timeout(
+               &urb_done, 20000);
+
+
+       usb_free_coherent(dev->udev, cbwlen, bcb, urb->transfer_dma);
+       INFO("%s(%d)\n", __func__, __LINE__);
+
+
+       if (transfer_length) {
+               INFO("%s(%d) Transfering Data = %d\n",
+                       __func__, __LINE__, transfer_length);
+               buf = usb_alloc_coherent(dev->udev, transfer_length, GFP_KERNEL,
+                       &urb->transfer_dma);
+
+               if (!buf) {
+                       INFO("%s(%d)failed to alloc buffer for data transfer\n",
+                       __func__, __LINE__);
+                       retval = -ENOMEM;
+                       goto out;
+               }
+
+               if (!data->data_direction) {
+                       memset(buf, data->write_char, transfer_length);
+                       usb_fill_bulk_urb(urb, dev->udev,
+                       usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
+                       buf, transfer_length, nv_usb_callback, dev);
+               } else {
+                       memset(buf, 0, transfer_length);
+                       usb_fill_bulk_urb(urb, dev->udev,
+                       usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
+                       buf, transfer_length, nv_usb_callback, dev);
+               }
+
+               urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+               urb->context = &urb_done;
+
+               do_gettimeofday(&g_sttime);
+               retval = usb_submit_urb(urb, GFP_KERNEL);
+               if (retval) {
+                       usb_free_coherent(dev->udev, transfer_length, buf,
+                               urb->transfer_dma);
+                       INFO("%s(%d) urb submit failed\n", __func__, __LINE__);
+                       goto out;
+               }
+
+               wait_for_completion_interruptible_timeout(
+                       &urb_done, 20000);
+               do_gettimeofday(&g_entime);
+               if (data->data_direction)
+                       memcpy(data->buf, buf , transfer_length);
+
+               usb_free_coherent(dev->udev,
+                       transfer_length, buf, urb->transfer_dma);
+               data_transfer_time = nv_usb_calc_time(g_sttime, g_entime);
+       }
+
+       bcs = usb_alloc_coherent(dev->udev, cswlen, GFP_KERNEL,
+               &urb->transfer_dma);
+       if (!bcs) {
+               INFO("%s(%d) failed to alloc bcs\n", __func__, __LINE__);
+               retval = -ENOMEM;
+               goto out;
+       }
+       memset(bcs, 0, cswlen);
+       usb_fill_bulk_urb(urb, dev->udev,
+               usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
+               bcs, cswlen, nv_usb_callback, dev);
+
+       urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+       urb->context = &urb_done;
+       retval = usb_submit_urb(urb, GFP_KERNEL);
+       if (retval) {
+               usb_free_coherent(dev->udev, cswlen, bcs, urb->transfer_dma);
+               INFO("%s(%d) urb submit failed\n", __func__, __LINE__);
+               goto out;
+       }
+
+       wait_for_completion_interruptible_timeout(
+               &urb_done, 20000);
+
+       INFO(
+       "%s(%d) dCSWSignature = %x dCSWTag = %x Dataresidur = %d status = %d\n",
+                               __func__, __LINE__, bcs->Signature,
+                               bcs->Tag, bcs->Residue, bcs->Status);
+
+       if (bcs->Status)
+               data_transfer_time = 0;
+
+       INFO("time taken is  %lu\n", data_transfer_time);
+
+       data->data_transfer_time = data_transfer_time;
+       data->g_data_transfer_time = bcs->Residue;
+
+       usb_free_coherent(dev->udev, cswlen, bcs, urb->transfer_dma);
+       usb_free_urb(urb);
+       mutex_unlock(&dev->mutex);
+       return 0;
+out:
+       usb_free_urb(urb);
+       mutex_unlock(&dev->mutex);
+       return retval;
+}
+
+static ssize_t nv_usb_write(struct file *file, const char *user_buffer,
+               size_t count, loff_t *ppos)
+{
+       return count;
+}
+
+static long nv_usb_ioctl(struct file *file, unsigned int cmd_in,
+               unsigned long arg, struct nv_usb *dev)
+{
+       void __user *p = (void __user *)arg;
+       struct bulk_data *data = NULL;
+       struct user_bulk_data *user_data = NULL;
+       int ret = 0;
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data) {
+               INFO("%s(%d) No Memory\n", __func__, __LINE__);
+               return -ENOMEM;
+       }
+
+       user_data = kzalloc(sizeof(*user_data), GFP_KERNEL);
+       if (!user_data) {
+               INFO("%s(%d) No Memory\n", __func__, __LINE__);
+               kfree(data);
+               return -ENOMEM;
+       }
+
+       if (__copy_from_user(user_data, p, sizeof(*user_data))) {
+               INFO("%s(%d) copy from user failed\n", __func__, __LINE__);
+               kfree(user_data);
+               kfree(data);
+               return -EFAULT;
+       }
+
+       data->dev = file->private_data;
+       data->sub_cmd_length = user_data->sub_cmd_length;
+
+       data->sub_cmd = kzalloc(data->sub_cmd_length, GFP_KERNEL);
+       if (!data->sub_cmd) {
+               INFO("%s(%d) No Memory\n", __func__, __LINE__);
+               kfree(user_data);
+               kfree(data);
+               return -ENOMEM;
+       }
+       if (__copy_from_user(data->sub_cmd,
+               user_data->sub_cmd, data->sub_cmd_length)) {
+               INFO("%s(%d) copy from user failed\n", __func__, __LINE__);
+               kfree(data->sub_cmd);
+               kfree(user_data);
+               kfree(data);
+               return -EFAULT;
+       }
+
+       data->length = user_data->length;
+       data->buf = kzalloc(data->length, GFP_KERNEL);
+       if (!data->buf) {
+               INFO("%s(%d) No Memory\n", __func__, __LINE__);
+               kfree(data->sub_cmd);
+               kfree(user_data);
+               kfree(data);
+               return -ENOMEM;
+       }
+
+       data->write_char = user_data->write_char;
+
+       switch (cmd_in) {
+       case NVUSB_BULK_WRITE:
+               if (__copy_from_user(data->buf, user_data->buf, data->length)) {
+                       INFO("%s(%d) copy from user failed\n",
+                                               __func__, __LINE__);
+                       ret = -EFAULT;
+                       goto error;
+               }
+               data->data_direction = 0;
+               ret = nv_usb_transfer(data);
+               if (ret) {
+                       INFO("%s(%d) nv_usb_transfer failed\n",
+                                               __func__, __LINE__);
+                       goto error;
+               }
+               break;
+       case NVUSB_BULK_READ:
+               data->data_direction = 1;
+               INFO("%s(%d)\n", __func__, __LINE__);
+               ret = nv_usb_transfer(data);
+               if (ret) {
+                       INFO("%s(%d) nv_usb_transfer failed\n",
+                                               __func__, __LINE__);
+                       goto error;
+               }
+
+               if (__copy_to_user(user_data->buf, data->buf, data->length)) {
+                       INFO("%s(%d) copy to user failed\n",
+                                               __func__, __LINE__);
+                       ret = -EFAULT;
+                       goto error;
+               }
+               break;
+       default:
+               INFO("%s(%d) Invalid ioctl command %x\n",
+                                       __func__, __LINE__, cmd_in);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       user_data->data_transfer_time = data->data_transfer_time;
+       user_data->g_data_transfer_time = data->g_data_transfer_time;
+
+       if (copy_to_user(p, user_data, sizeof(*user_data))) {
+               INFO("%s(%d) copy to user failed\n", __func__, __LINE__);
+               ret = -EFAULT;
+       }
+
+error:
+       kfree(data->buf);
+       kfree(data->sub_cmd);
+       kfree(user_data);
+       kfree(data);
+
+       return ret;
+
+}
+static long nv_usb_unlocked_ioctl(struct file *file, unsigned int cmd_in,
+               unsigned long arg)
+{
+       int ret;
+       struct nv_usb *dev;
+
+       dev = file->private_data;
+
+       ret = nv_usb_ioctl(file, cmd_in, arg, dev);
+
+       return ret;
+}
+
+static const struct file_operations nv_usb_fops = {
+       .owner          = THIS_MODULE,
+       .read           = nv_usb_read,
+       .write          = nv_usb_write,
+       .open           = nv_usb_open,
+       .unlocked_ioctl = nv_usb_unlocked_ioctl,
+       .release        = nv_usb_release,
+       .flush          = nv_usb_flush,
+       .llseek         = noop_llseek,
+};
+
+static struct usb_class_driver nv_usb_class = {
+       .name           = "nvbulk%d",
+       .fops           = &nv_usb_fops,
+       .minor_base     = NV_USB_BULK_MINOR_BASE,
+};
+
+static int nv_usb_probe(struct usb_interface *interface,
+               const struct usb_device_id *id)
+{
+       struct usb_host_interface *iface_desc;
+       struct usb_endpoint_descriptor *endpoint;
+       struct nv_usb *dev;
+       struct usb_device *udev;
+       int retval = 0;
+       int i;
+
+       udev = usb_get_dev(interface_to_usbdev(interface));
+       /* allocate memory for our device state and initialize it */
+       dev = devm_kzalloc(&udev->dev, sizeof(*dev), GFP_KERNEL);
+       if (!dev) {
+               INFO("%s(%d) Out of Memory\n", __func__, __LINE__);
+               return -ENOMEM;
+       }
+
+       kref_init(&dev->kref);
+       init_usb_anchor(&dev->submitted);
+       mutex_init(&dev->mutex);
+
+       dev->udev = udev;
+       dev->interface = interface;
+
+       /*Set up the Endpoint Information */
+       /* use only the first bulk-in and bulk-out endpoints*/
+       iface_desc = interface->cur_altsetting;
+       for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+               endpoint = &iface_desc->endpoint[i].desc;
+
+               if (usb_endpoint_is_bulk_in(endpoint)) {
+                       /*We found a bulk_in endpoint */
+                       dev->bulk_in_size = usb_endpoint_maxp(endpoint);
+                       dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+               }
+
+               if (usb_endpoint_is_bulk_out(endpoint)) {
+                       /*We found a buld_out endpoint */
+                       dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
+               }
+       }
+
+       if (dev->bulk_in_endpointAddr == 0
+               || dev->bulk_out_endpointAddr == 0) {
+               INFO("%s(%d) Count not find bulk point infomations\n",
+                                                       __func__, __LINE__);
+               goto error;
+       }
+
+       /* save our data pointer in this interface device */
+       usb_set_intfdata(interface, dev);
+
+       /* we can register the device now, as it is ready */
+       retval = usb_register_dev(interface, &nv_usb_class);
+
+       if (retval) {
+               INFO("%s(%d) usb_register_dev failed\n", __func__, __LINE__);
+               usb_set_intfdata(interface, NULL);
+               goto error;
+       }
+
+       INFO("%s(%d) device now attached to USB\n", __func__, __LINE__);
+       return 0;
+error:
+       if (dev) {
+               kref_put(&dev->kref, nv_usb_delete);
+               retval = -EFAULT;
+       }
+
+       return retval;
+
+}
+
+static void nv_usb_disconnect(struct usb_interface *interface)
+{
+       struct nv_usb *dev;
+       int minor = interface->minor;
+
+       dev = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+
+       usb_deregister_dev(interface, &nv_usb_class);
+
+       mutex_lock(&dev->mutex);
+       dev->interface = NULL;
+       mutex_unlock(&dev->mutex);
+
+       usb_kill_anchored_urbs(&dev->submitted);
+
+       kref_put(&dev->kref, nv_usb_delete);
+
+       INFO("%s(%d) disconnected minor = %d\n", __func__, __LINE__, minor);
+
+}
+
+static struct usb_driver nv_usb_driver = {
+       .name           = "nv-usb",
+       .probe          = nv_usb_probe,
+       .disconnect     = nv_usb_disconnect,
+       .id_table       = nv_usb_table,
+};
+
+module_usb_driver(nv_usb_driver);
+MODULE_AUTHOR("NVIDIA");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/class/nv-usb.h b/drivers/usb/class/nv-usb.h
new file mode 100644 (file)
index 0000000..83922cc
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LINUX_USB_NV_USB_BULK_H
+#define  __LINUX_USB_NV_USB_BULK_H
+
+
+#define NVUSB_BULK_WRITE       0X2000
+#define NVUSB_BULK_READ        0X2001
+
+/* command block wrapper */
+struct nvusb_cb_wrap {
+       __le32  Signature;
+       __u32   Tag;
+       __le32  DataTransferLength;
+       __u8    Flags;
+       __u8    Length;
+       __u8    CDB[16];
+};
+
+#define US_BULK_CB_WRAP_LEN    31
+#define US_BULK_CB_SIGN                0x43425355
+#define US_BULK_FLAG_IN                (1 << 7)
+#define US_BULK_FLAG_OUT       0
+
+/* command status wrapper */
+struct nvusb_cs_wrap {
+       __le32  Signature;
+       __u32   Tag;
+       __le32  Residue;
+       __u8    Status;
+};
+
+#define US_BULK_CS_WRAP_LEN    13
+#define US_BULK_CS_SIGN                0x53425355
+#define US_BULK_STAT_OK                0
+#define US_BULK_STAT_FAIL      1
+#define US_BULK_STAT_BAD_DATA  2
+
+struct bulk_data {
+       struct nv_usb *dev;
+       __u8 data_direction;
+       __u8 sub_cmd_length;
+       __u8 *sub_cmd;
+       __u32 length;
+       __u8 *buf;
+       __u8 write_char;
+       __u32 data_transfer_time;
+       __u32  g_data_transfer_time;
+};
+
+struct user_bulk_data {
+       __u8 sub_cmd_length;
+       __u8 __user *sub_cmd;
+       __u32 length;
+       __u8 __user *buf;
+       __u8 write_char;
+       __u32  data_transfer_time;
+       __u32  g_data_transfer_time;
+
+};
+
+
+/* Structure to hold all of our device specific stuff */
+struct nv_usb {
+       /* the usb device for this device */
+       struct usb_device       *udev;
+       /* the interface for this device */
+       struct usb_interface    *interface;
+       /* in case we need to retract our submissions */
+       struct usb_anchor       submitted;
+       /* the size of the receive buffer */
+       size_t                  bulk_in_size;
+       /* the address of the bulk in endpoint */
+       __u8                    bulk_in_endpointAddr;
+       /* the address of the bulk out endpoint */
+       __u8                    bulk_out_endpointAddr;
+       __u32                   tag;
+       struct kref             kref;
+       struct mutex            mutex;
+};
+#define to_nv_usb_dev(d) container_of(d, struct nv_usb, kref)
+
+#endif
+
+
+