Message ID | 1516279287-30032-1-git-send-email-jesse.sung@canonical.com |
---|---|
State | New |
Headers | show |
Series | [linux-oem] UBUNTU: SAUCE: Support fw upgrade for CAC Reader BCM58102 | expand |
On 18.01.2018 14:41, Wen-chien Jesse Sung wrote: > BugLink: https://launchpad.net/bugs/1744041 > > Import usb-cv from Broadcom's release 4.8.005.2-1.0.0. > > Signed-off-by: Wen-chien Jesse Sung <jesse.sung@canonical.com> > --- > ubuntu/Makefile | 6 + > ubuntu/usb-cv/Makefile | 1 + > ubuntu/usb-cv/usb-cv.c | 1106 ++++++++++++++++++++++++++++++++++++++++++++++++ > ubuntu/usb-cv/usb-cv.h | 192 +++++++++ > 4 files changed, 1305 insertions(+) > create mode 100644 ubuntu/usb-cv/Makefile > create mode 100644 ubuntu/usb-cv/usb-cv.c > create mode 100644 ubuntu/usb-cv/usb-cv.h > > diff --git a/ubuntu/Makefile b/ubuntu/Makefile > index 934baef..d9a36cb 100644 > --- a/ubuntu/Makefile > +++ b/ubuntu/Makefile > @@ -37,6 +37,12 @@ endif > ## > ## > ## > +ifeq ($(ARCH),x86) > +obj-y += usb-cv/ > +endif > +## > +## > +## > ## > ## > ## > diff --git a/ubuntu/usb-cv/Makefile b/ubuntu/usb-cv/Makefile > new file mode 100644 > index 0000000..40654ab > --- /dev/null > +++ b/ubuntu/usb-cv/Makefile > @@ -0,0 +1 @@ > +obj-m += usb-cv.o > diff --git a/ubuntu/usb-cv/usb-cv.c b/ubuntu/usb-cv/usb-cv.c > new file mode 100644 > index 0000000..86020dd > --- /dev/null > +++ b/ubuntu/usb-cv/usb-cv.c > @@ -0,0 +1,1106 @@ > +/****************************************************************************** > + * > + * Copyright 2017 > + * Broadcom Limited > + * 1 Yishun Avenue 7, Singapore 768923, Singapore > + * > + *****************************************************************************/ > + > +#include <linux/string.h> > +#include <linux/mm.h> > +#include "usb-cv.h" > +#include <linux/fs.h> > +#include <linux/sched.h> > +#include <linux/uaccess.h> > + > + > +volatile int int_urb_status = -1; > +volatile int tx_bulk_in_sig = 0; > +volatile int rx_bulk_in_sig = 0; > +volatile int ush_is_in_sbl = 0; > +volatile int reply_received_ush_is_in_sbl = 0; > + > +int packet_for_sbi_in_sbl = 0; > + > +struct write_buffer { > + unsigned int cmd_length; //Use int for both 32 and 64 bit kernel > + unsigned char cmd_buffer[1]; > +}; > + > +typedef unsigned int cv_transport_type; > +typedef unsigned short cv_command_id; > + > +typedef struct td_cv_host_control_get_request { > + cv_transport_type transportType; /* CV_TRANS_TYPE_HOST_CONTROL */ > + uint32_t transportLen; /* length of entire transport block in bytes */ > + cv_command_id commandID; /* CV_HOST_CONTROL_GET_REQUEST */ > +} /*__attribute__((__packed__))*/ cv_host_control_get_request; > + > +/* table of devices that work with this driver */ > +static struct usb_device_id cv_dev_table [] = { > + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_0000) }, > + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5800) }, > + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5801) }, > + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5802) }, > + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5804) }, > + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5805) }, > + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5821) }, > + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5822) }, > + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5823) }, > + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5824) }, > + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5825) }, > + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5826) }, > + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5830) }, > + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5831) }, > + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5832) }, > + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5833) }, > + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5834) }, > + { } /* Terminating entry */ > +}; > + > +MODULE_DEVICE_TABLE (usb, cv_dev_table); > + > +static DEFINE_MUTEX(cv_dev_usb_mutex); > + > +static struct usb_driver cv_dev_driver; > + > + > +void* KMalloc(size_t size, int flags) > +{ > + void *pMem; > + pMem = kmalloc(size, flags); > + DBG_PRINT(INFO_DBG, "kmalloc: 0x%x (0x%x) bytes at: %p\n", (int)size, flags, pMem); > + return pMem; > +} > + > + > +void KFree(void* pMem) > +{ > + DBG_PRINT(INFO_DBG, "kfree: %p\n", pMem); > + kfree(pMem); > +} > + > + > + > +int txCmdOk(struct usb_cv *dev) > +{ > + unsigned long start; > + unsigned long cur; > + unsigned long deltaTime; > + int txstatus; > + > + DBG_PRINT(INFO_DBG, "TX cmd in progress\n"); > + start = jiffies; > + while (!tx_bulk_in_sig) { > + txstatus = (dev->bulk_out_urb)->status; > + > + if(!packet_for_sbi_in_sbl) msleep(2); > + > + cur = jiffies; > + deltaTime = cur - start; > + > + if (deltaTime > MAX_WAIT_TX_TIME*HZ) { > + DBG_PRINT(ERR_DBG, "TX timeout, waited %d seconds\n", (unsigned int)(deltaTime/HZ)); > + return 0; > + } > + } > + > + // check if status error > + txstatus = dev->tx_status; > + if (txstatus) { > + DBG_PRINT(ERR_DBG, "tx error 0x%x\n", txstatus); > + return 0; > + } > + > + DBG_PRINT(INFO_DBG, "TX cmd done, urb->status: 0x%x\n", txstatus); > + > + // tx ok > + return 1; > +} > + > + > +void waitForResponse(struct usb_cv *dev) > +{ > + unsigned long start; > + unsigned long cur; > + unsigned long deltaTime; > + > + DBG_PRINT(INFO_DBG, "Waiting for RX response in progress\n"); > + start = jiffies; > + while (!rx_bulk_in_sig) { > + > + if(!packet_for_sbi_in_sbl) msleep(10); > + > + cur = jiffies; > + deltaTime = cur - start; > + > + if (deltaTime > MAX_WAIT_RX_TIME*HZ) { > + DBG_PRINT(ERR_DBG, "RX timeout, waited %d seconds\n", (unsigned int)(deltaTime/HZ)); > + break; > + } > + } > +} > + > + > +void displayDump(int outLen, unsigned char *buf) > +{ > + char tmp[4] = ""; > + char tmpl[4] = ""; > + int i = 0, j = 0; > + int k = 0; > + char content[DISPLEN*3+1], display[DISPLEN+1]; > + char contentl[DISPLEN*3+1];//, displayl[DISPLEN+1]; > + > + if((NULL==buf)||(0==outLen)) { > + DBG_PRINT(ERR_DBG, "Wrong input to the functions check at %d, in %s\n", __LINE__-2, __FILE__); > + } > + > + content[0] = display[0] = display[DISPLEN] = '\0'; > + contentl[0] = '\0'; > + > + // check length > + if (outLen>0x100) { > + DBG_PRINT(INFO_DBG,"Buffer Length: 0x%x, only printing 0x100\n", outLen); > + outLen=0x100; > + } > + > + do{ > + sprintf(tmp, "%02x ", buf[i]); > + sprintf(tmpl, "%02x ", buf[i]); > + strcat(content, tmp); > + > + /* printk adds new line, don't print now */ > + /* DBG_PRINT(ERR_DBG,tmp); */ > + display[i%DISPLEN] = '.'; > + > + if((buf[i]>31)&&(buf[i]<127)) > + display[i%DISPLEN] = buf[i]; > + > + i++; > + k++; > + > + if(k%8 == 0) { > + /* Print this way due to new line added by printk */ > + DBG_PRINT(INFO_DBG,"%s\t\t%s",content,display); > + /* > + DBG_PRINT(INFO_DBG,"%s",display); > + DBG_PRINT(INFO_DBG,"\n"); > + */ > + } > + > + if (0 == (i%DISPLEN)) { > + display[DISPLEN] = '\0'; > + content[0] = display[0] = '\0'; > + continue; > + } > + > + if (i == outLen) { > + display[i%DISPLEN] = '\0'; > + for(j = (i%DISPLEN); j < DISPLEN; j++) { > + /* Each byte takes 3 spaces */ > + strcat(contentl, " "); > + } > + > + /* Print this way due to new line added by printk */ > + DBG_PRINT(INFO_DBG,"%s%s\t\t%s",content, contentl, display); > + /* > + DBG_PRINT(ERR_DBG,"\t%s",contentl); > + DBG_PRINT(ERR_DBG,"\t%s\n",display); > + */ > + } > + > + } while(i<outLen); > + > + DBG_PRINT(INFO_DBG,"\n"); > +} > + > + > +static void cv_dev_delete(struct kref *kref) > +{ > + struct usb_cv *dev = to_cv_dev(kref); > + > + /* free memory allocated for interrupt buffer */ > + > + if(dev->udev != NULL) > + usb_put_dev(dev->udev); > + > + if(dev != NULL) > + KFree (dev); > +} > + > + > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21) > +static void cv_dev_read_interrupt_callback (struct urb *urb, struct pt_regs *regs) > +#else > +static void cv_dev_read_interrupt_callback (struct urb *urb) > +#endif > + > +{ > + struct usb_cv *dev; > + dev = (struct usb_cv *)urb->context; > + int_urb_status = urb->status; > + > + DBG_PRINT(INFO_DBG, "In cv_dev_read_interrupt_callback: %d, actual_length: 0x%x\n", urb->status, urb->actual_length); > + > + switch (urb->status) { > + case 0: /* success */ > + break; > + case -ECONNRESET: /* unlink */ > + case -ENOENT: > + case -ESHUTDOWN: > + //case -EPROTO: /* This happens before disconnecting (USH rebooting), still submitting here */ > + return; > + /* -EPIPE: should clear the halt */ > + default: /* error */ > + DBG_PRINT(ERR_DBG, "resubmitting the urb in interrupt_callback :%d\n", > + urb->status); > + } > + > + int_urb_status = usb_submit_urb (urb, GFP_ATOMIC); > + if (int_urb_status) > + DBG_PRINT(ERR_DBG,"can't resubmit urb in read interrupt callback\n"); > + > + return; > +} > + > + > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21) > +static void cv_dev_read_bulk_callback (struct urb *urb, struct pt_regs *regs) > +#else > +static void cv_dev_read_bulk_callback (struct urb *urb) > +#endif > +{ > + struct usb_cv *dev = urb->context; > + int status, len; > + int submit_urb = 0; > + > + DBG_PRINT(INFO_DBG, "CV, In cv_dev_read_bulk_callback()\n"); > + > + if(ush_is_in_sbl && reply_received_ush_is_in_sbl) > + { > + DBG_PRINT(INFO_DBG, "Reply in SBL has been received, don't process in cv_dev_read_bulk_callback\n"); > + return; > + } > + > + status = urb->status; > + > + if(status == -EPROTO) > + { > + /* This happens before disconnecting (USH rebooting), don't destroy the data received previously */ > + DBG_PRINT(INFO_DBG, "EPROTO, don't process in cv_dev_read_bulk_callback\n"); > + return; > + } > + > + dev->actual_length = urb->actual_length; > + dev->rx_status = urb->status; > + > + DBG_PRINT(INFO_DBG, "In cv_dev_read_bulk_callback: %d, actual_length: 0x%x\n", status, urb->actual_length); > + len = urb->actual_length; > + > + switch (status) { > + case 0: /* success */ > + break; > + case -ECONNRESET: /* unlink */ > + case -ENOENT: > + case -ESHUTDOWN: > + memset(dev->bulk_in_buffer, '\0', IN_BUFFER_SIZE); > + rx_bulk_in_sig = 1; > + return; > + /* -EPIPE: should clear the halt */ > + default: /* error */ > + memset(dev->bulk_in_buffer, '\0', IN_BUFFER_SIZE); > + DBG_PRINT(ERR_DBG, "resubmitting the urb in read bulk callback :%d\n", status); > + } > + > + if(ush_is_in_sbl) > + { > + // Don't submit if reply has been received when in SBL mode > + if(!reply_received_ush_is_in_sbl) > + { > + submit_urb = 1; > + reply_received_ush_is_in_sbl = 1; > + } > + } > + else > + { > + submit_urb = 1; > + } > + > + if(submit_urb) > + { > + status = usb_submit_urb(urb, GFP_ATOMIC); > + if (status) > + DBG_PRINT(ERR_DBG,"can't resubmit read urb \n"); > + } > + > + rx_bulk_in_sig = 1; > +} > + > + > +static int cv_dev_open(struct inode *inode, struct file *file) > +{ > + struct usb_cv *dev; > + struct usb_interface *interface; > + int subminor; > + int retval = 0; > + int_urb_status = -1; > + > + DBG_PRINT(INFO_DBG,"CV, In cv_dev_open()\n"); > + > + mutex_lock(&cv_dev_usb_mutex); > + > + subminor = iminor(inode); > + > + interface = usb_find_interface(&cv_dev_driver, subminor); > + > + if (!interface) { > + DBG_PRINT(ERR_DBG,"%s - error, can't find device for minor %d\n", __FUNCTION__, subminor); > + retval = -ENODEV; > + goto exit; > + } > + > + dev = usb_get_intfdata(interface); > + > + if (!dev) { > + retval = -ENODEV; > + goto exit; > + } > + > + spin_lock(&dev->cv_lock); > + > + /* increment our usage count for the device */ > + kref_get(&dev->kref); > + > + spin_unlock(&dev->cv_lock); > + > + /* save our object in the file's private structure */ > + file->private_data = dev; > + > +exit: > + mutex_unlock(&cv_dev_usb_mutex); > + return retval; > +} > + > + > +static int cv_dev_release(struct inode *inode, struct file *file) > +{ > + struct usb_cv *dev; > + > + DBG_PRINT(INFO_DBG, "CV, In cv_dev_release()\n"); > + > + dev = (struct usb_cv *)file->private_data; > + > + if (dev == NULL) { > + return -ENODEV; > + } > + > + spin_lock(&dev->cv_lock); > + > + /* decrement the count on our device */ > + kref_put(&dev->kref, cv_dev_delete); > + > + spin_unlock(&dev->cv_lock); > + > + return 0; > +} > + > + > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21) > +static void cv_dev_write_bulk_callback(struct urb *urb, struct pt_regs *regs) > +#else > +static void cv_dev_write_bulk_callback(struct urb *urb) > +#endif > +{ > + struct usb_cv *dev = urb->context; > + int status; > + > + DBG_PRINT(INFO_DBG, "CV, In cv_dev_write_bulk_callback()\n"); > + > + int_urb_status = -1; > + > + status = urb->status; > + > + DBG_PRINT(INFO_DBG, "In cv_dev_write_bulk_callback, status: %d, actual_length: %d\n", status, urb->actual_length); > + > + /* sync/async unlink faults aren't errors */ > + if (status) { > + if (status == -ENOENT || > + status == -ECONNRESET || > + status == -ESHUTDOWN) { > + goto exit; > + } else { > + DBG_PRINT(ERR_DBG, "nonzero status received: %d\n", status); > + } > + } > + > +exit: > + > + dev->tx_status = status; > + > + if(dev->bulk_out_buffer != NULL) { > + KFree(dev->bulk_out_buffer); > + dev->bulk_out_buffer=NULL; > + } > + > + if(dev->bulk_out_urb != NULL) > + usb_free_urb(dev->bulk_out_urb); > + > + // signal that the tx has completed > + tx_bulk_in_sig = 1; > +} > + > + > +static void cv_dev_submit_cmd_tasklet(unsigned long data) > +{ > + struct usb_cv *dev; > + int retval; > + > + DBG_PRINT(INFO_DBG, "CV, In cv_dev_submit_cmd_tasklet()\n"); > + > + dev = (struct usb_cv *)data; > + > + spin_lock(&dev->cv_lock); > + > + /* create a urb, and a buffer for it, and copy the data to the urb */ > + dev->bulk_out_urb = usb_alloc_urb(0, GFP_ATOMIC); > + > + if (!dev->bulk_out_urb) { > + retval = -ENOMEM; > + spin_unlock(&dev->cv_lock); > + return; > + } > + > + /* initialize the urb properly */ > + usb_fill_bulk_urb(dev->bulk_out_urb, > + dev->udev, > + usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), > + dev->bulk_out_buffer, > + dev->write_count, > + cv_dev_write_bulk_callback, > + dev); > + > + /* send the data out the bulk port */ > + retval = usb_submit_urb(dev->bulk_out_urb, GFP_ATOMIC); > + if (retval) { > + DBG_PRINT(ERR_DBG,"%s - failed submitting write urb, error %d\n", __FUNCTION__, retval); > + } > + > + spin_unlock(&dev->cv_lock); > +} > + > + > +static ssize_t cv_dev_submit_cmd(struct file *file, struct usb_cv *dev, const char __user *user_buffer) > +{ > + int retval=0; > + int count; > + void __user *argp = (void __user *)user_buffer; > + struct write_buffer *wbuffer = NULL; > + struct write_buffer *rbuffer = (struct write_buffer *)argp; > + int curr_buf_size; > + unsigned char *curr_buf_ptr; > + bool last_packet_for_sbi_in_sbl = 0; > + > + DBG_PRINT(INFO_DBG, "CV, In cv_dev_submit_cmd()\n"); > + > + spin_lock(&dev->cv_lock); > + > + // Get length at beginning of the user_buffer > + if (copy_from_user(&count, argp, sizeof(int))) { > + DBG_PRINT(ERR_DBG,"Copy length failed in %s\n",__FUNCTION__); > + retval = -EFAULT; > + goto error; > + } > + > + //FW Upgrade packets for SBI in SBL > + if(count & FW_UPGRADE_IN_SBL_MASK) > + { > + //Clear the flag > + count &= ~FW_UPGRADE_IN_SBL_MASK; > + packet_for_sbi_in_sbl = 1; > + } > + else > + { > + packet_for_sbi_in_sbl = 0; > + } > + > + if(count & FW_UPGRADE_IN_SBL_LAST_PKT_MASK) > + { > + DBG_PRINT(INFO_DBG,"Last pkt received in SBL\n"); > + > + //Clear the flag > + count &= ~FW_UPGRADE_IN_SBL_LAST_PKT_MASK; > + last_packet_for_sbi_in_sbl = 1; > + } > + else > + { > + last_packet_for_sbi_in_sbl = 0; > + } > + > + DBG_PRINT(INFO_DBG,"Received write request of %d bytes\n",(int)count); > + > + if (count > OUT_BUFFER_SIZE_LIMIT) > + { > + DBG_PRINT(ERR_DBG,"Write request length %d too long in %s\n", count, __FUNCTION__); > + retval = -EFAULT; > + goto error; > + } > + > + //wbuffer = (struct write_buffer *)KMalloc(sizeof(struct write_buffer) + OUT_BUFFER_SIZE - sizeof(unsigned char),GFP_ATOMIC); > + wbuffer = (struct write_buffer *)KMalloc(sizeof(struct write_buffer) + count - sizeof(unsigned char),GFP_ATOMIC); > + if(wbuffer == NULL) { > + DBG_PRINT(ERR_DBG,"Failed to KMalloc memory in %s\n",__FUNCTION__); > + retval = -ENOMEM; > + goto error; > + } > + > + if (copy_from_user(wbuffer, argp, sizeof(struct write_buffer) + count - sizeof(unsigned char))) { > + DBG_PRINT(ERR_DBG,"Copy failed in %s\n",__FUNCTION__); > + retval = -EFAULT; > + KFree(wbuffer); > + goto error; > + } > + > + DBG_PRINT(INFO_DBG,"In %s\n",__FUNCTION__); > + > + > + if(packet_for_sbi_in_sbl) > + { > + wbuffer->cmd_length &= ~FW_UPGRADE_IN_SBL_MASK; > + } > + > + if(last_packet_for_sbi_in_sbl) > + { > + wbuffer->cmd_length &= ~FW_UPGRADE_IN_SBL_LAST_PKT_MASK; > + } > + > + curr_buf_size = wbuffer->cmd_length; > + curr_buf_ptr = wbuffer->cmd_buffer; > + > + /* Increase alloc size to avoid errors when size is too small here */ > + dev->bulk_out_buffer = (char *)KMalloc(curr_buf_size < 512 ? 512 : curr_buf_size,GFP_ATOMIC); > + dev->write_count = curr_buf_size; > + > + if (!dev->bulk_out_buffer) { > + DBG_PRINT(ERR_DBG,"Failed to KMalloc memory in %s\n",__FUNCTION__); > + retval = -ENOMEM; > + KFree(wbuffer); > + goto error; > + } > + > + /* verify that we actually have some data to write */ > + if (curr_buf_size == 0) { > + DBG_PRINT(ERR_DBG,"The Length of data to be written is zero\n"); > + KFree(wbuffer); > + goto error; > + } > + > + DBG_PRINT(INFO_DBG,"Current write request of %d bytes\n", curr_buf_size); > + > + memcpy(dev->bulk_out_buffer, curr_buf_ptr, curr_buf_size); > + > + DBG_PRINT(INFO_DBG, "Conforming write of \n"); > + displayDump(curr_buf_size, dev->bulk_out_buffer); > + > + if(file->f_flags & O_NONBLOCK) { > + > + spin_unlock(&dev->cv_lock); > + tasklet_init(&dev->cv_dev_tl, cv_dev_submit_cmd_tasklet, (unsigned long)dev); > + tasklet_schedule(&dev->cv_dev_tl); > + tasklet_kill(&dev->cv_dev_tl); > + > + msleep(200); > + > + retval = dev->actual_length; > + if (copy_to_user((void __user *)rbuffer->cmd_buffer, dev->bulk_in_buffer,dev->actual_length)) { > + DBG_PRINT(ERR_DBG,"Copy to user failed in %s\n", __FUNCTION__); > + retval = -EFAULT; > + } > + > + DBG_PRINT(INFO_DBG, "Date read from device \n"); > + displayDump(dev->actual_length, dev->bulk_in_buffer); > + goto error; > + > + } else { > + > + /* create a urb, and a buffer for it, and copy the data to the urb */ > + dev->bulk_out_urb = usb_alloc_urb(0, GFP_ATOMIC); > + if (!dev->bulk_out_urb) { > + DBG_PRINT(ERR_DBG,"Failed to allocate urb in %s\n", __FUNCTION__); > + retval = -ENOMEM; > + goto error; > + } > + > + /* initialize the urb properly */ > + usb_fill_bulk_urb(dev->bulk_out_urb, > + dev->udev, > + usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), > + dev->bulk_out_buffer, > + dev->write_count, > + cv_dev_write_bulk_callback, > + dev); > + > + // initialize bulk in parameters > + memset(dev->bulk_in_buffer, '\0', IN_BUFFER_SIZE); > + dev->actual_length = 0; > + tx_bulk_in_sig = 0; // reset tx signal > + rx_bulk_in_sig = 0; // reset rx signal > + > + /* send the data out the bulk port */ > + retval = usb_submit_urb(dev->bulk_out_urb, GFP_ATOMIC); > + if (retval) { > + DBG_PRINT(ERR_DBG,"%s - failed submitting write urb, error %d\n", __FUNCTION__, retval); > + goto error; > + } > + > + DBG_PRINT(INFO_DBG, "URB Submitted OK, waiting for tx complete\n"); > + if (!txCmdOk(dev)) { > + DBG_PRINT(ERR_DBG,"Tx failed\n"); > + retval = -EFAULT; > + goto error; > + } > + > + if(packet_for_sbi_in_sbl && !last_packet_for_sbi_in_sbl) > + { > + DBG_PRINT(INFO_DBG,"SBI load in SBL, no response from USH expected\n"); > + retval = 0; > + goto error; > + } > + } > + > + if(wbuffer != NULL) > + KFree(wbuffer); > + > + if(!(file->f_flags & O_NONBLOCK)) > + { > + DBG_PRINT(INFO_DBG, "Transmitted command, waiting for rx\n"); > + waitForResponse(dev); > + if (dev->rx_status != 0) { > + DBG_PRINT(ERR_DBG,"rx error: 0x%x\n", dev->rx_status); > + retval = -EFAULT; > + goto error; > + } > + > + DBG_PRINT(INFO_DBG, "Completed the Bulk read request with %d bytes\n", dev->actual_length); > + if (dev->actual_length == 0) { > + DBG_PRINT(ERR_DBG,"failed to rx data\n"); > + retval = -EFAULT; > + goto error; > + } > + > + if (copy_to_user((void __user *)rbuffer->cmd_buffer, dev->bulk_in_buffer, dev->actual_length)) { > + DBG_PRINT(ERR_DBG,"Copy to user failed in %s\n", __FUNCTION__); > + retval = -EFAULT; > + goto error; > + } > + > + DBG_PRINT(INFO_DBG, "Data read from device \n"); > + displayDump(dev->actual_length, dev->bulk_in_buffer); > + > + retval = dev->actual_length; > + } > + > +error: > + > + if (dev->bulk_out_buffer != NULL) { > + KFree(dev->bulk_out_buffer); > + dev->bulk_out_buffer=NULL; > + } > + > + spin_unlock(&dev->cv_lock); > + return retval; > +} > + > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) > +static int cv_dev_ioctl(struct inode *inode, struct file *file, unsigned int code, unsigned long value) > +#else > +/* Changed from static int to static long to get rid of compilation errors on Ubuntu 16.04 */ > +static long cv_dev_ioctl(struct file *file, unsigned int code, unsigned long value) > +#endif > +{ > + struct usb_cv *dev; > + struct usb_interface *iface; > + struct usb_device *udev; > + int retval = 0; > + > + /* > + * extract the type and number bitfields, and don't decode > + * wrong cmds: return ENOTTY before verify_area() > + */ > + > + DBG_PRINT(INFO_DBG,"CV, In cv_dev_ioctl()\n"); > + > + if(_IOC_TYPE(code) != CV_IOC_MAGIC) return -ENOTTY; > + if(_IOC_NR(code) > CV_IOC_MAXNR) return -ENOTTY; > + > + dev = (struct usb_cv *)file->private_data; > + iface = dev->interface; > + udev = interface_to_usbdev (iface); > + > + switch(code) { > + > + case CV_SUBMIT_CMD: > + DBG_PRINT(INFO_DBG,"Received CV_SUBMIT_COMMAND\n"); > + if(dev->bulk_in_buffer) > + { > + memset(dev->bulk_in_buffer, '\0', IN_BUFFER_SIZE); > + retval = cv_dev_submit_cmd(file, dev, (void __user *)value); > + } > + else > + { > + DBG_PRINT(ERR_DBG,"dev->bulk_in_buffer is NULL\n"); > + retval = -EFAULT; > + } > + break; > + > + case CV_GET_COMMAND_STATUS: > + DBG_PRINT(INFO_DBG,"Received CV_GET_COMMAND_STATUS\n"); > + if(dev->bulk_in_buffer) > + { > + memset(dev->bulk_in_buffer, '\0', IN_BUFFER_SIZE); > + retval = cv_dev_submit_cmd(file, dev, (void __user *)value); > + } > + else > + { > + DBG_PRINT(ERR_DBG,"dev->bulk_in_buffer is NULL\n"); > + retval = -EFAULT; > + } > + break; > + > + default : > + DBG_PRINT(ERR_DBG,"Unknown IOCTL request received\n"); > + break; > + } > + > + return retval; > +} > + > + > +static struct file_operations cv_dev_fops = { > + .owner = THIS_MODULE, > + .open = cv_dev_open, > + //.write = cv_dev_write, > + .release = cv_dev_release, > +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) > + .ioctl = cv_dev_ioctl, > +#else > + /* compat_ioctl is for 32 bit user space applications. */ > + /* .unlocked_ioctl = cv_dev_ioctl, */ > + .compat_ioctl = cv_dev_ioctl, > +#endif > +}; > + > + > +/* > + * usb class driver info in order to get a minor number from the usb core, > + * and to have the device registered with devfs and the driver core > + */ > + > +static struct usb_class_driver cv_dev_class = { > + .name = DEVICE_NAME"%d", > + .fops = &cv_dev_fops, > + //.mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, > + .minor_base = USB_CV_MINOR_BASE, > +}; > + > + > +static int cv_dev_probe(struct usb_interface *interface, const struct usb_device_id *id) > +{ > + struct usb_cv *dev = NULL; > + struct usb_host_interface *iface_desc; > + struct usb_endpoint_descriptor *endpoint; > + struct usb_device_descriptor descriptor; > + int i; > + int retval = -ENOMEM; > + static int probe_on = 0; > + int pipe; > + > + probe_on++; > + > + DBG_PRINT(INFO_DBG, "Interface Minor :%d\n",interface->minor); > + DBG_PRINT(INFO_DBG, "BRCM CV_USB Device Driver First Cut \n"); > + /* Comment out to get built with Ubuntu 16.04 */ > + /* DBG_PRINT(INFO_DBG, "Built is %s %s\n",__DATE__,__TIME__); */ > + > + /* allocate memory for our device state and initialize it */ > + dev = (struct usb_cv *)KMalloc(sizeof(struct usb_cv), GFP_ATOMIC); > + if (dev == NULL) { > + goto error; > + } > + > + memset(dev, 0x00, sizeof (*dev)); > + kref_init(&dev->kref); > + > + dev->udev = usb_get_dev(interface_to_usbdev(interface)); > + dev->interface = interface; > + descriptor = dev->udev->descriptor; > + > + /* Validate the VENDOR ID & PRODUCT ID */ > + if ( (descriptor.idVendor != USB_CV_VENDOR_ID) || > + ((descriptor.idProduct != USB_CV_PID_0000) && > + (descriptor.idProduct != USB_CV_PID_5800) && > + (descriptor.idProduct != USB_CV_PID_5801) && > + (descriptor.idProduct != USB_CV_PID_5802) && > + (descriptor.idProduct != USB_CV_PID_5804) && > + (descriptor.idProduct != USB_CV_PID_5805) && > + (descriptor.idProduct != USB_CV_PID_5821) && > + (descriptor.idProduct != USB_CV_PID_5822) && > + (descriptor.idProduct != USB_CV_PID_5823) && > + (descriptor.idProduct != USB_CV_PID_5824) && > + (descriptor.idProduct != USB_CV_PID_5825) && > + (descriptor.idProduct != USB_CV_PID_5826) && > + (descriptor.idProduct != USB_CV_PID_5830) && > + (descriptor.idProduct != USB_CV_PID_5831) && > + (descriptor.idProduct != USB_CV_PID_5832) && > + (descriptor.idProduct != USB_CV_PID_5833) && > + (descriptor.idProduct != USB_CV_PID_5834)) ) > + { > + /* This is not an error case, so returning ZERO */ > + /* We need to free the memory allocated as this device is > + no supported by this driver */ > + KFree(dev); > + DBG_PRINT(INFO_DBG, "Probe on CV Driver for VENDOR:%X\tPRODUCT:%X\n", > + descriptor.idVendor,descriptor.idProduct); > + return 0; > + } > + > + if(descriptor.idProduct == USB_CV_PID_5830) > + { > + ush_is_in_sbl = 1; > + reply_received_ush_is_in_sbl = 0; > + } > + else > + { > + ush_is_in_sbl = 0; > + } > + > + /* set up the endpoint information */ > + /* use only the first bulk-in and bulk-out endpoints */ > + iface_desc = interface->cur_altsetting; > + > + DBG_PRINT(ERR_DBG, " bLength = 0x%02x \n bDescriptorType = 0x%02x \n bcdUSB = 0x%02x \n bDeviceClass = 0x%02x \n bDeviceProtocol = 0x%02x \n bMaxPacketSize = 0x%02x \n idVendor = 0x%02x \n idProduct = 0x%02x \n bcdDevice = 0x%02x \n iManufacturer = 0x%02x \n iProduct = 0x%02x \n iSerialNumber = 0x%02x \n bNumConfigurations = 0x%02x \n", > + descriptor.bLength, descriptor.bDescriptorType, descriptor.bcdUSB, descriptor.bDeviceClass, > + descriptor.bDeviceProtocol, descriptor.bMaxPacketSize0, descriptor.idVendor, descriptor.idProduct, > + descriptor.bcdDevice, descriptor.iManufacturer, descriptor.iProduct, descriptor.iSerialNumber, > + descriptor.bNumConfigurations); > + > + if( (descriptor.bDeviceClass) || (descriptor.bDeviceSubClass) || (descriptor.bDeviceProtocol)) { > + DBG_PRINT(ERR_DBG, "Non Zero values of Class/SubClass/Protocol. Not a Composite device \n"); > + } > + > + DBG_PRINT(INFO_DBG, " bNumEndpoints = %d\n", iface_desc->desc.bNumEndpoints); > + > + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { > + endpoint = &iface_desc->endpoint[i].desc; > + > + if (!dev->bulk_in_endpointAddr && > + (endpoint->bEndpointAddress & USB_DIR_IN) && > + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) > + { > + /* we found a bulk in endpoint */ > + DBG_PRINT(INFO_DBG, " found a bulk in endpoint %d\n", i); > + dev->bulk_in_size = endpoint->wMaxPacketSize; > + dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; > + } > + > + /* Changed USB_DIR_IN to USB_DIR_OUT */ > + if (!dev->bulk_out_endpointAddr && > + !(endpoint->bEndpointAddress & USB_DIR_OUT) && > + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) > + { > + /* we found a bulk out endpoint */ > + DBG_PRINT(INFO_DBG, " found a bulk out endpoint %d\n", i); > + dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; > + dev->bulk_out_size = endpoint->wMaxPacketSize; > + } > + > + if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) && > + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) > + { > + DBG_PRINT(INFO_DBG, " found a interrupt endpoint %d\n", i); > + dev->interrupt_in_endpoint = endpoint; > + dev->interrupt_in_size = endpoint->wMaxPacketSize; > + } > + } > + > + if (probe_on%2 == 1) { > + > + if ( !(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) { > + DBG_PRINT(ERR_DBG,"Could not find both bulk-in and bulk-out endpoints\n"); > + goto error; > + } > + > + if( !dev->interrupt_in_endpoint ) { > + DBG_PRINT(ERR_DBG,"Could not find interrupt_in_endpoint \n"); > + goto error; > + } > + > + dev->interrupt_in_urb = usb_alloc_urb(0, GFP_ATOMIC); > + > + if (!dev->interrupt_in_urb) { > + retval = -ENOMEM; > + goto error; > + } > + > + dev->interrupt_in_buffer = (unsigned char *)KMalloc(dev->interrupt_in_size, GFP_ATOMIC); > + if (!dev->interrupt_in_buffer) { > + retval = -ENOMEM; > + usb_free_urb(dev->interrupt_in_urb); > + goto error; > + } > + > + memset(dev->interrupt_in_buffer, '\0',8); > + > + pipe = usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress); > + > + usb_fill_int_urb(dev->interrupt_in_urb, dev->udev,pipe, > + dev->interrupt_in_buffer, 8, > + cv_dev_read_interrupt_callback, dev, > + dev->interrupt_in_endpoint->bInterval); > + > + retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC); > + if (retval) { > + retval = -ENOMEM; > + DBG_PRINT(ERR_DBG,"%s - failed submitting interrupt urb, error %d\n", __FUNCTION__, retval); > + goto error; > + } > + > + dev->bulk_in_urb = usb_alloc_urb(0, GFP_ATOMIC); > + > + if (!dev->bulk_in_urb) { > + retval = -ENOMEM; > + DBG_PRINT(ERR_DBG,"%s - failed to alloc_urb %d\n", __FUNCTION__, retval); > + goto error; > + } > + > + dev->bulk_in_buffer = (unsigned char *)KMalloc(IN_BUFFER_SIZE, GFP_ATOMIC); > + > + if(!dev->bulk_in_buffer) { > + retval = -ENOMEM; > + DBG_PRINT(ERR_DBG,"%s - failed to allocate memory %d\n", __FUNCTION__, retval); > + usb_free_urb(dev->bulk_in_urb); > + goto error; > + } > + > + memset(dev->bulk_in_buffer, '\0', IN_BUFFER_SIZE); > + > + usb_fill_bulk_urb(dev->bulk_in_urb, > + dev->udev, > + usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), > + dev->bulk_in_buffer, > + IN_BUFFER_SIZE, > + cv_dev_read_bulk_callback, > + dev); > + > + retval = usb_submit_urb(dev->bulk_in_urb, GFP_ATOMIC); > + if (retval) { > + DBG_PRINT(ERR_DBG,"%s - failed submitting bulk urb, error %d\n", __FUNCTION__, retval); > + goto error; > + } > + > + spin_lock_init(&dev->cv_lock); > + > + } > + > + /* 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, &cv_dev_class); > + > + if (retval) { > + /* something prevented us from registering this driver */ > + DBG_PRINT(ERR_DBG,"Not able to get a minor for this device.\n"); > + usb_set_intfdata(interface, NULL); > + goto error; > + } > + /* let the user know what node this device is now attached to */ > + DBG_PRINT(ERR_DBG,"USB Credential Vault device now attached to USBCv-%d\n", interface->minor); > + return 0; > + > +error: > + if (dev) > + kref_put(&dev->kref, cv_dev_delete); > + return retval; > +} > + > + > +static void cv_dev_disconnect(struct usb_interface *interface) > +{ > + struct usb_cv *dev; > + int minor = interface->minor; > + > + if(interface != NULL) { > + > + /* prevent cv_dev_open() from racing cv_dev_disconnect() */ > + mutex_lock(&cv_dev_usb_mutex); > + > + dev = usb_get_intfdata(interface); > + > + if(dev!=NULL) { > + > + if(dev->interrupt_in_urb != NULL) > + usb_kill_urb(dev->interrupt_in_urb); > + > + if(dev->interrupt_in_buffer != NULL) > + KFree(dev->interrupt_in_buffer); > + > + if(dev->interrupt_in_urb != NULL) > + usb_free_urb(dev->interrupt_in_urb); > + > + if(dev->bulk_in_urb != NULL) > + usb_kill_urb(dev->bulk_in_urb); > + > + if(dev->bulk_in_buffer != NULL) > + KFree(dev->bulk_in_buffer); > + > + if(dev->bulk_in_urb != NULL) > + usb_free_urb(dev->bulk_in_urb); > + > + } > + > + usb_set_intfdata(interface, NULL); > + /* give back our minor */ > + usb_deregister_dev(interface, &cv_dev_class); > + mutex_unlock(&cv_dev_usb_mutex); > + > + /* decrement our usage count */ > + kref_put(&dev->kref, cv_dev_delete); > + DBG_PRINT(ERR_DBG,"USB Credential Vault #%d now disconnected\n", minor); > + } > + > +} > + > + > +static struct usb_driver cv_dev_driver = { > + .name = "Credential Vault", > + .id_table = cv_dev_table, > + .probe = cv_dev_probe, > + .disconnect = cv_dev_disconnect, > +}; > + > + > +static int __init usb_cv_dev_init(void) > +{ > + int result; > + > + /* register this driver with the USB subsystem */ > + result = usb_register(&cv_dev_driver); > + if (result) > + DBG_PRINT(ERR_DBG,"usb_register failed. Error number %d\n", result); > + > + return result; > +} > + > + > +static void __exit usb_cv_dev_exit(void) > +{ > + /* deregister this driver with the USB subsystem */ > + usb_deregister(&cv_dev_driver); > +} > + > +module_init (usb_cv_dev_init); > +module_exit (usb_cv_dev_exit); > + > +module_param(interval, int, S_IRUGO | S_IWUSR); > +MODULE_PARM_DESC(interval, "Overrides interrupt interval"); > +MODULE_LICENSE("GPL"); > diff --git a/ubuntu/usb-cv/usb-cv.h b/ubuntu/usb-cv/usb-cv.h > new file mode 100644 > index 0000000..65b2a16 > --- /dev/null > +++ b/ubuntu/usb-cv/usb-cv.h > @@ -0,0 +1,192 @@ > +/****************************************************************************** > + * > + * Copyright 2017 > + * Broadcom Limited > + * 1 Yishun Avenue 7, Singapore 768923, Singapore > + * > + *****************************************************************************/ > +#ifndef _USB_CV_H_ > +#define _USB_CV_H_ > + > +#ifdef __KERNEL__ > +#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/usb.h> > +#include <asm/uaccess.h> > +#include <linux/version.h> > +#include <linux/fs.h> > + > + > +/* Define these values to match your devices */ > +#define USB_CV_VENDOR_ID 0x0a5c > + > +#define USB_CV_PID_0000 0x0000 > + > +#define USB_CV_PID_5800 0x5800 > +#define USB_CV_PID_5801 0x5801 > +#define USB_CV_PID_5802 0x5802 > +#define USB_CV_PID_5804 0x5804 > +#define USB_CV_PID_5805 0x5805 > +#define USB_CV_PID_5821 0x5821 > +#define USB_CV_PID_5822 0x5822 > +#define USB_CV_PID_5823 0x5823 > +#define USB_CV_PID_5824 0x5824 > +#define USB_CV_PID_5825 0x5825 > +#define USB_CV_PID_5826 0x5826 > +#define USB_CV_PID_5830 0x5830 > +#define USB_CV_PID_5831 0x5831 > +#define USB_CV_PID_5832 0x5832 > +#define USB_CV_PID_5833 0x5833 > +#define USB_CV_PID_5834 0x5834 > +#endif > + > +#define CMD_BUF_SIZE 16*sizeof(unsigned char) > + > +#define CV_IOC_MAGIC 'k' > + > +#define CV_GET_LAST_COMMAND_STATUS _IOR(CV_IOC_MAGIC,1,int) > + > +#define CV_GET_LATEST_COMMAND_STATUS _IOR(CV_IOC_MAGIC,2,int) > + > +#define CV_GET_COMMAND_STATUS _IOR(CV_IOC_MAGIC,3,int) > + > +#define CV_GET_CONFIG_DESCRIPTOR _IOR(CV_IOC_MAGIC,4,int) > + > +#define CV_SUBMIT_CMD _IOW(CV_IOC_MAGIC,5,int) > + > +#define CV_BURN_FW _IOWR(CV_IOC_MAGIC,6,int) > + > +#define CV_HOST_CTL _IOWR(CV_IOC_MAGIC,7,int) > + > + > +#define CV_IOC_MAXNR 7 > + > + > +#define GET_STATUS 0 > + > +#define GET_DESCRIPTOR 0x06 > + > +#define GET_CONFIGURATION 0x08 > + > + > +#define VENDOR_WRITE_REQUEST_TYPE 0x42 // 0100 0010 > + > +#define VENDOR_READ_REQUEST_TYPE 0xC2 // 1100 0010 > + > + > +#define ERR_DBG 0 > + > +#define INIT_DBG 1 > + > +#define INFO_DBG 2 > + > +#define TX_DBG 3 > + > +#define INTR_DBG 4 > + > +typedef unsigned short cv_command_id; > + > +#define FW_UPGRADE_IN_SBL_MASK 0x80000000 > +#define FW_UPGRADE_IN_SBL_LAST_PKT_MASK 0x40000000 > + > +/* Get a minor range for your devices from the usb maintainer */ > +#define USB_CV_MINOR_BASE 192 > +#define BUFFER_SIZE 4096 > +#define IN_BUFFER_SIZE 99000 > + > +#define OUT_BUFFER_SIZE 4096 > +#define OUT_BUFFER_SIZE_LIMIT 524288 /* 512 KB */ > + > +#define DISPLEN 0x08 > + > + > +#define to_cv_dev(d) container_of(d, struct usb_cv, kref) > + > +#define DEVICE_NAME "usb/cv" > + > +#define MAX_WAIT_TX_TIME 10 > +#define MAX_WAIT_RX_TIME 10 > + > +#ifdef __KERNEL__ > +/* Structure to hold all of our device specific stuff */ > +struct usb_cv { > + spinlock_t cv_lock; > + struct usb_device* udev; /* the usb device for this device */ > + struct usb_interface* interface; /* the interface for this device */ > + > + size_t bulk_in_size; /* the size of the receive buffer */ > + > + size_t bulk_out_size; /* the size of the maximum transmit buffer */ > + > + size_t interrupt_in_size; /* the size of the interrupt buffer */ > + > + __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ > + > + __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ > + > + > + > + struct usb_endpoint_descriptor* interrupt_in_endpoint; > + > + > + struct urb* interrupt_in_urb; > + > + struct urb* bulk_in_urb; > + > + struct urb* bulk_out_urb; > + > + struct urb* descriptor_in_urb; > + > + > + > + unsigned char* interrupt_in_buffer; > + > + unsigned char* bulk_out_buffer; > + > + unsigned char* bulk_in_buffer; > + > + unsigned char* descriptor_in_buffer; > + > + unsigned char* cmd_in_buffer; > + > + unsigned char* out_buffer; > + > + unsigned char* in_buffer; > + > + > + > + struct usb_ctrlrequest* cr_desc; > + > + struct usb_ctrlrequest* cr_submit_cmd; > + > + struct usb_ctrlrequest* cr_get_cmd; > + > + > + > + int probe_done; > + > + int actual_length; > + > + struct tasklet_struct cv_dev_tl; > + > + int write_count; > + > + struct kref kref; > + > + int tx_status; > + > + int rx_status; > + > +}; > +#endif > + > +/* Global variable that defines the present debug level of the driver. */ > +static int debug_level = ERR_DBG; > +static int interval = 1; > +#define DBG_PRINT(dbg_level, args...) if(!(debug_level<dbg_level)) printk(args) > + > +#endif /* _USB_CV_H_ */ > applied to linux-oem, thanks
diff --git a/ubuntu/Makefile b/ubuntu/Makefile index 934baef..d9a36cb 100644 --- a/ubuntu/Makefile +++ b/ubuntu/Makefile @@ -37,6 +37,12 @@ endif ## ## ## +ifeq ($(ARCH),x86) +obj-y += usb-cv/ +endif +## +## +## ## ## ## diff --git a/ubuntu/usb-cv/Makefile b/ubuntu/usb-cv/Makefile new file mode 100644 index 0000000..40654ab --- /dev/null +++ b/ubuntu/usb-cv/Makefile @@ -0,0 +1 @@ +obj-m += usb-cv.o diff --git a/ubuntu/usb-cv/usb-cv.c b/ubuntu/usb-cv/usb-cv.c new file mode 100644 index 0000000..86020dd --- /dev/null +++ b/ubuntu/usb-cv/usb-cv.c @@ -0,0 +1,1106 @@ +/****************************************************************************** + * + * Copyright 2017 + * Broadcom Limited + * 1 Yishun Avenue 7, Singapore 768923, Singapore + * + *****************************************************************************/ + +#include <linux/string.h> +#include <linux/mm.h> +#include "usb-cv.h" +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/uaccess.h> + + +volatile int int_urb_status = -1; +volatile int tx_bulk_in_sig = 0; +volatile int rx_bulk_in_sig = 0; +volatile int ush_is_in_sbl = 0; +volatile int reply_received_ush_is_in_sbl = 0; + +int packet_for_sbi_in_sbl = 0; + +struct write_buffer { + unsigned int cmd_length; //Use int for both 32 and 64 bit kernel + unsigned char cmd_buffer[1]; +}; + +typedef unsigned int cv_transport_type; +typedef unsigned short cv_command_id; + +typedef struct td_cv_host_control_get_request { + cv_transport_type transportType; /* CV_TRANS_TYPE_HOST_CONTROL */ + uint32_t transportLen; /* length of entire transport block in bytes */ + cv_command_id commandID; /* CV_HOST_CONTROL_GET_REQUEST */ +} /*__attribute__((__packed__))*/ cv_host_control_get_request; + +/* table of devices that work with this driver */ +static struct usb_device_id cv_dev_table [] = { + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_0000) }, + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5800) }, + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5801) }, + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5802) }, + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5804) }, + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5805) }, + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5821) }, + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5822) }, + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5823) }, + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5824) }, + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5825) }, + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5826) }, + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5830) }, + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5831) }, + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5832) }, + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5833) }, + { USB_DEVICE(USB_CV_VENDOR_ID, USB_CV_PID_5834) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, cv_dev_table); + +static DEFINE_MUTEX(cv_dev_usb_mutex); + +static struct usb_driver cv_dev_driver; + + +void* KMalloc(size_t size, int flags) +{ + void *pMem; + pMem = kmalloc(size, flags); + DBG_PRINT(INFO_DBG, "kmalloc: 0x%x (0x%x) bytes at: %p\n", (int)size, flags, pMem); + return pMem; +} + + +void KFree(void* pMem) +{ + DBG_PRINT(INFO_DBG, "kfree: %p\n", pMem); + kfree(pMem); +} + + + +int txCmdOk(struct usb_cv *dev) +{ + unsigned long start; + unsigned long cur; + unsigned long deltaTime; + int txstatus; + + DBG_PRINT(INFO_DBG, "TX cmd in progress\n"); + start = jiffies; + while (!tx_bulk_in_sig) { + txstatus = (dev->bulk_out_urb)->status; + + if(!packet_for_sbi_in_sbl) msleep(2); + + cur = jiffies; + deltaTime = cur - start; + + if (deltaTime > MAX_WAIT_TX_TIME*HZ) { + DBG_PRINT(ERR_DBG, "TX timeout, waited %d seconds\n", (unsigned int)(deltaTime/HZ)); + return 0; + } + } + + // check if status error + txstatus = dev->tx_status; + if (txstatus) { + DBG_PRINT(ERR_DBG, "tx error 0x%x\n", txstatus); + return 0; + } + + DBG_PRINT(INFO_DBG, "TX cmd done, urb->status: 0x%x\n", txstatus); + + // tx ok + return 1; +} + + +void waitForResponse(struct usb_cv *dev) +{ + unsigned long start; + unsigned long cur; + unsigned long deltaTime; + + DBG_PRINT(INFO_DBG, "Waiting for RX response in progress\n"); + start = jiffies; + while (!rx_bulk_in_sig) { + + if(!packet_for_sbi_in_sbl) msleep(10); + + cur = jiffies; + deltaTime = cur - start; + + if (deltaTime > MAX_WAIT_RX_TIME*HZ) { + DBG_PRINT(ERR_DBG, "RX timeout, waited %d seconds\n", (unsigned int)(deltaTime/HZ)); + break; + } + } +} + + +void displayDump(int outLen, unsigned char *buf) +{ + char tmp[4] = ""; + char tmpl[4] = ""; + int i = 0, j = 0; + int k = 0; + char content[DISPLEN*3+1], display[DISPLEN+1]; + char contentl[DISPLEN*3+1];//, displayl[DISPLEN+1]; + + if((NULL==buf)||(0==outLen)) { + DBG_PRINT(ERR_DBG, "Wrong input to the functions check at %d, in %s\n", __LINE__-2, __FILE__); + } + + content[0] = display[0] = display[DISPLEN] = '\0'; + contentl[0] = '\0'; + + // check length + if (outLen>0x100) { + DBG_PRINT(INFO_DBG,"Buffer Length: 0x%x, only printing 0x100\n", outLen); + outLen=0x100; + } + + do{ + sprintf(tmp, "%02x ", buf[i]); + sprintf(tmpl, "%02x ", buf[i]); + strcat(content, tmp); + + /* printk adds new line, don't print now */ + /* DBG_PRINT(ERR_DBG,tmp); */ + display[i%DISPLEN] = '.'; + + if((buf[i]>31)&&(buf[i]<127)) + display[i%DISPLEN] = buf[i]; + + i++; + k++; + + if(k%8 == 0) { + /* Print this way due to new line added by printk */ + DBG_PRINT(INFO_DBG,"%s\t\t%s",content,display); + /* + DBG_PRINT(INFO_DBG,"%s",display); + DBG_PRINT(INFO_DBG,"\n"); + */ + } + + if (0 == (i%DISPLEN)) { + display[DISPLEN] = '\0'; + content[0] = display[0] = '\0'; + continue; + } + + if (i == outLen) { + display[i%DISPLEN] = '\0'; + for(j = (i%DISPLEN); j < DISPLEN; j++) { + /* Each byte takes 3 spaces */ + strcat(contentl, " "); + } + + /* Print this way due to new line added by printk */ + DBG_PRINT(INFO_DBG,"%s%s\t\t%s",content, contentl, display); + /* + DBG_PRINT(ERR_DBG,"\t%s",contentl); + DBG_PRINT(ERR_DBG,"\t%s\n",display); + */ + } + + } while(i<outLen); + + DBG_PRINT(INFO_DBG,"\n"); +} + + +static void cv_dev_delete(struct kref *kref) +{ + struct usb_cv *dev = to_cv_dev(kref); + + /* free memory allocated for interrupt buffer */ + + if(dev->udev != NULL) + usb_put_dev(dev->udev); + + if(dev != NULL) + KFree (dev); +} + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21) +static void cv_dev_read_interrupt_callback (struct urb *urb, struct pt_regs *regs) +#else +static void cv_dev_read_interrupt_callback (struct urb *urb) +#endif + +{ + struct usb_cv *dev; + dev = (struct usb_cv *)urb->context; + int_urb_status = urb->status; + + DBG_PRINT(INFO_DBG, "In cv_dev_read_interrupt_callback: %d, actual_length: 0x%x\n", urb->status, urb->actual_length); + + switch (urb->status) { + case 0: /* success */ + break; + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + //case -EPROTO: /* This happens before disconnecting (USH rebooting), still submitting here */ + return; + /* -EPIPE: should clear the halt */ + default: /* error */ + DBG_PRINT(ERR_DBG, "resubmitting the urb in interrupt_callback :%d\n", + urb->status); + } + + int_urb_status = usb_submit_urb (urb, GFP_ATOMIC); + if (int_urb_status) + DBG_PRINT(ERR_DBG,"can't resubmit urb in read interrupt callback\n"); + + return; +} + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21) +static void cv_dev_read_bulk_callback (struct urb *urb, struct pt_regs *regs) +#else +static void cv_dev_read_bulk_callback (struct urb *urb) +#endif +{ + struct usb_cv *dev = urb->context; + int status, len; + int submit_urb = 0; + + DBG_PRINT(INFO_DBG, "CV, In cv_dev_read_bulk_callback()\n"); + + if(ush_is_in_sbl && reply_received_ush_is_in_sbl) + { + DBG_PRINT(INFO_DBG, "Reply in SBL has been received, don't process in cv_dev_read_bulk_callback\n"); + return; + } + + status = urb->status; + + if(status == -EPROTO) + { + /* This happens before disconnecting (USH rebooting), don't destroy the data received previously */ + DBG_PRINT(INFO_DBG, "EPROTO, don't process in cv_dev_read_bulk_callback\n"); + return; + } + + dev->actual_length = urb->actual_length; + dev->rx_status = urb->status; + + DBG_PRINT(INFO_DBG, "In cv_dev_read_bulk_callback: %d, actual_length: 0x%x\n", status, urb->actual_length); + len = urb->actual_length; + + switch (status) { + case 0: /* success */ + break; + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + memset(dev->bulk_in_buffer, '\0', IN_BUFFER_SIZE); + rx_bulk_in_sig = 1; + return; + /* -EPIPE: should clear the halt */ + default: /* error */ + memset(dev->bulk_in_buffer, '\0', IN_BUFFER_SIZE); + DBG_PRINT(ERR_DBG, "resubmitting the urb in read bulk callback :%d\n", status); + } + + if(ush_is_in_sbl) + { + // Don't submit if reply has been received when in SBL mode + if(!reply_received_ush_is_in_sbl) + { + submit_urb = 1; + reply_received_ush_is_in_sbl = 1; + } + } + else + { + submit_urb = 1; + } + + if(submit_urb) + { + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) + DBG_PRINT(ERR_DBG,"can't resubmit read urb \n"); + } + + rx_bulk_in_sig = 1; +} + + +static int cv_dev_open(struct inode *inode, struct file *file) +{ + struct usb_cv *dev; + struct usb_interface *interface; + int subminor; + int retval = 0; + int_urb_status = -1; + + DBG_PRINT(INFO_DBG,"CV, In cv_dev_open()\n"); + + mutex_lock(&cv_dev_usb_mutex); + + subminor = iminor(inode); + + interface = usb_find_interface(&cv_dev_driver, subminor); + + if (!interface) { + DBG_PRINT(ERR_DBG,"%s - error, can't find device for minor %d\n", __FUNCTION__, subminor); + retval = -ENODEV; + goto exit; + } + + dev = usb_get_intfdata(interface); + + if (!dev) { + retval = -ENODEV; + goto exit; + } + + spin_lock(&dev->cv_lock); + + /* increment our usage count for the device */ + kref_get(&dev->kref); + + spin_unlock(&dev->cv_lock); + + /* save our object in the file's private structure */ + file->private_data = dev; + +exit: + mutex_unlock(&cv_dev_usb_mutex); + return retval; +} + + +static int cv_dev_release(struct inode *inode, struct file *file) +{ + struct usb_cv *dev; + + DBG_PRINT(INFO_DBG, "CV, In cv_dev_release()\n"); + + dev = (struct usb_cv *)file->private_data; + + if (dev == NULL) { + return -ENODEV; + } + + spin_lock(&dev->cv_lock); + + /* decrement the count on our device */ + kref_put(&dev->kref, cv_dev_delete); + + spin_unlock(&dev->cv_lock); + + return 0; +} + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21) +static void cv_dev_write_bulk_callback(struct urb *urb, struct pt_regs *regs) +#else +static void cv_dev_write_bulk_callback(struct urb *urb) +#endif +{ + struct usb_cv *dev = urb->context; + int status; + + DBG_PRINT(INFO_DBG, "CV, In cv_dev_write_bulk_callback()\n"); + + int_urb_status = -1; + + status = urb->status; + + DBG_PRINT(INFO_DBG, "In cv_dev_write_bulk_callback, status: %d, actual_length: %d\n", status, urb->actual_length); + + /* sync/async unlink faults aren't errors */ + if (status) { + if (status == -ENOENT || + status == -ECONNRESET || + status == -ESHUTDOWN) { + goto exit; + } else { + DBG_PRINT(ERR_DBG, "nonzero status received: %d\n", status); + } + } + +exit: + + dev->tx_status = status; + + if(dev->bulk_out_buffer != NULL) { + KFree(dev->bulk_out_buffer); + dev->bulk_out_buffer=NULL; + } + + if(dev->bulk_out_urb != NULL) + usb_free_urb(dev->bulk_out_urb); + + // signal that the tx has completed + tx_bulk_in_sig = 1; +} + + +static void cv_dev_submit_cmd_tasklet(unsigned long data) +{ + struct usb_cv *dev; + int retval; + + DBG_PRINT(INFO_DBG, "CV, In cv_dev_submit_cmd_tasklet()\n"); + + dev = (struct usb_cv *)data; + + spin_lock(&dev->cv_lock); + + /* create a urb, and a buffer for it, and copy the data to the urb */ + dev->bulk_out_urb = usb_alloc_urb(0, GFP_ATOMIC); + + if (!dev->bulk_out_urb) { + retval = -ENOMEM; + spin_unlock(&dev->cv_lock); + return; + } + + /* initialize the urb properly */ + usb_fill_bulk_urb(dev->bulk_out_urb, + dev->udev, + usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), + dev->bulk_out_buffer, + dev->write_count, + cv_dev_write_bulk_callback, + dev); + + /* send the data out the bulk port */ + retval = usb_submit_urb(dev->bulk_out_urb, GFP_ATOMIC); + if (retval) { + DBG_PRINT(ERR_DBG,"%s - failed submitting write urb, error %d\n", __FUNCTION__, retval); + } + + spin_unlock(&dev->cv_lock); +} + + +static ssize_t cv_dev_submit_cmd(struct file *file, struct usb_cv *dev, const char __user *user_buffer) +{ + int retval=0; + int count; + void __user *argp = (void __user *)user_buffer; + struct write_buffer *wbuffer = NULL; + struct write_buffer *rbuffer = (struct write_buffer *)argp; + int curr_buf_size; + unsigned char *curr_buf_ptr; + bool last_packet_for_sbi_in_sbl = 0; + + DBG_PRINT(INFO_DBG, "CV, In cv_dev_submit_cmd()\n"); + + spin_lock(&dev->cv_lock); + + // Get length at beginning of the user_buffer + if (copy_from_user(&count, argp, sizeof(int))) { + DBG_PRINT(ERR_DBG,"Copy length failed in %s\n",__FUNCTION__); + retval = -EFAULT; + goto error; + } + + //FW Upgrade packets for SBI in SBL + if(count & FW_UPGRADE_IN_SBL_MASK) + { + //Clear the flag + count &= ~FW_UPGRADE_IN_SBL_MASK; + packet_for_sbi_in_sbl = 1; + } + else + { + packet_for_sbi_in_sbl = 0; + } + + if(count & FW_UPGRADE_IN_SBL_LAST_PKT_MASK) + { + DBG_PRINT(INFO_DBG,"Last pkt received in SBL\n"); + + //Clear the flag + count &= ~FW_UPGRADE_IN_SBL_LAST_PKT_MASK; + last_packet_for_sbi_in_sbl = 1; + } + else + { + last_packet_for_sbi_in_sbl = 0; + } + + DBG_PRINT(INFO_DBG,"Received write request of %d bytes\n",(int)count); + + if (count > OUT_BUFFER_SIZE_LIMIT) + { + DBG_PRINT(ERR_DBG,"Write request length %d too long in %s\n", count, __FUNCTION__); + retval = -EFAULT; + goto error; + } + + //wbuffer = (struct write_buffer *)KMalloc(sizeof(struct write_buffer) + OUT_BUFFER_SIZE - sizeof(unsigned char),GFP_ATOMIC); + wbuffer = (struct write_buffer *)KMalloc(sizeof(struct write_buffer) + count - sizeof(unsigned char),GFP_ATOMIC); + if(wbuffer == NULL) { + DBG_PRINT(ERR_DBG,"Failed to KMalloc memory in %s\n",__FUNCTION__); + retval = -ENOMEM; + goto error; + } + + if (copy_from_user(wbuffer, argp, sizeof(struct write_buffer) + count - sizeof(unsigned char))) { + DBG_PRINT(ERR_DBG,"Copy failed in %s\n",__FUNCTION__); + retval = -EFAULT; + KFree(wbuffer); + goto error; + } + + DBG_PRINT(INFO_DBG,"In %s\n",__FUNCTION__); + + + if(packet_for_sbi_in_sbl) + { + wbuffer->cmd_length &= ~FW_UPGRADE_IN_SBL_MASK; + } + + if(last_packet_for_sbi_in_sbl) + { + wbuffer->cmd_length &= ~FW_UPGRADE_IN_SBL_LAST_PKT_MASK; + } + + curr_buf_size = wbuffer->cmd_length; + curr_buf_ptr = wbuffer->cmd_buffer; + + /* Increase alloc size to avoid errors when size is too small here */ + dev->bulk_out_buffer = (char *)KMalloc(curr_buf_size < 512 ? 512 : curr_buf_size,GFP_ATOMIC); + dev->write_count = curr_buf_size; + + if (!dev->bulk_out_buffer) { + DBG_PRINT(ERR_DBG,"Failed to KMalloc memory in %s\n",__FUNCTION__); + retval = -ENOMEM; + KFree(wbuffer); + goto error; + } + + /* verify that we actually have some data to write */ + if (curr_buf_size == 0) { + DBG_PRINT(ERR_DBG,"The Length of data to be written is zero\n"); + KFree(wbuffer); + goto error; + } + + DBG_PRINT(INFO_DBG,"Current write request of %d bytes\n", curr_buf_size); + + memcpy(dev->bulk_out_buffer, curr_buf_ptr, curr_buf_size); + + DBG_PRINT(INFO_DBG, "Conforming write of \n"); + displayDump(curr_buf_size, dev->bulk_out_buffer); + + if(file->f_flags & O_NONBLOCK) { + + spin_unlock(&dev->cv_lock); + tasklet_init(&dev->cv_dev_tl, cv_dev_submit_cmd_tasklet, (unsigned long)dev); + tasklet_schedule(&dev->cv_dev_tl); + tasklet_kill(&dev->cv_dev_tl); + + msleep(200); + + retval = dev->actual_length; + if (copy_to_user((void __user *)rbuffer->cmd_buffer, dev->bulk_in_buffer,dev->actual_length)) { + DBG_PRINT(ERR_DBG,"Copy to user failed in %s\n", __FUNCTION__); + retval = -EFAULT; + } + + DBG_PRINT(INFO_DBG, "Date read from device \n"); + displayDump(dev->actual_length, dev->bulk_in_buffer); + goto error; + + } else { + + /* create a urb, and a buffer for it, and copy the data to the urb */ + dev->bulk_out_urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!dev->bulk_out_urb) { + DBG_PRINT(ERR_DBG,"Failed to allocate urb in %s\n", __FUNCTION__); + retval = -ENOMEM; + goto error; + } + + /* initialize the urb properly */ + usb_fill_bulk_urb(dev->bulk_out_urb, + dev->udev, + usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), + dev->bulk_out_buffer, + dev->write_count, + cv_dev_write_bulk_callback, + dev); + + // initialize bulk in parameters + memset(dev->bulk_in_buffer, '\0', IN_BUFFER_SIZE); + dev->actual_length = 0; + tx_bulk_in_sig = 0; // reset tx signal + rx_bulk_in_sig = 0; // reset rx signal + + /* send the data out the bulk port */ + retval = usb_submit_urb(dev->bulk_out_urb, GFP_ATOMIC); + if (retval) { + DBG_PRINT(ERR_DBG,"%s - failed submitting write urb, error %d\n", __FUNCTION__, retval); + goto error; + } + + DBG_PRINT(INFO_DBG, "URB Submitted OK, waiting for tx complete\n"); + if (!txCmdOk(dev)) { + DBG_PRINT(ERR_DBG,"Tx failed\n"); + retval = -EFAULT; + goto error; + } + + if(packet_for_sbi_in_sbl && !last_packet_for_sbi_in_sbl) + { + DBG_PRINT(INFO_DBG,"SBI load in SBL, no response from USH expected\n"); + retval = 0; + goto error; + } + } + + if(wbuffer != NULL) + KFree(wbuffer); + + if(!(file->f_flags & O_NONBLOCK)) + { + DBG_PRINT(INFO_DBG, "Transmitted command, waiting for rx\n"); + waitForResponse(dev); + if (dev->rx_status != 0) { + DBG_PRINT(ERR_DBG,"rx error: 0x%x\n", dev->rx_status); + retval = -EFAULT; + goto error; + } + + DBG_PRINT(INFO_DBG, "Completed the Bulk read request with %d bytes\n", dev->actual_length); + if (dev->actual_length == 0) { + DBG_PRINT(ERR_DBG,"failed to rx data\n"); + retval = -EFAULT; + goto error; + } + + if (copy_to_user((void __user *)rbuffer->cmd_buffer, dev->bulk_in_buffer, dev->actual_length)) { + DBG_PRINT(ERR_DBG,"Copy to user failed in %s\n", __FUNCTION__); + retval = -EFAULT; + goto error; + } + + DBG_PRINT(INFO_DBG, "Data read from device \n"); + displayDump(dev->actual_length, dev->bulk_in_buffer); + + retval = dev->actual_length; + } + +error: + + if (dev->bulk_out_buffer != NULL) { + KFree(dev->bulk_out_buffer); + dev->bulk_out_buffer=NULL; + } + + spin_unlock(&dev->cv_lock); + return retval; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +static int cv_dev_ioctl(struct inode *inode, struct file *file, unsigned int code, unsigned long value) +#else +/* Changed from static int to static long to get rid of compilation errors on Ubuntu 16.04 */ +static long cv_dev_ioctl(struct file *file, unsigned int code, unsigned long value) +#endif +{ + struct usb_cv *dev; + struct usb_interface *iface; + struct usb_device *udev; + int retval = 0; + + /* + * extract the type and number bitfields, and don't decode + * wrong cmds: return ENOTTY before verify_area() + */ + + DBG_PRINT(INFO_DBG,"CV, In cv_dev_ioctl()\n"); + + if(_IOC_TYPE(code) != CV_IOC_MAGIC) return -ENOTTY; + if(_IOC_NR(code) > CV_IOC_MAXNR) return -ENOTTY; + + dev = (struct usb_cv *)file->private_data; + iface = dev->interface; + udev = interface_to_usbdev (iface); + + switch(code) { + + case CV_SUBMIT_CMD: + DBG_PRINT(INFO_DBG,"Received CV_SUBMIT_COMMAND\n"); + if(dev->bulk_in_buffer) + { + memset(dev->bulk_in_buffer, '\0', IN_BUFFER_SIZE); + retval = cv_dev_submit_cmd(file, dev, (void __user *)value); + } + else + { + DBG_PRINT(ERR_DBG,"dev->bulk_in_buffer is NULL\n"); + retval = -EFAULT; + } + break; + + case CV_GET_COMMAND_STATUS: + DBG_PRINT(INFO_DBG,"Received CV_GET_COMMAND_STATUS\n"); + if(dev->bulk_in_buffer) + { + memset(dev->bulk_in_buffer, '\0', IN_BUFFER_SIZE); + retval = cv_dev_submit_cmd(file, dev, (void __user *)value); + } + else + { + DBG_PRINT(ERR_DBG,"dev->bulk_in_buffer is NULL\n"); + retval = -EFAULT; + } + break; + + default : + DBG_PRINT(ERR_DBG,"Unknown IOCTL request received\n"); + break; + } + + return retval; +} + + +static struct file_operations cv_dev_fops = { + .owner = THIS_MODULE, + .open = cv_dev_open, + //.write = cv_dev_write, + .release = cv_dev_release, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) + .ioctl = cv_dev_ioctl, +#else + /* compat_ioctl is for 32 bit user space applications. */ + /* .unlocked_ioctl = cv_dev_ioctl, */ + .compat_ioctl = cv_dev_ioctl, +#endif +}; + + +/* + * usb class driver info in order to get a minor number from the usb core, + * and to have the device registered with devfs and the driver core + */ + +static struct usb_class_driver cv_dev_class = { + .name = DEVICE_NAME"%d", + .fops = &cv_dev_fops, + //.mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, + .minor_base = USB_CV_MINOR_BASE, +}; + + +static int cv_dev_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_cv *dev = NULL; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + struct usb_device_descriptor descriptor; + int i; + int retval = -ENOMEM; + static int probe_on = 0; + int pipe; + + probe_on++; + + DBG_PRINT(INFO_DBG, "Interface Minor :%d\n",interface->minor); + DBG_PRINT(INFO_DBG, "BRCM CV_USB Device Driver First Cut \n"); + /* Comment out to get built with Ubuntu 16.04 */ + /* DBG_PRINT(INFO_DBG, "Built is %s %s\n",__DATE__,__TIME__); */ + + /* allocate memory for our device state and initialize it */ + dev = (struct usb_cv *)KMalloc(sizeof(struct usb_cv), GFP_ATOMIC); + if (dev == NULL) { + goto error; + } + + memset(dev, 0x00, sizeof (*dev)); + kref_init(&dev->kref); + + dev->udev = usb_get_dev(interface_to_usbdev(interface)); + dev->interface = interface; + descriptor = dev->udev->descriptor; + + /* Validate the VENDOR ID & PRODUCT ID */ + if ( (descriptor.idVendor != USB_CV_VENDOR_ID) || + ((descriptor.idProduct != USB_CV_PID_0000) && + (descriptor.idProduct != USB_CV_PID_5800) && + (descriptor.idProduct != USB_CV_PID_5801) && + (descriptor.idProduct != USB_CV_PID_5802) && + (descriptor.idProduct != USB_CV_PID_5804) && + (descriptor.idProduct != USB_CV_PID_5805) && + (descriptor.idProduct != USB_CV_PID_5821) && + (descriptor.idProduct != USB_CV_PID_5822) && + (descriptor.idProduct != USB_CV_PID_5823) && + (descriptor.idProduct != USB_CV_PID_5824) && + (descriptor.idProduct != USB_CV_PID_5825) && + (descriptor.idProduct != USB_CV_PID_5826) && + (descriptor.idProduct != USB_CV_PID_5830) && + (descriptor.idProduct != USB_CV_PID_5831) && + (descriptor.idProduct != USB_CV_PID_5832) && + (descriptor.idProduct != USB_CV_PID_5833) && + (descriptor.idProduct != USB_CV_PID_5834)) ) + { + /* This is not an error case, so returning ZERO */ + /* We need to free the memory allocated as this device is + no supported by this driver */ + KFree(dev); + DBG_PRINT(INFO_DBG, "Probe on CV Driver for VENDOR:%X\tPRODUCT:%X\n", + descriptor.idVendor,descriptor.idProduct); + return 0; + } + + if(descriptor.idProduct == USB_CV_PID_5830) + { + ush_is_in_sbl = 1; + reply_received_ush_is_in_sbl = 0; + } + else + { + ush_is_in_sbl = 0; + } + + /* set up the endpoint information */ + /* use only the first bulk-in and bulk-out endpoints */ + iface_desc = interface->cur_altsetting; + + DBG_PRINT(ERR_DBG, " bLength = 0x%02x \n bDescriptorType = 0x%02x \n bcdUSB = 0x%02x \n bDeviceClass = 0x%02x \n bDeviceProtocol = 0x%02x \n bMaxPacketSize = 0x%02x \n idVendor = 0x%02x \n idProduct = 0x%02x \n bcdDevice = 0x%02x \n iManufacturer = 0x%02x \n iProduct = 0x%02x \n iSerialNumber = 0x%02x \n bNumConfigurations = 0x%02x \n", + descriptor.bLength, descriptor.bDescriptorType, descriptor.bcdUSB, descriptor.bDeviceClass, + descriptor.bDeviceProtocol, descriptor.bMaxPacketSize0, descriptor.idVendor, descriptor.idProduct, + descriptor.bcdDevice, descriptor.iManufacturer, descriptor.iProduct, descriptor.iSerialNumber, + descriptor.bNumConfigurations); + + if( (descriptor.bDeviceClass) || (descriptor.bDeviceSubClass) || (descriptor.bDeviceProtocol)) { + DBG_PRINT(ERR_DBG, "Non Zero values of Class/SubClass/Protocol. Not a Composite device \n"); + } + + DBG_PRINT(INFO_DBG, " bNumEndpoints = %d\n", iface_desc->desc.bNumEndpoints); + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (!dev->bulk_in_endpointAddr && + (endpoint->bEndpointAddress & USB_DIR_IN) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) + { + /* we found a bulk in endpoint */ + DBG_PRINT(INFO_DBG, " found a bulk in endpoint %d\n", i); + dev->bulk_in_size = endpoint->wMaxPacketSize; + dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; + } + + /* Changed USB_DIR_IN to USB_DIR_OUT */ + if (!dev->bulk_out_endpointAddr && + !(endpoint->bEndpointAddress & USB_DIR_OUT) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) + { + /* we found a bulk out endpoint */ + DBG_PRINT(INFO_DBG, " found a bulk out endpoint %d\n", i); + dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; + dev->bulk_out_size = endpoint->wMaxPacketSize; + } + + if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) + { + DBG_PRINT(INFO_DBG, " found a interrupt endpoint %d\n", i); + dev->interrupt_in_endpoint = endpoint; + dev->interrupt_in_size = endpoint->wMaxPacketSize; + } + } + + if (probe_on%2 == 1) { + + if ( !(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) { + DBG_PRINT(ERR_DBG,"Could not find both bulk-in and bulk-out endpoints\n"); + goto error; + } + + if( !dev->interrupt_in_endpoint ) { + DBG_PRINT(ERR_DBG,"Could not find interrupt_in_endpoint \n"); + goto error; + } + + dev->interrupt_in_urb = usb_alloc_urb(0, GFP_ATOMIC); + + if (!dev->interrupt_in_urb) { + retval = -ENOMEM; + goto error; + } + + dev->interrupt_in_buffer = (unsigned char *)KMalloc(dev->interrupt_in_size, GFP_ATOMIC); + if (!dev->interrupt_in_buffer) { + retval = -ENOMEM; + usb_free_urb(dev->interrupt_in_urb); + goto error; + } + + memset(dev->interrupt_in_buffer, '\0',8); + + pipe = usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress); + + usb_fill_int_urb(dev->interrupt_in_urb, dev->udev,pipe, + dev->interrupt_in_buffer, 8, + cv_dev_read_interrupt_callback, dev, + dev->interrupt_in_endpoint->bInterval); + + retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC); + if (retval) { + retval = -ENOMEM; + DBG_PRINT(ERR_DBG,"%s - failed submitting interrupt urb, error %d\n", __FUNCTION__, retval); + goto error; + } + + dev->bulk_in_urb = usb_alloc_urb(0, GFP_ATOMIC); + + if (!dev->bulk_in_urb) { + retval = -ENOMEM; + DBG_PRINT(ERR_DBG,"%s - failed to alloc_urb %d\n", __FUNCTION__, retval); + goto error; + } + + dev->bulk_in_buffer = (unsigned char *)KMalloc(IN_BUFFER_SIZE, GFP_ATOMIC); + + if(!dev->bulk_in_buffer) { + retval = -ENOMEM; + DBG_PRINT(ERR_DBG,"%s - failed to allocate memory %d\n", __FUNCTION__, retval); + usb_free_urb(dev->bulk_in_urb); + goto error; + } + + memset(dev->bulk_in_buffer, '\0', IN_BUFFER_SIZE); + + usb_fill_bulk_urb(dev->bulk_in_urb, + dev->udev, + usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), + dev->bulk_in_buffer, + IN_BUFFER_SIZE, + cv_dev_read_bulk_callback, + dev); + + retval = usb_submit_urb(dev->bulk_in_urb, GFP_ATOMIC); + if (retval) { + DBG_PRINT(ERR_DBG,"%s - failed submitting bulk urb, error %d\n", __FUNCTION__, retval); + goto error; + } + + spin_lock_init(&dev->cv_lock); + + } + + /* 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, &cv_dev_class); + + if (retval) { + /* something prevented us from registering this driver */ + DBG_PRINT(ERR_DBG,"Not able to get a minor for this device.\n"); + usb_set_intfdata(interface, NULL); + goto error; + } + /* let the user know what node this device is now attached to */ + DBG_PRINT(ERR_DBG,"USB Credential Vault device now attached to USBCv-%d\n", interface->minor); + return 0; + +error: + if (dev) + kref_put(&dev->kref, cv_dev_delete); + return retval; +} + + +static void cv_dev_disconnect(struct usb_interface *interface) +{ + struct usb_cv *dev; + int minor = interface->minor; + + if(interface != NULL) { + + /* prevent cv_dev_open() from racing cv_dev_disconnect() */ + mutex_lock(&cv_dev_usb_mutex); + + dev = usb_get_intfdata(interface); + + if(dev!=NULL) { + + if(dev->interrupt_in_urb != NULL) + usb_kill_urb(dev->interrupt_in_urb); + + if(dev->interrupt_in_buffer != NULL) + KFree(dev->interrupt_in_buffer); + + if(dev->interrupt_in_urb != NULL) + usb_free_urb(dev->interrupt_in_urb); + + if(dev->bulk_in_urb != NULL) + usb_kill_urb(dev->bulk_in_urb); + + if(dev->bulk_in_buffer != NULL) + KFree(dev->bulk_in_buffer); + + if(dev->bulk_in_urb != NULL) + usb_free_urb(dev->bulk_in_urb); + + } + + usb_set_intfdata(interface, NULL); + /* give back our minor */ + usb_deregister_dev(interface, &cv_dev_class); + mutex_unlock(&cv_dev_usb_mutex); + + /* decrement our usage count */ + kref_put(&dev->kref, cv_dev_delete); + DBG_PRINT(ERR_DBG,"USB Credential Vault #%d now disconnected\n", minor); + } + +} + + +static struct usb_driver cv_dev_driver = { + .name = "Credential Vault", + .id_table = cv_dev_table, + .probe = cv_dev_probe, + .disconnect = cv_dev_disconnect, +}; + + +static int __init usb_cv_dev_init(void) +{ + int result; + + /* register this driver with the USB subsystem */ + result = usb_register(&cv_dev_driver); + if (result) + DBG_PRINT(ERR_DBG,"usb_register failed. Error number %d\n", result); + + return result; +} + + +static void __exit usb_cv_dev_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&cv_dev_driver); +} + +module_init (usb_cv_dev_init); +module_exit (usb_cv_dev_exit); + +module_param(interval, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(interval, "Overrides interrupt interval"); +MODULE_LICENSE("GPL"); diff --git a/ubuntu/usb-cv/usb-cv.h b/ubuntu/usb-cv/usb-cv.h new file mode 100644 index 0000000..65b2a16 --- /dev/null +++ b/ubuntu/usb-cv/usb-cv.h @@ -0,0 +1,192 @@ +/****************************************************************************** + * + * Copyright 2017 + * Broadcom Limited + * 1 Yishun Avenue 7, Singapore 768923, Singapore + * + *****************************************************************************/ +#ifndef _USB_CV_H_ +#define _USB_CV_H_ + +#ifdef __KERNEL__ +#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/usb.h> +#include <asm/uaccess.h> +#include <linux/version.h> +#include <linux/fs.h> + + +/* Define these values to match your devices */ +#define USB_CV_VENDOR_ID 0x0a5c + +#define USB_CV_PID_0000 0x0000 + +#define USB_CV_PID_5800 0x5800 +#define USB_CV_PID_5801 0x5801 +#define USB_CV_PID_5802 0x5802 +#define USB_CV_PID_5804 0x5804 +#define USB_CV_PID_5805 0x5805 +#define USB_CV_PID_5821 0x5821 +#define USB_CV_PID_5822 0x5822 +#define USB_CV_PID_5823 0x5823 +#define USB_CV_PID_5824 0x5824 +#define USB_CV_PID_5825 0x5825 +#define USB_CV_PID_5826 0x5826 +#define USB_CV_PID_5830 0x5830 +#define USB_CV_PID_5831 0x5831 +#define USB_CV_PID_5832 0x5832 +#define USB_CV_PID_5833 0x5833 +#define USB_CV_PID_5834 0x5834 +#endif + +#define CMD_BUF_SIZE 16*sizeof(unsigned char) + +#define CV_IOC_MAGIC 'k' + +#define CV_GET_LAST_COMMAND_STATUS _IOR(CV_IOC_MAGIC,1,int) + +#define CV_GET_LATEST_COMMAND_STATUS _IOR(CV_IOC_MAGIC,2,int) + +#define CV_GET_COMMAND_STATUS _IOR(CV_IOC_MAGIC,3,int) + +#define CV_GET_CONFIG_DESCRIPTOR _IOR(CV_IOC_MAGIC,4,int) + +#define CV_SUBMIT_CMD _IOW(CV_IOC_MAGIC,5,int) + +#define CV_BURN_FW _IOWR(CV_IOC_MAGIC,6,int) + +#define CV_HOST_CTL _IOWR(CV_IOC_MAGIC,7,int) + + +#define CV_IOC_MAXNR 7 + + +#define GET_STATUS 0 + +#define GET_DESCRIPTOR 0x06 + +#define GET_CONFIGURATION 0x08 + + +#define VENDOR_WRITE_REQUEST_TYPE 0x42 // 0100 0010 + +#define VENDOR_READ_REQUEST_TYPE 0xC2 // 1100 0010 + + +#define ERR_DBG 0 + +#define INIT_DBG 1 + +#define INFO_DBG 2 + +#define TX_DBG 3 + +#define INTR_DBG 4 + +typedef unsigned short cv_command_id; + +#define FW_UPGRADE_IN_SBL_MASK 0x80000000 +#define FW_UPGRADE_IN_SBL_LAST_PKT_MASK 0x40000000 + +/* Get a minor range for your devices from the usb maintainer */ +#define USB_CV_MINOR_BASE 192 +#define BUFFER_SIZE 4096 +#define IN_BUFFER_SIZE 99000 + +#define OUT_BUFFER_SIZE 4096 +#define OUT_BUFFER_SIZE_LIMIT 524288 /* 512 KB */ + +#define DISPLEN 0x08 + + +#define to_cv_dev(d) container_of(d, struct usb_cv, kref) + +#define DEVICE_NAME "usb/cv" + +#define MAX_WAIT_TX_TIME 10 +#define MAX_WAIT_RX_TIME 10 + +#ifdef __KERNEL__ +/* Structure to hold all of our device specific stuff */ +struct usb_cv { + spinlock_t cv_lock; + struct usb_device* udev; /* the usb device for this device */ + struct usb_interface* interface; /* the interface for this device */ + + size_t bulk_in_size; /* the size of the receive buffer */ + + size_t bulk_out_size; /* the size of the maximum transmit buffer */ + + size_t interrupt_in_size; /* the size of the interrupt buffer */ + + __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ + + __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ + + + + struct usb_endpoint_descriptor* interrupt_in_endpoint; + + + struct urb* interrupt_in_urb; + + struct urb* bulk_in_urb; + + struct urb* bulk_out_urb; + + struct urb* descriptor_in_urb; + + + + unsigned char* interrupt_in_buffer; + + unsigned char* bulk_out_buffer; + + unsigned char* bulk_in_buffer; + + unsigned char* descriptor_in_buffer; + + unsigned char* cmd_in_buffer; + + unsigned char* out_buffer; + + unsigned char* in_buffer; + + + + struct usb_ctrlrequest* cr_desc; + + struct usb_ctrlrequest* cr_submit_cmd; + + struct usb_ctrlrequest* cr_get_cmd; + + + + int probe_done; + + int actual_length; + + struct tasklet_struct cv_dev_tl; + + int write_count; + + struct kref kref; + + int tx_status; + + int rx_status; + +}; +#endif + +/* Global variable that defines the present debug level of the driver. */ +static int debug_level = ERR_DBG; +static int interval = 1; +#define DBG_PRINT(dbg_level, args...) if(!(debug_level<dbg_level)) printk(args) + +#endif /* _USB_CV_H_ */
BugLink: https://launchpad.net/bugs/1744041 Import usb-cv from Broadcom's release 4.8.005.2-1.0.0. Signed-off-by: Wen-chien Jesse Sung <jesse.sung@canonical.com> --- ubuntu/Makefile | 6 + ubuntu/usb-cv/Makefile | 1 + ubuntu/usb-cv/usb-cv.c | 1106 ++++++++++++++++++++++++++++++++++++++++++++++++ ubuntu/usb-cv/usb-cv.h | 192 +++++++++ 4 files changed, 1305 insertions(+) create mode 100644 ubuntu/usb-cv/Makefile create mode 100644 ubuntu/usb-cv/usb-cv.c create mode 100644 ubuntu/usb-cv/usb-cv.h