--- /dev/null
+/*
+ * 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");