<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>i.MX Processors中的主题 Re: UVC Gadget Driver Issue</title>
    <link>https://community.nxp.com/t5/i-MX-Processors/UVC-Gadget-Driver-Issue/m-p/1613113#M202567</link>
    <description>&lt;P&gt;Hi Aldo,&lt;/P&gt;&lt;P&gt;Thanks for reply. I moved on the usb printer gadget driver and i am able to transfer data on default end points.&lt;/P&gt;&lt;P&gt;Further I have added two more endpoints and able to see in libusb output but not able to send/receive data so if you can help me to resolve this issue that would be really great help otherwise you can close the ticket.&lt;/P&gt;&lt;P&gt;Below is the driver&lt;/P&gt;&lt;P&gt;// SPDX-License-Identifier: GPL-2.0+&lt;BR /&gt;/*&lt;BR /&gt;* f_printer.c - USB printer function driver&lt;BR /&gt;*&lt;BR /&gt;* Copied from drivers/usb/gadget/legacy/printer.c,&lt;BR /&gt;* which was:&lt;BR /&gt;*&lt;BR /&gt;* printer.c -- Printer gadget driver&lt;BR /&gt;*&lt;BR /&gt;* Copyright (C) 2003-2005 David Brownell&lt;BR /&gt;* Copyright (C) 2006 Craig W. Nadler&lt;BR /&gt;*/&lt;/P&gt;&lt;P&gt;#include &amp;lt;linux/module.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/kernel.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/delay.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/ioport.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/sched.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/slab.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/mutex.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/errno.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/init.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/idr.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/timer.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/list.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/interrupt.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/device.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/moduleparam.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/fs.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/poll.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/types.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/ctype.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/cdev.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/kref.h&amp;gt;&lt;BR /&gt;#include &amp;lt;asm/byteorder.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/io.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/irq.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/uaccess.h&amp;gt;&lt;BR /&gt;#include &amp;lt;asm/unaligned.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/usb/ch9.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/usb/composite.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/usb/gadget.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/usb/g_printer.h&amp;gt;&lt;BR /&gt;#include "u_printer.h"&lt;/P&gt;&lt;P&gt;#define PRINTER_MINORS 4&lt;BR /&gt;#define GET_DEVICE_ID 0&lt;BR /&gt;#define GET_PORT_STATUS 1&lt;BR /&gt;#define SOFT_RESET 2&lt;/P&gt;&lt;P&gt;#define DEFAULT_Q_LEN 10 /* same as legacy g_printer gadget */&lt;/P&gt;&lt;P&gt;static int major, minors;&lt;BR /&gt;static struct class *usb_gadget_class;&lt;BR /&gt;static DEFINE_IDA(printer_ida);&lt;BR /&gt;static DEFINE_MUTEX(printer_ida_lock); /* protects access do printer_ida */&lt;/P&gt;&lt;P&gt;/*-------------------------------------------------------------------------*/&lt;/P&gt;&lt;P&gt;struct printer_dev {&lt;BR /&gt;spinlock_t lock; /* lock this structure */&lt;BR /&gt;/* lock buffer lists during read/write calls */&lt;BR /&gt;struct mutex lock_printer_io;&lt;BR /&gt;struct usb_gadget *gadget;&lt;BR /&gt;s8 interface;&lt;BR /&gt;struct usb_ep *in_ep, *out_ep;&lt;BR /&gt;struct kref kref;&lt;BR /&gt;struct list_head rx_reqs; /* List of free RX structs */&lt;BR /&gt;struct list_head rx_reqs_active; /* List of Active RX xfers */&lt;BR /&gt;struct list_head rx_buffers; /* List of completed xfers */&lt;BR /&gt;/* wait until there is data to be read. */&lt;BR /&gt;wait_queue_head_t rx_wait;&lt;BR /&gt;struct list_head tx_reqs; /* List of free TX structs */&lt;BR /&gt;struct list_head tx_reqs_active; /* List of Active TX xfers */&lt;BR /&gt;/* Wait until there are write buffers available to use. */&lt;BR /&gt;wait_queue_head_t tx_wait;&lt;BR /&gt;/* Wait until all write buffers have been sent. */&lt;BR /&gt;wait_queue_head_t tx_flush_wait;&lt;BR /&gt;struct usb_request *current_rx_req;&lt;BR /&gt;size_t current_rx_bytes;&lt;BR /&gt;u8 *current_rx_buf;&lt;BR /&gt;u8 printer_status;&lt;BR /&gt;u8 reset_printer;&lt;BR /&gt;int minor;&lt;BR /&gt;struct cdev printer_cdev;&lt;BR /&gt;u8 printer_cdev_open;&lt;BR /&gt;wait_queue_head_t wait;&lt;BR /&gt;unsigned q_len;&lt;BR /&gt;char *pnp_string; /* We don't own memory! */&lt;BR /&gt;struct usb_function function;&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static inline struct printer_dev *func_to_printer(struct usb_function *f)&lt;BR /&gt;{&lt;BR /&gt;return container_of(f, struct printer_dev, function);&lt;BR /&gt;}&lt;BR /&gt;/*-------------------------------------------------------------------------*/&lt;/P&gt;&lt;P&gt;/*&lt;BR /&gt;* DESCRIPTORS ... most are static, but strings and (full) configuration&lt;BR /&gt;* descriptors are built on demand.&lt;BR /&gt;*/&lt;/P&gt;&lt;P&gt;/* holds our biggest descriptor */&lt;BR /&gt;#define USB_DESC_BUFSIZE 256&lt;BR /&gt;#define USB_BUFSIZE 8192&lt;/P&gt;&lt;P&gt;static struct usb_interface_descriptor intf_desc = {&lt;BR /&gt;.bLength = sizeof(intf_desc),&lt;BR /&gt;.bDescriptorType = USB_DT_INTERFACE,&lt;BR /&gt;.bNumEndpoints = 4,&lt;BR /&gt;.bInterfaceClass = USB_CLASS_PER_INTERFACE,&lt;BR /&gt;.bInterfaceSubClass = USB_CLASS_PER_INTERFACE, /* Printer Sub-Class */&lt;BR /&gt;.bInterfaceProtocol = USB_CLASS_PER_INTERFACE, /* Bi-Directional */&lt;BR /&gt;.iInterface = 0&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_endpoint_descriptor fs_ep_in_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bEndpointAddress = USB_DIR_IN,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_endpoint_descriptor fs_ep_out_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bEndpointAddress = USB_DIR_OUT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_endpoint_descriptor fs_ep1_in_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.bEndpointAddress = USB_DIR_IN | 0x02&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_endpoint_descriptor fs_ep1_out_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.bEndpointAddress = USB_DIR_OUT | 0x02&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_descriptor_header *fs_printer_function[] = {&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;intf_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;fs_ep_in_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;fs_ep_out_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;fs_ep1_in_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;fs_ep1_out_desc,&lt;BR /&gt;NULL&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;/*&lt;BR /&gt;* usb 2.0 devices need to expose both high speed and full speed&lt;BR /&gt;* descriptors, unless they only run at full speed.&lt;BR /&gt;*/&lt;/P&gt;&lt;P&gt;static struct usb_endpoint_descriptor hs_ep_in_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.bEndpointAddress = USB_DIR_IN,&lt;BR /&gt;.wMaxPacketSize = cpu_to_le16(512)&lt;BR /&gt;};&lt;BR /&gt;//Commeted this to just get he ep1_out&lt;BR /&gt;static struct usb_endpoint_descriptor hs_ep_out_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.bEndpointAddress = USB_DIR_OUT,&lt;BR /&gt;.wMaxPacketSize = cpu_to_le16(512)&lt;BR /&gt;};&lt;BR /&gt;static struct usb_endpoint_descriptor hs_ep1_in_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.bEndpointAddress = USB_DIR_IN | 0x02,&lt;BR /&gt;.wMaxPacketSize = cpu_to_le16(512)&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_endpoint_descriptor hs_ep1_out_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.bEndpointAddress = USB_DIR_OUT | 0x02,&lt;BR /&gt;.wMaxPacketSize = cpu_to_le16(512)&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_descriptor_header *hs_printer_function[] = {&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;intf_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;hs_ep_in_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;hs_ep_out_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;hs_ep1_in_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;hs_ep1_out_desc,&lt;BR /&gt;NULL&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;/*&lt;BR /&gt;* Added endpoint descriptors for 3.0 devices&lt;BR /&gt;*/&lt;/P&gt;&lt;P&gt;static struct usb_endpoint_descriptor ss_ep_in_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.wMaxPacketSize = cpu_to_le16(1024),&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = {&lt;BR /&gt;.bLength = sizeof(ss_ep_in_comp_desc),&lt;BR /&gt;.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;&lt;BR /&gt;static struct usb_endpoint_descriptor ss_ep_out_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.wMaxPacketSize = cpu_to_le16(1024),&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = {&lt;BR /&gt;.bLength = sizeof(ss_ep_out_comp_desc),&lt;BR /&gt;.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_endpoint_descriptor ss_ep1_in_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.bEndpointAddress = USB_DIR_IN | 0x02,&lt;BR /&gt;.wMaxPacketSize = cpu_to_le16(1024)&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_ss_ep_comp_descriptor ss_ep1_in_comp_desc = {&lt;BR /&gt;.bLength = sizeof(ss_ep1_in_comp_desc),&lt;BR /&gt;.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;&lt;BR /&gt;static struct usb_endpoint_descriptor ss_ep1_out_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.bEndpointAddress = USB_DIR_OUT | 0x02,&lt;BR /&gt;.wMaxPacketSize = cpu_to_le16(1024)&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_ss_ep_comp_descriptor ss_ep1_out_comp_desc = {&lt;BR /&gt;.bLength = sizeof(ss_ep1_out_comp_desc),&lt;BR /&gt;.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_descriptor_header *ss_printer_function[] = {&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;intf_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;ss_ep_in_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;ss_ep_in_comp_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;ss_ep_out_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;ss_ep_out_comp_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;ss_ep1_in_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;ss_ep1_in_comp_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;ss_ep1_out_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;ss_ep1_out_comp_desc,&lt;BR /&gt;NULL&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;/* maxpacket and other transfer characteristics vary by speed. */&lt;BR /&gt;static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget,&lt;BR /&gt;struct usb_endpoint_descriptor *fs,&lt;BR /&gt;struct usb_endpoint_descriptor *hs,&lt;BR /&gt;struct usb_endpoint_descriptor *ss)&lt;BR /&gt;{&lt;BR /&gt;switch (gadget-&amp;gt;speed) {&lt;BR /&gt;case USB_SPEED_SUPER:&lt;BR /&gt;return ss;&lt;BR /&gt;case USB_SPEED_HIGH:&lt;BR /&gt;return hs;&lt;BR /&gt;default:&lt;BR /&gt;return fs;&lt;BR /&gt;}&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/*-------------------------------------------------------------------------*/&lt;/P&gt;&lt;P&gt;static void printer_dev_free(struct kref *kref)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = container_of(kref, struct printer_dev, kref);&lt;/P&gt;&lt;P&gt;kfree(dev);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static struct usb_request *&lt;BR /&gt;printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags)&lt;BR /&gt;{&lt;BR /&gt;struct usb_request *req;&lt;/P&gt;&lt;P&gt;req = usb_ep_alloc_request(ep, gfp_flags);&lt;/P&gt;&lt;P&gt;if (req != NULL) {&lt;BR /&gt;req-&amp;gt;length = len;&lt;BR /&gt;req-&amp;gt;buf = kmalloc(len, gfp_flags);&lt;BR /&gt;if (req-&amp;gt;buf == NULL) {&lt;BR /&gt;usb_ep_free_request(ep, req);&lt;BR /&gt;return NULL;&lt;BR /&gt;}&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;return req;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void&lt;BR /&gt;printer_req_free(struct usb_ep *ep, struct usb_request *req)&lt;BR /&gt;{&lt;BR /&gt;if (ep != NULL &amp;amp;&amp;amp; req != NULL) {&lt;BR /&gt;kfree(req-&amp;gt;buf);&lt;BR /&gt;usb_ep_free_request(ep, req);&lt;BR /&gt;}&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/*-------------------------------------------------------------------------*/&lt;/P&gt;&lt;P&gt;static void rx_complete(struct usb_ep *ep, struct usb_request *req)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = ep-&amp;gt;driver_data;&lt;BR /&gt;int status = req-&amp;gt;status;&lt;BR /&gt;unsigned long flags;&lt;/P&gt;&lt;P&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;list_del_init(&amp;amp;req-&amp;gt;list); /* Remode from Active List */&lt;/P&gt;&lt;P&gt;switch (status) {&lt;/P&gt;&lt;P&gt;/* normal completion */&lt;BR /&gt;case 0:&lt;BR /&gt;if (req-&amp;gt;actual &amp;gt; 0) {&lt;BR /&gt;list_add_tail(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_buffers);&lt;BR /&gt;DBG(dev, "G_Printer : rx length %d\n", req-&amp;gt;actual);&lt;BR /&gt;} else {&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;}&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;/* software-driven interface shutdown */&lt;BR /&gt;case -ECONNRESET: /* unlink */&lt;BR /&gt;case -ESHUTDOWN: /* disconnect etc */&lt;BR /&gt;VDBG(dev, "rx shutdown, code %d\n", status);&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;/* for hardware automagic (such as pxa) */&lt;BR /&gt;case -ECONNABORTED: /* endpoint reset */&lt;BR /&gt;DBG(dev, "rx %s reset\n", ep-&amp;gt;name);&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;/* data overrun */&lt;BR /&gt;case -EOVERFLOW:&lt;BR /&gt;fallthrough;&lt;/P&gt;&lt;P&gt;default:&lt;BR /&gt;DBG(dev, "rx status %d\n", status);&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;break;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;wake_up_interruptible(&amp;amp;dev-&amp;gt;rx_wait);&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void tx_complete(struct usb_ep *ep, struct usb_request *req)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = ep-&amp;gt;driver_data;&lt;/P&gt;&lt;P&gt;switch (req-&amp;gt;status) {&lt;BR /&gt;default:&lt;BR /&gt;VDBG(dev, "tx err %d\n", req-&amp;gt;status);&lt;BR /&gt;fallthrough;&lt;BR /&gt;case -ECONNRESET: /* unlink */&lt;BR /&gt;case -ESHUTDOWN: /* disconnect etc */&lt;BR /&gt;break;&lt;BR /&gt;case 0:&lt;BR /&gt;break;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;spin_lock(&amp;amp;dev-&amp;gt;lock);&lt;BR /&gt;/* Take the request struct off the active list and put it on the&lt;BR /&gt;* free list.&lt;BR /&gt;*/&lt;BR /&gt;list_del_init(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;tx_reqs);&lt;BR /&gt;wake_up_interruptible(&amp;amp;dev-&amp;gt;tx_wait);&lt;BR /&gt;if (likely(list_empty(&amp;amp;dev-&amp;gt;tx_reqs_active)))&lt;BR /&gt;wake_up_interruptible(&amp;amp;dev-&amp;gt;tx_flush_wait);&lt;/P&gt;&lt;P&gt;spin_unlock(&amp;amp;dev-&amp;gt;lock);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/*-------------------------------------------------------------------------*/&lt;/P&gt;&lt;P&gt;static int&lt;BR /&gt;printer_open(struct inode *inode, struct file *fd)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev;&lt;BR /&gt;unsigned long flags;&lt;BR /&gt;int ret = -EBUSY;&lt;/P&gt;&lt;P&gt;dev = container_of(inode-&amp;gt;i_cdev, struct printer_dev, printer_cdev);&lt;/P&gt;&lt;P&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;interface &amp;lt; 0) {&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;return -ENODEV;&lt;BR /&gt;}&lt;BR /&gt;printk(KERN_INFO "Opening Sonosa Driver=================\n");&lt;BR /&gt;if (!dev-&amp;gt;printer_cdev_open) {&lt;BR /&gt;dev-&amp;gt;printer_cdev_open = 1;&lt;BR /&gt;fd-&amp;gt;private_data = dev;&lt;BR /&gt;ret = 0;&lt;BR /&gt;/* Change the printer status to show that it's on-line. */&lt;BR /&gt;dev-&amp;gt;printer_status |= PRINTER_SELECTED;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;kref_get(&amp;amp;dev-&amp;gt;kref);&lt;BR /&gt;DBG(dev, "Sonosa_open returned %x\n", ret);&lt;BR /&gt;return ret;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static int&lt;BR /&gt;printer_close(struct inode *inode, struct file *fd)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = fd-&amp;gt;private_data;&lt;BR /&gt;unsigned long flags;&lt;/P&gt;&lt;P&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;dev-&amp;gt;printer_cdev_open = 0;&lt;BR /&gt;fd-&amp;gt;private_data = NULL;&lt;BR /&gt;/* Change printer status to show that the printer is off-line. */&lt;BR /&gt;dev-&amp;gt;printer_status &amp;amp;= ~PRINTER_SELECTED;&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;kref_put(&amp;amp;dev-&amp;gt;kref, printer_dev_free);&lt;BR /&gt;DBG(dev, "Sonosa_close\n");&lt;/P&gt;&lt;P&gt;return 0;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* This function must be called with interrupts turned off. */&lt;BR /&gt;static void&lt;BR /&gt;setup_rx_reqs(struct printer_dev *dev)&lt;BR /&gt;{&lt;BR /&gt;struct usb_request *req;&lt;/P&gt;&lt;P&gt;while (likely(!list_empty(&amp;amp;dev-&amp;gt;rx_reqs))) {&lt;BR /&gt;int error;&lt;/P&gt;&lt;P&gt;req = container_of(dev-&amp;gt;rx_reqs.next,&lt;BR /&gt;struct usb_request, list);&lt;BR /&gt;list_del_init(&amp;amp;req-&amp;gt;list);&lt;/P&gt;&lt;P&gt;/* The USB Host sends us whatever amount of data it wants to&lt;BR /&gt;* so we always set the length field to the full USB_BUFSIZE.&lt;BR /&gt;* If the amount of data is more than the read() caller asked&lt;BR /&gt;* for it will be stored in the request buffer until it is&lt;BR /&gt;* asked for by read().&lt;BR /&gt;*/&lt;BR /&gt;req-&amp;gt;length = USB_BUFSIZE;&lt;BR /&gt;req-&amp;gt;complete = rx_complete;&lt;/P&gt;&lt;P&gt;/* here, we unlock, and only unlock, to avoid deadlock. */&lt;BR /&gt;spin_unlock(&amp;amp;dev-&amp;gt;lock);&lt;BR /&gt;error = usb_ep_queue(dev-&amp;gt;out_ep, req, GFP_ATOMIC);&lt;BR /&gt;spin_lock(&amp;amp;dev-&amp;gt;lock);&lt;BR /&gt;if (error) {&lt;BR /&gt;DBG(dev, "rx submit --&amp;gt; %d\n", error);&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;break;&lt;BR /&gt;}&lt;BR /&gt;/* if the req is empty, then add it into dev-&amp;gt;rx_reqs_active. */&lt;BR /&gt;else if (list_empty(&amp;amp;req-&amp;gt;list))&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs_active);&lt;BR /&gt;}&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static ssize_t&lt;BR /&gt;printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = fd-&amp;gt;private_data;&lt;BR /&gt;unsigned long flags;&lt;BR /&gt;size_t size;&lt;BR /&gt;size_t bytes_copied;&lt;BR /&gt;struct usb_request *req;&lt;BR /&gt;/* This is a pointer to the current USB rx request. */&lt;BR /&gt;struct usb_request *current_rx_req;&lt;BR /&gt;/* This is the number of bytes in the current rx buffer. */&lt;BR /&gt;size_t current_rx_bytes;&lt;BR /&gt;/* This is a pointer to the current rx buffer. */&lt;BR /&gt;u8 *current_rx_buf;&lt;/P&gt;&lt;P&gt;if (len == 0)&lt;BR /&gt;return -EINVAL;&lt;/P&gt;&lt;P&gt;DBG(dev, "sonosa_read trying to read %d bytes\n", (int)len);&lt;BR /&gt;printk(KERN_INFO "Sonosa Read=================:\n");&lt;BR /&gt;mutex_lock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;interface &amp;lt; 0) {&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return -ENODEV;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* We will use this flag later to check if a printer reset happened&lt;BR /&gt;* after we turn interrupts back on.&lt;BR /&gt;*/&lt;BR /&gt;dev-&amp;gt;reset_printer = 0;&lt;/P&gt;&lt;P&gt;setup_rx_reqs(dev);&lt;/P&gt;&lt;P&gt;bytes_copied = 0;&lt;BR /&gt;current_rx_req = dev-&amp;gt;current_rx_req;&lt;BR /&gt;current_rx_bytes = dev-&amp;gt;current_rx_bytes;&lt;BR /&gt;current_rx_buf = dev-&amp;gt;current_rx_buf;&lt;BR /&gt;dev-&amp;gt;current_rx_req = NULL;&lt;BR /&gt;dev-&amp;gt;current_rx_bytes = 0;&lt;BR /&gt;dev-&amp;gt;current_rx_buf = NULL;&lt;/P&gt;&lt;P&gt;/* Check if there is any data in the read buffers. Please note that&lt;BR /&gt;* current_rx_bytes is the number of bytes in the current rx buffer.&lt;BR /&gt;* If it is zero then check if there are any other rx_buffers that&lt;BR /&gt;* are on the completed list. We are only out of data if all rx&lt;BR /&gt;* buffers are empty.&lt;BR /&gt;*/&lt;BR /&gt;if ((current_rx_bytes == 0) &amp;amp;&amp;amp;&lt;BR /&gt;(likely(list_empty(&amp;amp;dev-&amp;gt;rx_buffers)))) {&lt;BR /&gt;/* Turn interrupts back on before sleeping. */&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;/*&lt;BR /&gt;* If no data is available check if this is a NON-Blocking&lt;BR /&gt;* call or not.&lt;BR /&gt;*/&lt;BR /&gt;if (fd-&amp;gt;f_flags &amp;amp; (O_NONBLOCK|O_NDELAY)) {&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return -EAGAIN;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* Sleep until data is available */&lt;BR /&gt;wait_event_interruptible(dev-&amp;gt;rx_wait,&lt;BR /&gt;(likely(!list_empty(&amp;amp;dev-&amp;gt;rx_buffers))));&lt;BR /&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* We have data to return then copy it to the caller's buffer.*/&lt;BR /&gt;while ((current_rx_bytes || likely(!list_empty(&amp;amp;dev-&amp;gt;rx_buffers)))&lt;BR /&gt;&amp;amp;&amp;amp; len) {&lt;BR /&gt;if (current_rx_bytes == 0) {&lt;BR /&gt;req = container_of(dev-&amp;gt;rx_buffers.next,&lt;BR /&gt;struct usb_request, list);&lt;BR /&gt;list_del_init(&amp;amp;req-&amp;gt;list);&lt;/P&gt;&lt;P&gt;if (req-&amp;gt;actual &amp;amp;&amp;amp; req-&amp;gt;buf) {&lt;BR /&gt;current_rx_req = req;&lt;BR /&gt;current_rx_bytes = req-&amp;gt;actual;&lt;BR /&gt;current_rx_buf = req-&amp;gt;buf;&lt;BR /&gt;} else {&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;continue;&lt;BR /&gt;}&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* Don't leave irqs off while doing memory copies */&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (len &amp;gt; current_rx_bytes)&lt;BR /&gt;size = current_rx_bytes;&lt;BR /&gt;else&lt;BR /&gt;size = len;&lt;/P&gt;&lt;P&gt;size -= copy_to_user(buf, current_rx_buf, size);&lt;BR /&gt;bytes_copied += size;&lt;BR /&gt;len -= size;&lt;BR /&gt;buf += size;&lt;/P&gt;&lt;P&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;/* We've disconnected or reset so return. */&lt;BR /&gt;if (dev-&amp;gt;reset_printer) {&lt;BR /&gt;list_add(&amp;amp;current_rx_req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return -EAGAIN;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* If we not returning all the data left in this RX request&lt;BR /&gt;* buffer then adjust the amount of data left in the buffer.&lt;BR /&gt;* Othewise if we are done with this RX request buffer then&lt;BR /&gt;* requeue it to get any incoming data from the USB host.&lt;BR /&gt;*/&lt;BR /&gt;if (size &amp;lt; current_rx_bytes) {&lt;BR /&gt;current_rx_bytes -= size;&lt;BR /&gt;current_rx_buf += size;&lt;BR /&gt;} else {&lt;BR /&gt;list_add(&amp;amp;current_rx_req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;current_rx_bytes = 0;&lt;BR /&gt;current_rx_buf = NULL;&lt;BR /&gt;current_rx_req = NULL;&lt;BR /&gt;}&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;dev-&amp;gt;current_rx_req = current_rx_req;&lt;BR /&gt;dev-&amp;gt;current_rx_bytes = current_rx_bytes;&lt;BR /&gt;dev-&amp;gt;current_rx_buf = current_rx_buf;&lt;/P&gt;&lt;P&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;/P&gt;&lt;P&gt;DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied);&lt;/P&gt;&lt;P&gt;if (bytes_copied)&lt;BR /&gt;return bytes_copied;&lt;BR /&gt;else&lt;BR /&gt;return -EAGAIN;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static ssize_t&lt;BR /&gt;printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = fd-&amp;gt;private_data;&lt;BR /&gt;unsigned long flags;&lt;BR /&gt;size_t size; /* Amount of data in a TX request. */&lt;BR /&gt;size_t bytes_copied = 0;&lt;BR /&gt;struct usb_request *req;&lt;BR /&gt;int value;&lt;/P&gt;&lt;P&gt;DBG(dev, "printer_write trying to send %d bytes\n", (int)len);&lt;/P&gt;&lt;P&gt;if (len == 0)&lt;BR /&gt;return -EINVAL;&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;interface &amp;lt; 0) {&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return -ENODEV;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* Check if a printer reset happens while we have interrupts on */&lt;BR /&gt;dev-&amp;gt;reset_printer = 0;&lt;/P&gt;&lt;P&gt;/* Check if there is any available write buffers */&lt;BR /&gt;if (likely(list_empty(&amp;amp;dev-&amp;gt;tx_reqs))) {&lt;BR /&gt;/* Turn interrupts back on before sleeping. */&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;/*&lt;BR /&gt;* If write buffers are available check if this is&lt;BR /&gt;* a NON-Blocking call or not.&lt;BR /&gt;*/&lt;BR /&gt;if (fd-&amp;gt;f_flags &amp;amp; (O_NONBLOCK|O_NDELAY)) {&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return -EAGAIN;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* Sleep until a write buffer is available */&lt;BR /&gt;wait_event_interruptible(dev-&amp;gt;tx_wait,&lt;BR /&gt;(likely(!list_empty(&amp;amp;dev-&amp;gt;tx_reqs))));&lt;BR /&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;while (likely(!list_empty(&amp;amp;dev-&amp;gt;tx_reqs)) &amp;amp;&amp;amp; len) {&lt;/P&gt;&lt;P&gt;if (len &amp;gt; USB_BUFSIZE)&lt;BR /&gt;size = USB_BUFSIZE;&lt;BR /&gt;else&lt;BR /&gt;size = len;&lt;/P&gt;&lt;P&gt;req = container_of(dev-&amp;gt;tx_reqs.next, struct usb_request,&lt;BR /&gt;list);&lt;BR /&gt;list_del_init(&amp;amp;req-&amp;gt;list);&lt;/P&gt;&lt;P&gt;req-&amp;gt;complete = tx_complete;&lt;BR /&gt;req-&amp;gt;length = size;&lt;/P&gt;&lt;P&gt;/* Check if we need to send a zero length packet. */&lt;BR /&gt;if (len &amp;gt; size)&lt;BR /&gt;/* They will be more TX requests so no yet. */&lt;BR /&gt;req-&amp;gt;zero = 0;&lt;BR /&gt;else&lt;BR /&gt;/* If the data amount is not a multiple of the&lt;BR /&gt;* maxpacket size then send a zero length packet.&lt;BR /&gt;*/&lt;BR /&gt;req-&amp;gt;zero = ((len % dev-&amp;gt;in_ep-&amp;gt;maxpacket) == 0);&lt;/P&gt;&lt;P&gt;/* Don't leave irqs off while doing memory copies */&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (copy_from_user(req-&amp;gt;buf, buf, size)) {&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;tx_reqs);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return bytes_copied;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;bytes_copied += size;&lt;BR /&gt;len -= size;&lt;BR /&gt;buf += size;&lt;/P&gt;&lt;P&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;/* We've disconnected or reset so free the req and buffer */&lt;BR /&gt;if (dev-&amp;gt;reset_printer) {&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;tx_reqs);&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return -EAGAIN;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;tx_reqs_active);&lt;/P&gt;&lt;P&gt;/* here, we unlock, and only unlock, to avoid deadlock. */&lt;BR /&gt;spin_unlock(&amp;amp;dev-&amp;gt;lock);&lt;BR /&gt;value = usb_ep_queue(dev-&amp;gt;in_ep, req, GFP_ATOMIC);&lt;BR /&gt;spin_lock(&amp;amp;dev-&amp;gt;lock);&lt;BR /&gt;if (value) {&lt;BR /&gt;list_move(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;tx_reqs);&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return -EAGAIN;&lt;BR /&gt;}&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;/P&gt;&lt;P&gt;DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied);&lt;/P&gt;&lt;P&gt;if (bytes_copied)&lt;BR /&gt;return bytes_copied;&lt;BR /&gt;else&lt;BR /&gt;return -EAGAIN;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static int&lt;BR /&gt;printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = fd-&amp;gt;private_data;&lt;BR /&gt;struct inode *inode = file_inode(fd);&lt;BR /&gt;unsigned long flags;&lt;BR /&gt;int tx_list_empty;&lt;/P&gt;&lt;P&gt;inode_lock(inode);&lt;BR /&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;interface &amp;lt; 0) {&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;inode_unlock(inode);&lt;BR /&gt;return -ENODEV;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;tx_list_empty = (likely(list_empty(&amp;amp;dev-&amp;gt;tx_reqs)));&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (!tx_list_empty) {&lt;BR /&gt;/* Sleep until all data has been sent */&lt;BR /&gt;wait_event_interruptible(dev-&amp;gt;tx_flush_wait,&lt;BR /&gt;(likely(list_empty(&amp;amp;dev-&amp;gt;tx_reqs_active))));&lt;BR /&gt;}&lt;BR /&gt;inode_unlock(inode);&lt;/P&gt;&lt;P&gt;return 0;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static __poll_t&lt;BR /&gt;printer_poll(struct file *fd, poll_table *wait)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = fd-&amp;gt;private_data;&lt;BR /&gt;unsigned long flags;&lt;BR /&gt;__poll_t status = 0;&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;interface &amp;lt; 0) {&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return EPOLLERR | EPOLLHUP;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;setup_rx_reqs(dev);&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;/P&gt;&lt;P&gt;poll_wait(fd, &amp;amp;dev-&amp;gt;rx_wait, wait);&lt;BR /&gt;poll_wait(fd, &amp;amp;dev-&amp;gt;tx_wait, wait);&lt;/P&gt;&lt;P&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;if (likely(!list_empty(&amp;amp;dev-&amp;gt;tx_reqs)))&lt;BR /&gt;status |= EPOLLOUT | EPOLLWRNORM;&lt;/P&gt;&lt;P&gt;if (likely(dev-&amp;gt;current_rx_bytes) ||&lt;BR /&gt;likely(!list_empty(&amp;amp;dev-&amp;gt;rx_buffers)))&lt;BR /&gt;status |= EPOLLIN | EPOLLRDNORM;&lt;/P&gt;&lt;P&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;return status;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static long&lt;BR /&gt;printer_ioctl(struct file *fd, unsigned int code, unsigned long arg)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = fd-&amp;gt;private_data;&lt;BR /&gt;unsigned long flags;&lt;BR /&gt;int status = 0;&lt;/P&gt;&lt;P&gt;DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg);&lt;/P&gt;&lt;P&gt;/* handle ioctls */&lt;/P&gt;&lt;P&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;interface &amp;lt; 0) {&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;return -ENODEV;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;switch (code) {&lt;BR /&gt;case GADGET_GET_PRINTER_STATUS:&lt;BR /&gt;status = (int)dev-&amp;gt;printer_status;&lt;BR /&gt;break;&lt;BR /&gt;case GADGET_SET_PRINTER_STATUS:&lt;BR /&gt;dev-&amp;gt;printer_status = (u8)arg;&lt;BR /&gt;break;&lt;BR /&gt;default:&lt;BR /&gt;/* could not handle ioctl */&lt;BR /&gt;DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n",&lt;BR /&gt;code);&lt;BR /&gt;status = -ENOTTY;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;return status;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* used after endpoint configuration */&lt;BR /&gt;static const struct file_operations printer_io_operations = {&lt;BR /&gt;.owner = THIS_MODULE,&lt;BR /&gt;.open = printer_open,&lt;BR /&gt;.read = printer_read,&lt;BR /&gt;.write = printer_write,&lt;BR /&gt;.fsync = printer_fsync,&lt;BR /&gt;.poll = printer_poll,&lt;BR /&gt;.unlocked_ioctl = printer_ioctl,&lt;BR /&gt;.release = printer_close,&lt;BR /&gt;.llseek = noop_llseek,&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;/*-------------------------------------------------------------------------*/&lt;/P&gt;&lt;P&gt;static int&lt;BR /&gt;set_printer_interface(struct printer_dev *dev)&lt;BR /&gt;{&lt;BR /&gt;int result = 0;&lt;/P&gt;&lt;P&gt;dev-&amp;gt;in_ep-&amp;gt;desc = ep_desc(dev-&amp;gt;gadget, &amp;amp;fs_ep_in_desc, &amp;amp;hs_ep_in_desc,&lt;BR /&gt;&amp;amp;ss_ep_in_desc);&lt;BR /&gt;dev-&amp;gt;in_ep-&amp;gt;driver_data = dev;&lt;/P&gt;&lt;P&gt;dev-&amp;gt;out_ep-&amp;gt;desc = ep_desc(dev-&amp;gt;gadget, &amp;amp;fs_ep_out_desc,&lt;BR /&gt;&amp;amp;hs_ep1_out_desc,&amp;amp;ss_ep_out_desc);&lt;BR /&gt;dev-&amp;gt;out_ep-&amp;gt;driver_data = dev;&lt;/P&gt;&lt;P&gt;result = usb_ep_enable(dev-&amp;gt;in_ep);&lt;BR /&gt;if (result != 0) {&lt;BR /&gt;DBG(dev, "enable %s --&amp;gt; %d\n", dev-&amp;gt;in_ep-&amp;gt;name, result);&lt;BR /&gt;printk(KERN_INFO, "Enabling Endpoints+++++++++++++++++++");&lt;BR /&gt;goto done;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;result = usb_ep_enable(dev-&amp;gt;out_ep);&lt;BR /&gt;if (result != 0) {&lt;BR /&gt;DBG(dev, "enable %s --&amp;gt; %d\n", dev-&amp;gt;out_ep-&amp;gt;name, result);&lt;BR /&gt;goto done;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;done:&lt;BR /&gt;/* on error, disable any endpoints */&lt;BR /&gt;if (result != 0) {&lt;BR /&gt;(void) usb_ep_disable(dev-&amp;gt;in_ep);&lt;BR /&gt;(void) usb_ep_disable(dev-&amp;gt;out_ep);&lt;BR /&gt;dev-&amp;gt;in_ep-&amp;gt;desc = NULL;&lt;BR /&gt;dev-&amp;gt;out_ep-&amp;gt;desc = NULL;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* caller is responsible for cleanup on error */&lt;BR /&gt;return result;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void printer_reset_interface(struct printer_dev *dev)&lt;BR /&gt;{&lt;BR /&gt;unsigned long flags;&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;interface &amp;lt; 0)&lt;BR /&gt;return;&lt;/P&gt;&lt;P&gt;DBG(dev, "%s\n", __func__);&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;in_ep-&amp;gt;desc)&lt;BR /&gt;usb_ep_disable(dev-&amp;gt;in_ep);&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;out_ep-&amp;gt;desc)&lt;BR /&gt;usb_ep_disable(dev-&amp;gt;out_ep);&lt;/P&gt;&lt;P&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;dev-&amp;gt;in_ep-&amp;gt;desc = NULL;&lt;BR /&gt;dev-&amp;gt;out_ep-&amp;gt;desc = NULL;&lt;BR /&gt;dev-&amp;gt;interface = -1;&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* Change our operational Interface. */&lt;BR /&gt;static int set_interface(struct printer_dev *dev, unsigned number)&lt;BR /&gt;{&lt;BR /&gt;int result = 0;&lt;/P&gt;&lt;P&gt;/* Free the current interface */&lt;BR /&gt;printer_reset_interface(dev);&lt;/P&gt;&lt;P&gt;result = set_printer_interface(dev);&lt;BR /&gt;if (result)&lt;BR /&gt;printer_reset_interface(dev);&lt;BR /&gt;else&lt;BR /&gt;dev-&amp;gt;interface = number;&lt;/P&gt;&lt;P&gt;if (!result)&lt;BR /&gt;INFO(dev, "Using interface %x\n", number);&lt;/P&gt;&lt;P&gt;return result;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void printer_soft_reset(struct printer_dev *dev)&lt;BR /&gt;{&lt;BR /&gt;struct usb_request *req;&lt;/P&gt;&lt;P&gt;INFO(dev, "Received Printer Reset Request\n");&lt;/P&gt;&lt;P&gt;if (usb_ep_disable(dev-&amp;gt;in_ep))&lt;BR /&gt;DBG(dev, "Failed to disable USB in_ep\n");&lt;BR /&gt;if (usb_ep_disable(dev-&amp;gt;out_ep))&lt;BR /&gt;DBG(dev, "Failed to disable USB out_ep\n");&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;current_rx_req != NULL) {&lt;BR /&gt;list_add(&amp;amp;dev-&amp;gt;current_rx_req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;dev-&amp;gt;current_rx_req = NULL;&lt;BR /&gt;}&lt;BR /&gt;dev-&amp;gt;current_rx_bytes = 0;&lt;BR /&gt;dev-&amp;gt;current_rx_buf = NULL;&lt;BR /&gt;dev-&amp;gt;reset_printer = 1;&lt;/P&gt;&lt;P&gt;while (likely(!(list_empty(&amp;amp;dev-&amp;gt;rx_buffers)))) {&lt;BR /&gt;req = container_of(dev-&amp;gt;rx_buffers.next, struct usb_request,&lt;BR /&gt;list);&lt;BR /&gt;list_del_init(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;while (likely(!(list_empty(&amp;amp;dev-&amp;gt;rx_reqs_active)))) {&lt;BR /&gt;req = container_of(dev-&amp;gt;rx_buffers.next, struct usb_request,&lt;BR /&gt;list);&lt;BR /&gt;list_del_init(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;while (likely(!(list_empty(&amp;amp;dev-&amp;gt;tx_reqs_active)))) {&lt;BR /&gt;req = container_of(dev-&amp;gt;tx_reqs_active.next,&lt;BR /&gt;struct usb_request, list);&lt;BR /&gt;list_del_init(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;tx_reqs);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;if (usb_ep_enable(dev-&amp;gt;in_ep))&lt;BR /&gt;DBG(dev, "Failed to enable USB in_ep\n");&lt;BR /&gt;if (usb_ep_enable(dev-&amp;gt;out_ep))&lt;BR /&gt;DBG(dev, "Failed to enable USB out_ep\n");&lt;/P&gt;&lt;P&gt;wake_up_interruptible(&amp;amp;dev-&amp;gt;rx_wait);&lt;BR /&gt;wake_up_interruptible(&amp;amp;dev-&amp;gt;tx_wait);&lt;BR /&gt;wake_up_interruptible(&amp;amp;dev-&amp;gt;tx_flush_wait);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/*-------------------------------------------------------------------------*/&lt;/P&gt;&lt;P&gt;static bool gprinter_req_match(struct usb_function *f,&lt;BR /&gt;const struct usb_ctrlrequest *ctrl,&lt;BR /&gt;bool config0)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = func_to_printer(f);&lt;BR /&gt;u16 w_index = le16_to_cpu(ctrl-&amp;gt;wIndex);&lt;BR /&gt;u16 w_value = le16_to_cpu(ctrl-&amp;gt;wValue);&lt;BR /&gt;u16 w_length = le16_to_cpu(ctrl-&amp;gt;wLength);&lt;/P&gt;&lt;P&gt;if (config0)&lt;BR /&gt;return false;&lt;/P&gt;&lt;P&gt;if ((ctrl-&amp;gt;bRequestType &amp;amp; USB_RECIP_MASK) != USB_RECIP_INTERFACE ||&lt;BR /&gt;(ctrl-&amp;gt;bRequestType &amp;amp; USB_TYPE_MASK) != USB_TYPE_CLASS)&lt;BR /&gt;return false;&lt;/P&gt;&lt;P&gt;switch (ctrl-&amp;gt;bRequest) {&lt;BR /&gt;case GET_DEVICE_ID:&lt;BR /&gt;w_index &amp;gt;&amp;gt;= 8;&lt;BR /&gt;if (USB_DIR_IN &amp;amp; ctrl-&amp;gt;bRequestType)&lt;BR /&gt;break;&lt;BR /&gt;return false;&lt;BR /&gt;case GET_PORT_STATUS:&lt;BR /&gt;if (!w_value &amp;amp;&amp;amp; w_length == 1 &amp;amp;&amp;amp;&lt;BR /&gt;(USB_DIR_IN &amp;amp; ctrl-&amp;gt;bRequestType))&lt;BR /&gt;break;&lt;BR /&gt;return false;&lt;BR /&gt;case SOFT_RESET:&lt;BR /&gt;if (!w_value &amp;amp;&amp;amp; !w_length &amp;amp;&amp;amp;&lt;BR /&gt;!(USB_DIR_IN &amp;amp; ctrl-&amp;gt;bRequestType))&lt;BR /&gt;break;&lt;BR /&gt;fallthrough;&lt;BR /&gt;default:&lt;BR /&gt;return false;&lt;BR /&gt;}&lt;BR /&gt;return w_index == dev-&amp;gt;interface;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/*&lt;BR /&gt;* The setup() callback implements all the ep0 functionality that's not&lt;BR /&gt;* handled lower down.&lt;BR /&gt;*/&lt;BR /&gt;static int printer_func_setup(struct usb_function *f,&lt;BR /&gt;const struct usb_ctrlrequest *ctrl)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = func_to_printer(f);&lt;BR /&gt;struct usb_composite_dev *cdev = f-&amp;gt;config-&amp;gt;cdev;&lt;BR /&gt;struct usb_request *req = cdev-&amp;gt;req;&lt;BR /&gt;u8 *buf = req-&amp;gt;buf;&lt;BR /&gt;int value = -EOPNOTSUPP;&lt;BR /&gt;u16 wIndex = le16_to_cpu(ctrl-&amp;gt;wIndex);&lt;BR /&gt;u16 wValue = le16_to_cpu(ctrl-&amp;gt;wValue);&lt;BR /&gt;u16 wLength = le16_to_cpu(ctrl-&amp;gt;wLength);&lt;/P&gt;&lt;P&gt;DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n",&lt;BR /&gt;ctrl-&amp;gt;bRequestType, ctrl-&amp;gt;bRequest, wValue, wIndex, wLength);&lt;/P&gt;&lt;P&gt;switch (ctrl-&amp;gt;bRequestType&amp;amp;USB_TYPE_MASK) {&lt;BR /&gt;case USB_TYPE_CLASS:&lt;BR /&gt;switch (ctrl-&amp;gt;bRequest) {&lt;BR /&gt;case GET_DEVICE_ID: /* Get the IEEE-1284 PNP String */&lt;BR /&gt;/* Only one printer interface is supported. */&lt;BR /&gt;if ((wIndex&amp;gt;&amp;gt;8) != dev-&amp;gt;interface)&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;if (!dev-&amp;gt;pnp_string) {&lt;BR /&gt;value = 0;&lt;BR /&gt;break;&lt;BR /&gt;}&lt;BR /&gt;value = strlen(dev-&amp;gt;pnp_string);&lt;BR /&gt;buf[0] = (value &amp;gt;&amp;gt; &lt;LI-EMOJI id="lia_smiling-face-with-sunglasses" title=":smiling_face_with_sunglasses:"&gt;&lt;/LI-EMOJI&gt; &amp;amp; 0xFF;&lt;BR /&gt;buf[1] = value &amp;amp; 0xFF;&lt;BR /&gt;memcpy(buf + 2, dev-&amp;gt;pnp_string, value);&lt;BR /&gt;DBG(dev, "1284 PNP String: %x %s\n", value,&lt;BR /&gt;dev-&amp;gt;pnp_string);&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;case GET_PORT_STATUS: /* Get Port Status */&lt;BR /&gt;/* Only one printer interface is supported. */&lt;BR /&gt;if (wIndex != dev-&amp;gt;interface)&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;buf[0] = dev-&amp;gt;printer_status;&lt;BR /&gt;value = min_t(u16, wLength, 1);&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;case SOFT_RESET: /* Soft Reset */&lt;BR /&gt;/* Only one printer interface is supported. */&lt;BR /&gt;if (wIndex != dev-&amp;gt;interface)&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;printer_soft_reset(dev);&lt;/P&gt;&lt;P&gt;value = 0;&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;default:&lt;BR /&gt;goto unknown;&lt;BR /&gt;}&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;default:&lt;BR /&gt;unknown:&lt;BR /&gt;VDBG(dev,&lt;BR /&gt;"unknown ctrl req%02x.%02x v%04x i%04x l%d\n",&lt;BR /&gt;ctrl-&amp;gt;bRequestType, ctrl-&amp;gt;bRequest,&lt;BR /&gt;wValue, wIndex, wLength);&lt;BR /&gt;break;&lt;BR /&gt;}&lt;BR /&gt;/* host either stalls (value &amp;lt; 0) or reports success */&lt;BR /&gt;if (value &amp;gt;= 0) {&lt;BR /&gt;req-&amp;gt;length = value;&lt;BR /&gt;req-&amp;gt;zero = value &amp;lt; wLength;&lt;BR /&gt;value = usb_ep_queue(cdev-&amp;gt;gadget-&amp;gt;ep0, req, GFP_ATOMIC);&lt;BR /&gt;if (value &amp;lt; 0) {&lt;BR /&gt;ERROR(dev, "%s:%d Error!\n", __func__, __LINE__);&lt;BR /&gt;req-&amp;gt;status = 0;&lt;BR /&gt;}&lt;BR /&gt;}&lt;BR /&gt;return value;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static int printer_func_bind(struct usb_configuration *c,&lt;BR /&gt;struct usb_function *f)&lt;BR /&gt;{&lt;BR /&gt;struct usb_gadget *gadget = c-&amp;gt;cdev-&amp;gt;gadget;&lt;BR /&gt;struct printer_dev *dev = func_to_printer(f);&lt;BR /&gt;struct device *pdev;&lt;BR /&gt;struct usb_composite_dev *cdev = c-&amp;gt;cdev;&lt;BR /&gt;struct usb_ep *in_ep;&lt;BR /&gt;struct usb_ep *out_ep = NULL;&lt;BR /&gt;struct usb_request *req;&lt;BR /&gt;int id;&lt;BR /&gt;int ret;&lt;BR /&gt;u32 i;&lt;BR /&gt;dev_t devt;&lt;BR /&gt;id = usb_interface_id(c, f);&lt;BR /&gt;if (id &amp;lt; 0)&lt;BR /&gt;return id;&lt;BR /&gt;intf_desc.bInterfaceNumber = id;&lt;/P&gt;&lt;P&gt;/* finish hookup to lower layer ... */&lt;BR /&gt;dev-&amp;gt;gadget = gadget;&lt;/P&gt;&lt;P&gt;/* all we really need is bulk IN/OUT */&lt;BR /&gt;in_ep = usb_ep_autoconfig(cdev-&amp;gt;gadget, &amp;amp;fs_ep_in_desc);&lt;BR /&gt;if (!in_ep) {&lt;BR /&gt;autoconf_fail:&lt;BR /&gt;dev_err(&amp;amp;cdev-&amp;gt;gadget-&amp;gt;dev, "can't autoconfigure on %s\n",&lt;BR /&gt;cdev-&amp;gt;gadget-&amp;gt;name);&lt;BR /&gt;return -ENODEV;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;out_ep = usb_ep_autoconfig(cdev-&amp;gt;gadget, &amp;amp;fs_ep_out_desc);&lt;BR /&gt;if (!out_ep)&lt;BR /&gt;goto autoconf_fail;&lt;/P&gt;&lt;P&gt;/* assumes that all endpoints are dual-speed */&lt;BR /&gt;hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;&lt;BR /&gt;// hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;&lt;BR /&gt;hs_ep1_out_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;&lt;BR /&gt;// hs_ep1_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;&lt;BR /&gt;ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;&lt;BR /&gt;ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;&lt;/P&gt;&lt;P&gt;ret = usb_assign_descriptors(f, fs_printer_function,&lt;BR /&gt;hs_printer_function, ss_printer_function,&lt;BR /&gt;ss_printer_function);&lt;BR /&gt;if (ret)&lt;BR /&gt;return ret;&lt;/P&gt;&lt;P&gt;dev-&amp;gt;in_ep = in_ep;&lt;BR /&gt;dev-&amp;gt;out_ep = out_ep;&lt;/P&gt;&lt;P&gt;ret = -ENOMEM;&lt;BR /&gt;for (i = 0; i &amp;lt; dev-&amp;gt;q_len; i++) {&lt;BR /&gt;req = printer_req_alloc(dev-&amp;gt;in_ep, USB_BUFSIZE, GFP_KERNEL);&lt;BR /&gt;if (!req)&lt;BR /&gt;goto fail_tx_reqs;&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;tx_reqs);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;for (i = 0; i &amp;lt; dev-&amp;gt;q_len; i++) {&lt;BR /&gt;req = printer_req_alloc(dev-&amp;gt;out_ep, USB_BUFSIZE, GFP_KERNEL);&lt;BR /&gt;if (!req)&lt;BR /&gt;goto fail_rx_reqs;&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* Setup the sysfs files for the printer gadget. */&lt;BR /&gt;devt = MKDEV(major, dev-&amp;gt;minor);&lt;BR /&gt;pdev = device_create(usb_gadget_class, NULL, devt,&lt;BR /&gt;NULL, "sonosa_acu%d", dev-&amp;gt;minor);&lt;BR /&gt;if (IS_ERR(pdev)) {&lt;BR /&gt;ERROR(dev, "Failed to create device: sonosa_acu\n");&lt;BR /&gt;ret = PTR_ERR(pdev);&lt;BR /&gt;goto fail_rx_reqs;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/*&lt;BR /&gt;* Register a character device as an interface to a user mode&lt;BR /&gt;* program that handles the printer specific functionality.&lt;BR /&gt;*/&lt;BR /&gt;cdev_init(&amp;amp;dev-&amp;gt;printer_cdev, &amp;amp;printer_io_operations);&lt;BR /&gt;dev-&amp;gt;printer_cdev.owner = THIS_MODULE;&lt;BR /&gt;ret = cdev_add(&amp;amp;dev-&amp;gt;printer_cdev, devt, 1);&lt;BR /&gt;if (ret) {&lt;BR /&gt;ERROR(dev, "Failed to open char device\n");&lt;BR /&gt;goto fail_cdev_add;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;return 0;&lt;/P&gt;&lt;P&gt;fail_cdev_add:&lt;BR /&gt;device_destroy(usb_gadget_class, devt);&lt;/P&gt;&lt;P&gt;fail_rx_reqs:&lt;BR /&gt;while (!list_empty(&amp;amp;dev-&amp;gt;rx_reqs)) {&lt;BR /&gt;req = container_of(dev-&amp;gt;rx_reqs.next, struct usb_request, list);&lt;BR /&gt;list_del(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;printer_req_free(dev-&amp;gt;out_ep, req);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;fail_tx_reqs:&lt;BR /&gt;while (!list_empty(&amp;amp;dev-&amp;gt;tx_reqs)) {&lt;BR /&gt;req = container_of(dev-&amp;gt;tx_reqs.next, struct usb_request, list);&lt;BR /&gt;list_del(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;printer_req_free(dev-&amp;gt;in_ep, req);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;usb_free_all_descriptors(f);&lt;BR /&gt;return ret;&lt;/P&gt;&lt;P&gt;}&lt;/P&gt;&lt;P&gt;static int printer_func_set_alt(struct usb_function *f,&lt;BR /&gt;unsigned intf, unsigned alt)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = func_to_printer(f);&lt;BR /&gt;int ret = -ENOTSUPP;&lt;/P&gt;&lt;P&gt;if (!alt)&lt;BR /&gt;ret = set_interface(dev, intf);&lt;/P&gt;&lt;P&gt;return ret;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void printer_func_disable(struct usb_function *f)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = func_to_printer(f);&lt;/P&gt;&lt;P&gt;DBG(dev, "%s\n", __func__);&lt;/P&gt;&lt;P&gt;printer_reset_interface(dev);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static inline struct f_printer_opts&lt;BR /&gt;*to_f_printer_opts(struct config_item *item)&lt;BR /&gt;{&lt;BR /&gt;return container_of(to_config_group(item), struct f_printer_opts,&lt;BR /&gt;func_inst.group);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void printer_attr_release(struct config_item *item)&lt;BR /&gt;{&lt;BR /&gt;struct f_printer_opts *opts = to_f_printer_opts(item);&lt;/P&gt;&lt;P&gt;usb_put_function_instance(&amp;amp;opts-&amp;gt;func_inst);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static struct configfs_item_operations printer_item_ops = {&lt;BR /&gt;.release = printer_attr_release,&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static ssize_t f_printer_opts_pnp_string_show(struct config_item *item,&lt;BR /&gt;char *page)&lt;BR /&gt;{&lt;BR /&gt;struct f_printer_opts *opts = to_f_printer_opts(item);&lt;BR /&gt;int result = 0;&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;if (!opts-&amp;gt;pnp_string)&lt;BR /&gt;goto unlock;&lt;/P&gt;&lt;P&gt;result = strlcpy(page, opts-&amp;gt;pnp_string, PAGE_SIZE);&lt;BR /&gt;if (result &amp;gt;= PAGE_SIZE) {&lt;BR /&gt;result = PAGE_SIZE;&lt;BR /&gt;} else if (page[result - 1] != '\n' &amp;amp;&amp;amp; result + 1 &amp;lt; PAGE_SIZE) {&lt;BR /&gt;page[result++] = '\n';&lt;BR /&gt;page[result] = '\0';&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;unlock:&lt;BR /&gt;mutex_unlock(&amp;amp;opts-&amp;gt;lock);&lt;/P&gt;&lt;P&gt;return result;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static ssize_t f_printer_opts_pnp_string_store(struct config_item *item,&lt;BR /&gt;const char *page, size_t len)&lt;BR /&gt;{&lt;BR /&gt;struct f_printer_opts *opts = to_f_printer_opts(item);&lt;BR /&gt;char *new_pnp;&lt;BR /&gt;int result;&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;opts-&amp;gt;lock);&lt;/P&gt;&lt;P&gt;new_pnp = kstrndup(page, len, GFP_KERNEL);&lt;BR /&gt;if (!new_pnp) {&lt;BR /&gt;result = -ENOMEM;&lt;BR /&gt;goto unlock;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;if (opts-&amp;gt;pnp_string_allocated)&lt;BR /&gt;kfree(opts-&amp;gt;pnp_string);&lt;/P&gt;&lt;P&gt;opts-&amp;gt;pnp_string_allocated = true;&lt;BR /&gt;opts-&amp;gt;pnp_string = new_pnp;&lt;BR /&gt;result = len;&lt;BR /&gt;unlock:&lt;BR /&gt;mutex_unlock(&amp;amp;opts-&amp;gt;lock);&lt;/P&gt;&lt;P&gt;return result;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;CONFIGFS_ATTR(f_printer_opts_, pnp_string);&lt;/P&gt;&lt;P&gt;static ssize_t f_printer_opts_q_len_show(struct config_item *item,&lt;BR /&gt;char *page)&lt;BR /&gt;{&lt;BR /&gt;struct f_printer_opts *opts = to_f_printer_opts(item);&lt;BR /&gt;int result;&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;result = sprintf(page, "%d\n", opts-&amp;gt;q_len);&lt;BR /&gt;mutex_unlock(&amp;amp;opts-&amp;gt;lock);&lt;/P&gt;&lt;P&gt;return result;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static ssize_t f_printer_opts_q_len_store(struct config_item *item,&lt;BR /&gt;const char *page, size_t len)&lt;BR /&gt;{&lt;BR /&gt;struct f_printer_opts *opts = to_f_printer_opts(item);&lt;BR /&gt;int ret;&lt;BR /&gt;u16 num;&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;if (opts-&amp;gt;refcnt) {&lt;BR /&gt;ret = -EBUSY;&lt;BR /&gt;goto end;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;ret = kstrtou16(page, 0, &amp;amp;num);&lt;BR /&gt;if (ret)&lt;BR /&gt;goto end;&lt;/P&gt;&lt;P&gt;opts-&amp;gt;q_len = (unsigned)num;&lt;BR /&gt;ret = len;&lt;BR /&gt;end:&lt;BR /&gt;mutex_unlock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;return ret;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;CONFIGFS_ATTR(f_printer_opts_, q_len);&lt;/P&gt;&lt;P&gt;static struct configfs_attribute *printer_attrs[] = {&lt;BR /&gt;&amp;amp;f_printer_opts_attr_pnp_string,&lt;BR /&gt;&amp;amp;f_printer_opts_attr_q_len,&lt;BR /&gt;NULL,&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static const struct config_item_type printer_func_type = {&lt;BR /&gt;.ct_item_ops = &amp;amp;printer_item_ops,&lt;BR /&gt;.ct_attrs = printer_attrs,&lt;BR /&gt;.ct_owner = THIS_MODULE,&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static inline int gprinter_get_minor(void)&lt;BR /&gt;{&lt;BR /&gt;int ret;&lt;/P&gt;&lt;P&gt;ret = ida_simple_get(&amp;amp;printer_ida, 0, 0, GFP_KERNEL);&lt;BR /&gt;if (ret &amp;gt;= PRINTER_MINORS) {&lt;BR /&gt;ida_simple_remove(&amp;amp;printer_ida, ret);&lt;BR /&gt;ret = -ENODEV;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;return ret;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static inline void gprinter_put_minor(int minor)&lt;BR /&gt;{&lt;BR /&gt;ida_simple_remove(&amp;amp;printer_ida, minor);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static int gprinter_setup(int);&lt;BR /&gt;static void gprinter_cleanup(void);&lt;/P&gt;&lt;P&gt;static void gprinter_free_inst(struct usb_function_instance *f)&lt;BR /&gt;{&lt;BR /&gt;struct f_printer_opts *opts;&lt;/P&gt;&lt;P&gt;opts = container_of(f, struct f_printer_opts, func_inst);&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;printer_ida_lock);&lt;/P&gt;&lt;P&gt;gprinter_put_minor(opts-&amp;gt;minor);&lt;BR /&gt;if (ida_is_empty(&amp;amp;printer_ida))&lt;BR /&gt;gprinter_cleanup();&lt;/P&gt;&lt;P&gt;mutex_unlock(&amp;amp;printer_ida_lock);&lt;/P&gt;&lt;P&gt;if (opts-&amp;gt;pnp_string_allocated)&lt;BR /&gt;kfree(opts-&amp;gt;pnp_string);&lt;BR /&gt;kfree(opts);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static struct usb_function_instance *gprinter_alloc_inst(void)&lt;BR /&gt;{&lt;BR /&gt;struct f_printer_opts *opts;&lt;BR /&gt;struct usb_function_instance *ret;&lt;BR /&gt;int status = 0;&lt;/P&gt;&lt;P&gt;opts = kzalloc(sizeof(*opts), GFP_KERNEL);&lt;BR /&gt;if (!opts)&lt;BR /&gt;return ERR_PTR(-ENOMEM);&lt;/P&gt;&lt;P&gt;mutex_init(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;opts-&amp;gt;func_inst.free_func_inst = gprinter_free_inst;&lt;BR /&gt;ret = &amp;amp;opts-&amp;gt;func_inst;&lt;/P&gt;&lt;P&gt;/* Make sure q_len is initialized, otherwise the bound device can't support read/write! */&lt;BR /&gt;opts-&amp;gt;q_len = DEFAULT_Q_LEN;&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;printer_ida_lock);&lt;/P&gt;&lt;P&gt;if (ida_is_empty(&amp;amp;printer_ida)) {&lt;BR /&gt;status = gprinter_setup(PRINTER_MINORS);&lt;BR /&gt;if (status) {&lt;BR /&gt;ret = ERR_PTR(status);&lt;BR /&gt;kfree(opts);&lt;BR /&gt;goto unlock;&lt;BR /&gt;}&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;opts-&amp;gt;minor = gprinter_get_minor();&lt;BR /&gt;if (opts-&amp;gt;minor &amp;lt; 0) {&lt;BR /&gt;ret = ERR_PTR(opts-&amp;gt;minor);&lt;BR /&gt;kfree(opts);&lt;BR /&gt;if (ida_is_empty(&amp;amp;printer_ida))&lt;BR /&gt;gprinter_cleanup();&lt;BR /&gt;goto unlock;&lt;BR /&gt;}&lt;BR /&gt;config_group_init_type_name(&amp;amp;opts-&amp;gt;func_inst.group, "",&lt;BR /&gt;&amp;amp;printer_func_type);&lt;/P&gt;&lt;P&gt;unlock:&lt;BR /&gt;mutex_unlock(&amp;amp;printer_ida_lock);&lt;BR /&gt;return ret;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void gprinter_free(struct usb_function *f)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = func_to_printer(f);&lt;BR /&gt;struct f_printer_opts *opts;&lt;/P&gt;&lt;P&gt;opts = container_of(f-&amp;gt;fi, struct f_printer_opts, func_inst);&lt;/P&gt;&lt;P&gt;kref_put(&amp;amp;dev-&amp;gt;kref, printer_dev_free);&lt;BR /&gt;mutex_lock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;--opts-&amp;gt;refcnt;&lt;BR /&gt;mutex_unlock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void printer_func_unbind(struct usb_configuration *c,&lt;BR /&gt;struct usb_function *f)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev;&lt;BR /&gt;struct usb_request *req;&lt;/P&gt;&lt;P&gt;dev = func_to_printer(f);&lt;/P&gt;&lt;P&gt;device_destroy(usb_gadget_class, MKDEV(major, dev-&amp;gt;minor));&lt;/P&gt;&lt;P&gt;/* Remove Character Device */&lt;BR /&gt;cdev_del(&amp;amp;dev-&amp;gt;printer_cdev);&lt;/P&gt;&lt;P&gt;/* we must already have been disconnected ... no i/o may be active */&lt;BR /&gt;WARN_ON(!list_empty(&amp;amp;dev-&amp;gt;tx_reqs_active));&lt;BR /&gt;WARN_ON(!list_empty(&amp;amp;dev-&amp;gt;rx_reqs_active));&lt;/P&gt;&lt;P&gt;/* Free all memory for this driver. */&lt;BR /&gt;while (!list_empty(&amp;amp;dev-&amp;gt;tx_reqs)) {&lt;BR /&gt;req = container_of(dev-&amp;gt;tx_reqs.next, struct usb_request,&lt;BR /&gt;list);&lt;BR /&gt;list_del(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;printer_req_free(dev-&amp;gt;in_ep, req);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;current_rx_req != NULL)&lt;BR /&gt;printer_req_free(dev-&amp;gt;out_ep, dev-&amp;gt;current_rx_req);&lt;/P&gt;&lt;P&gt;while (!list_empty(&amp;amp;dev-&amp;gt;rx_reqs)) {&lt;BR /&gt;req = container_of(dev-&amp;gt;rx_reqs.next,&lt;BR /&gt;struct usb_request, list);&lt;BR /&gt;list_del(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;printer_req_free(dev-&amp;gt;out_ep, req);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;while (!list_empty(&amp;amp;dev-&amp;gt;rx_buffers)) {&lt;BR /&gt;req = container_of(dev-&amp;gt;rx_buffers.next,&lt;BR /&gt;struct usb_request, list);&lt;BR /&gt;list_del(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;printer_req_free(dev-&amp;gt;out_ep, req);&lt;BR /&gt;}&lt;BR /&gt;usb_free_all_descriptors(f);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static struct usb_function *gprinter_alloc(struct usb_function_instance *fi)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev;&lt;BR /&gt;struct f_printer_opts *opts;&lt;/P&gt;&lt;P&gt;opts = container_of(fi, struct f_printer_opts, func_inst);&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;if (opts-&amp;gt;minor &amp;gt;= minors) {&lt;BR /&gt;mutex_unlock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;return ERR_PTR(-ENOENT);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;dev = kzalloc(sizeof(*dev), GFP_KERNEL);&lt;BR /&gt;if (!dev) {&lt;BR /&gt;mutex_unlock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;return ERR_PTR(-ENOMEM);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;kref_init(&amp;amp;dev-&amp;gt;kref);&lt;BR /&gt;++opts-&amp;gt;refcnt;&lt;BR /&gt;dev-&amp;gt;minor = opts-&amp;gt;minor;&lt;BR /&gt;dev-&amp;gt;pnp_string = opts-&amp;gt;pnp_string;&lt;BR /&gt;dev-&amp;gt;q_len = opts-&amp;gt;q_len;&lt;BR /&gt;mutex_unlock(&amp;amp;opts-&amp;gt;lock);&lt;/P&gt;&lt;P&gt;dev-&amp;gt;function.name = "printer";&lt;BR /&gt;dev-&amp;gt;function.bind = printer_func_bind;&lt;BR /&gt;dev-&amp;gt;function.setup = printer_func_setup;&lt;BR /&gt;dev-&amp;gt;function.unbind = printer_func_unbind;&lt;BR /&gt;dev-&amp;gt;function.set_alt = printer_func_set_alt;&lt;BR /&gt;dev-&amp;gt;function.disable = printer_func_disable;&lt;BR /&gt;dev-&amp;gt;function.req_match = gprinter_req_match;&lt;BR /&gt;dev-&amp;gt;function.free_func = gprinter_free;&lt;/P&gt;&lt;P&gt;INIT_LIST_HEAD(&amp;amp;dev-&amp;gt;tx_reqs);&lt;BR /&gt;INIT_LIST_HEAD(&amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;INIT_LIST_HEAD(&amp;amp;dev-&amp;gt;rx_buffers);&lt;BR /&gt;INIT_LIST_HEAD(&amp;amp;dev-&amp;gt;tx_reqs_active);&lt;BR /&gt;INIT_LIST_HEAD(&amp;amp;dev-&amp;gt;rx_reqs_active);&lt;/P&gt;&lt;P&gt;spin_lock_init(&amp;amp;dev-&amp;gt;lock);&lt;BR /&gt;mutex_init(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;init_waitqueue_head(&amp;amp;dev-&amp;gt;rx_wait);&lt;BR /&gt;init_waitqueue_head(&amp;amp;dev-&amp;gt;tx_wait);&lt;BR /&gt;init_waitqueue_head(&amp;amp;dev-&amp;gt;tx_flush_wait);&lt;/P&gt;&lt;P&gt;dev-&amp;gt;interface = -1;&lt;BR /&gt;dev-&amp;gt;printer_cdev_open = 0;&lt;BR /&gt;dev-&amp;gt;printer_status = PRINTER_NOT_ERROR;&lt;BR /&gt;dev-&amp;gt;current_rx_req = NULL;&lt;BR /&gt;dev-&amp;gt;current_rx_bytes = 0;&lt;BR /&gt;dev-&amp;gt;current_rx_buf = NULL;&lt;/P&gt;&lt;P&gt;return &amp;amp;dev-&amp;gt;function;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc);&lt;BR /&gt;MODULE_LICENSE("GPL");&lt;BR /&gt;MODULE_AUTHOR("Craig Nadler");&lt;/P&gt;&lt;P&gt;static int gprinter_setup(int count)&lt;BR /&gt;{&lt;BR /&gt;int status;&lt;BR /&gt;dev_t devt;&lt;/P&gt;&lt;P&gt;usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget");&lt;BR /&gt;//usb_gadget_class = class_create(THIS_MODULE, "sonosa_acu");&lt;BR /&gt;if (IS_ERR(usb_gadget_class)) {&lt;BR /&gt;status = PTR_ERR(usb_gadget_class);&lt;BR /&gt;usb_gadget_class = NULL;&lt;BR /&gt;pr_err("unable to create usb_gadget class %d\n", status);&lt;BR /&gt;return status;&lt;BR /&gt;}&lt;BR /&gt;status = alloc_chrdev_region(&amp;amp;devt, 0, count, "sonosa_acu");&lt;BR /&gt;if (status) {&lt;BR /&gt;pr_err("alloc_chrdev_region %d\n", status);&lt;BR /&gt;class_destroy(usb_gadget_class);&lt;BR /&gt;usb_gadget_class = NULL;&lt;BR /&gt;return status;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;major = MAJOR(devt);&lt;BR /&gt;minors = count;&lt;/P&gt;&lt;P&gt;return status;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void gprinter_cleanup(void)&lt;BR /&gt;{&lt;BR /&gt;if (major) {&lt;BR /&gt;unregister_chrdev_region(MKDEV(major, 0), minors);&lt;BR /&gt;major = minors = 0;&lt;BR /&gt;}&lt;BR /&gt;class_destroy(usb_gadget_class);&lt;BR /&gt;usb_gadget_class= NULL;&lt;BR /&gt;}&lt;/P&gt;</description>
    <pubDate>Fri, 10 Mar 2023 11:01:44 GMT</pubDate>
    <dc:creator>sunil_embex</dc:creator>
    <dc:date>2023-03-10T11:01:44Z</dc:date>
    <item>
      <title>UVC Gadget Driver Issue</title>
      <link>https://community.nxp.com/t5/i-MX-Processors/UVC-Gadget-Driver-Issue/m-p/1606882#M202132</link>
      <description>&lt;P&gt;Dear Team,&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;I am using IMX8M-Mini evk board and trying to configure usb uvc gadget&amp;nbsp; driver.&lt;/P&gt;&lt;P&gt;I followed this link:&amp;nbsp;&lt;A href="https://developer.ridgerun.com/wiki/index.php/How_to_use_the_UVC_gadget_driver_in_Linux" target="_blank"&gt;https://developer.ridgerun.com/wiki/index.php/How_to_use_the_UVC_gadget_driver_in_Linux&lt;/A&gt;&lt;/P&gt;&lt;P&gt;But I am not able to see any node in /sys/class/udc.&amp;nbsp;&lt;/P&gt;&lt;P&gt;Can you please help and do the needful.&lt;/P&gt;&lt;P&gt;Regards,&lt;/P&gt;&lt;P&gt;Sunilk&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Tue, 28 Feb 2023 13:41:39 GMT</pubDate>
      <guid>https://community.nxp.com/t5/i-MX-Processors/UVC-Gadget-Driver-Issue/m-p/1606882#M202132</guid>
      <dc:creator>sunil_embex</dc:creator>
      <dc:date>2023-02-28T13:41:39Z</dc:date>
    </item>
    <item>
      <title>Re: UVC Gadget Driver Issue</title>
      <link>https://community.nxp.com/t5/i-MX-Processors/UVC-Gadget-Driver-Issue/m-p/1607016#M202143</link>
      <description>&lt;P&gt;Hello,&lt;BR /&gt;&lt;BR /&gt;Could you share the BSP version that you're working with?&lt;BR /&gt;Also, to which USB port are you using for your test?&lt;BR /&gt;&lt;BR /&gt;Best regards,&lt;BR /&gt;Aldo.&lt;/P&gt;</description>
      <pubDate>Tue, 28 Feb 2023 17:27:55 GMT</pubDate>
      <guid>https://community.nxp.com/t5/i-MX-Processors/UVC-Gadget-Driver-Issue/m-p/1607016#M202143</guid>
      <dc:creator>AldoG</dc:creator>
      <dc:date>2023-02-28T17:27:55Z</dc:date>
    </item>
    <item>
      <title>Re: UVC Gadget Driver Issue</title>
      <link>https://community.nxp.com/t5/i-MX-Processors/UVC-Gadget-Driver-Issue/m-p/1613113#M202567</link>
      <description>&lt;P&gt;Hi Aldo,&lt;/P&gt;&lt;P&gt;Thanks for reply. I moved on the usb printer gadget driver and i am able to transfer data on default end points.&lt;/P&gt;&lt;P&gt;Further I have added two more endpoints and able to see in libusb output but not able to send/receive data so if you can help me to resolve this issue that would be really great help otherwise you can close the ticket.&lt;/P&gt;&lt;P&gt;Below is the driver&lt;/P&gt;&lt;P&gt;// SPDX-License-Identifier: GPL-2.0+&lt;BR /&gt;/*&lt;BR /&gt;* f_printer.c - USB printer function driver&lt;BR /&gt;*&lt;BR /&gt;* Copied from drivers/usb/gadget/legacy/printer.c,&lt;BR /&gt;* which was:&lt;BR /&gt;*&lt;BR /&gt;* printer.c -- Printer gadget driver&lt;BR /&gt;*&lt;BR /&gt;* Copyright (C) 2003-2005 David Brownell&lt;BR /&gt;* Copyright (C) 2006 Craig W. Nadler&lt;BR /&gt;*/&lt;/P&gt;&lt;P&gt;#include &amp;lt;linux/module.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/kernel.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/delay.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/ioport.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/sched.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/slab.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/mutex.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/errno.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/init.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/idr.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/timer.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/list.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/interrupt.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/device.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/moduleparam.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/fs.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/poll.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/types.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/ctype.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/cdev.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/kref.h&amp;gt;&lt;BR /&gt;#include &amp;lt;asm/byteorder.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/io.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/irq.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/uaccess.h&amp;gt;&lt;BR /&gt;#include &amp;lt;asm/unaligned.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/usb/ch9.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/usb/composite.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/usb/gadget.h&amp;gt;&lt;BR /&gt;#include &amp;lt;linux/usb/g_printer.h&amp;gt;&lt;BR /&gt;#include "u_printer.h"&lt;/P&gt;&lt;P&gt;#define PRINTER_MINORS 4&lt;BR /&gt;#define GET_DEVICE_ID 0&lt;BR /&gt;#define GET_PORT_STATUS 1&lt;BR /&gt;#define SOFT_RESET 2&lt;/P&gt;&lt;P&gt;#define DEFAULT_Q_LEN 10 /* same as legacy g_printer gadget */&lt;/P&gt;&lt;P&gt;static int major, minors;&lt;BR /&gt;static struct class *usb_gadget_class;&lt;BR /&gt;static DEFINE_IDA(printer_ida);&lt;BR /&gt;static DEFINE_MUTEX(printer_ida_lock); /* protects access do printer_ida */&lt;/P&gt;&lt;P&gt;/*-------------------------------------------------------------------------*/&lt;/P&gt;&lt;P&gt;struct printer_dev {&lt;BR /&gt;spinlock_t lock; /* lock this structure */&lt;BR /&gt;/* lock buffer lists during read/write calls */&lt;BR /&gt;struct mutex lock_printer_io;&lt;BR /&gt;struct usb_gadget *gadget;&lt;BR /&gt;s8 interface;&lt;BR /&gt;struct usb_ep *in_ep, *out_ep;&lt;BR /&gt;struct kref kref;&lt;BR /&gt;struct list_head rx_reqs; /* List of free RX structs */&lt;BR /&gt;struct list_head rx_reqs_active; /* List of Active RX xfers */&lt;BR /&gt;struct list_head rx_buffers; /* List of completed xfers */&lt;BR /&gt;/* wait until there is data to be read. */&lt;BR /&gt;wait_queue_head_t rx_wait;&lt;BR /&gt;struct list_head tx_reqs; /* List of free TX structs */&lt;BR /&gt;struct list_head tx_reqs_active; /* List of Active TX xfers */&lt;BR /&gt;/* Wait until there are write buffers available to use. */&lt;BR /&gt;wait_queue_head_t tx_wait;&lt;BR /&gt;/* Wait until all write buffers have been sent. */&lt;BR /&gt;wait_queue_head_t tx_flush_wait;&lt;BR /&gt;struct usb_request *current_rx_req;&lt;BR /&gt;size_t current_rx_bytes;&lt;BR /&gt;u8 *current_rx_buf;&lt;BR /&gt;u8 printer_status;&lt;BR /&gt;u8 reset_printer;&lt;BR /&gt;int minor;&lt;BR /&gt;struct cdev printer_cdev;&lt;BR /&gt;u8 printer_cdev_open;&lt;BR /&gt;wait_queue_head_t wait;&lt;BR /&gt;unsigned q_len;&lt;BR /&gt;char *pnp_string; /* We don't own memory! */&lt;BR /&gt;struct usb_function function;&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static inline struct printer_dev *func_to_printer(struct usb_function *f)&lt;BR /&gt;{&lt;BR /&gt;return container_of(f, struct printer_dev, function);&lt;BR /&gt;}&lt;BR /&gt;/*-------------------------------------------------------------------------*/&lt;/P&gt;&lt;P&gt;/*&lt;BR /&gt;* DESCRIPTORS ... most are static, but strings and (full) configuration&lt;BR /&gt;* descriptors are built on demand.&lt;BR /&gt;*/&lt;/P&gt;&lt;P&gt;/* holds our biggest descriptor */&lt;BR /&gt;#define USB_DESC_BUFSIZE 256&lt;BR /&gt;#define USB_BUFSIZE 8192&lt;/P&gt;&lt;P&gt;static struct usb_interface_descriptor intf_desc = {&lt;BR /&gt;.bLength = sizeof(intf_desc),&lt;BR /&gt;.bDescriptorType = USB_DT_INTERFACE,&lt;BR /&gt;.bNumEndpoints = 4,&lt;BR /&gt;.bInterfaceClass = USB_CLASS_PER_INTERFACE,&lt;BR /&gt;.bInterfaceSubClass = USB_CLASS_PER_INTERFACE, /* Printer Sub-Class */&lt;BR /&gt;.bInterfaceProtocol = USB_CLASS_PER_INTERFACE, /* Bi-Directional */&lt;BR /&gt;.iInterface = 0&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_endpoint_descriptor fs_ep_in_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bEndpointAddress = USB_DIR_IN,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_endpoint_descriptor fs_ep_out_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bEndpointAddress = USB_DIR_OUT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_endpoint_descriptor fs_ep1_in_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.bEndpointAddress = USB_DIR_IN | 0x02&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_endpoint_descriptor fs_ep1_out_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.bEndpointAddress = USB_DIR_OUT | 0x02&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_descriptor_header *fs_printer_function[] = {&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;intf_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;fs_ep_in_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;fs_ep_out_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;fs_ep1_in_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;fs_ep1_out_desc,&lt;BR /&gt;NULL&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;/*&lt;BR /&gt;* usb 2.0 devices need to expose both high speed and full speed&lt;BR /&gt;* descriptors, unless they only run at full speed.&lt;BR /&gt;*/&lt;/P&gt;&lt;P&gt;static struct usb_endpoint_descriptor hs_ep_in_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.bEndpointAddress = USB_DIR_IN,&lt;BR /&gt;.wMaxPacketSize = cpu_to_le16(512)&lt;BR /&gt;};&lt;BR /&gt;//Commeted this to just get he ep1_out&lt;BR /&gt;static struct usb_endpoint_descriptor hs_ep_out_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.bEndpointAddress = USB_DIR_OUT,&lt;BR /&gt;.wMaxPacketSize = cpu_to_le16(512)&lt;BR /&gt;};&lt;BR /&gt;static struct usb_endpoint_descriptor hs_ep1_in_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.bEndpointAddress = USB_DIR_IN | 0x02,&lt;BR /&gt;.wMaxPacketSize = cpu_to_le16(512)&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_endpoint_descriptor hs_ep1_out_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.bEndpointAddress = USB_DIR_OUT | 0x02,&lt;BR /&gt;.wMaxPacketSize = cpu_to_le16(512)&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_descriptor_header *hs_printer_function[] = {&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;intf_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;hs_ep_in_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;hs_ep_out_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;hs_ep1_in_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;hs_ep1_out_desc,&lt;BR /&gt;NULL&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;/*&lt;BR /&gt;* Added endpoint descriptors for 3.0 devices&lt;BR /&gt;*/&lt;/P&gt;&lt;P&gt;static struct usb_endpoint_descriptor ss_ep_in_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.wMaxPacketSize = cpu_to_le16(1024),&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = {&lt;BR /&gt;.bLength = sizeof(ss_ep_in_comp_desc),&lt;BR /&gt;.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;&lt;BR /&gt;static struct usb_endpoint_descriptor ss_ep_out_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.wMaxPacketSize = cpu_to_le16(1024),&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = {&lt;BR /&gt;.bLength = sizeof(ss_ep_out_comp_desc),&lt;BR /&gt;.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_endpoint_descriptor ss_ep1_in_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.bEndpointAddress = USB_DIR_IN | 0x02,&lt;BR /&gt;.wMaxPacketSize = cpu_to_le16(1024)&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_ss_ep_comp_descriptor ss_ep1_in_comp_desc = {&lt;BR /&gt;.bLength = sizeof(ss_ep1_in_comp_desc),&lt;BR /&gt;.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;&lt;BR /&gt;static struct usb_endpoint_descriptor ss_ep1_out_desc = {&lt;BR /&gt;.bLength = USB_DT_ENDPOINT_SIZE,&lt;BR /&gt;.bDescriptorType = USB_DT_ENDPOINT,&lt;BR /&gt;.bmAttributes = USB_ENDPOINT_XFER_BULK,&lt;BR /&gt;.bEndpointAddress = USB_DIR_OUT | 0x02,&lt;BR /&gt;.wMaxPacketSize = cpu_to_le16(1024)&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_ss_ep_comp_descriptor ss_ep1_out_comp_desc = {&lt;BR /&gt;.bLength = sizeof(ss_ep1_out_comp_desc),&lt;BR /&gt;.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static struct usb_descriptor_header *ss_printer_function[] = {&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;intf_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;ss_ep_in_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;ss_ep_in_comp_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;ss_ep_out_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;ss_ep_out_comp_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;ss_ep1_in_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;ss_ep1_in_comp_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;ss_ep1_out_desc,&lt;BR /&gt;(struct usb_descriptor_header *) &amp;amp;ss_ep1_out_comp_desc,&lt;BR /&gt;NULL&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;/* maxpacket and other transfer characteristics vary by speed. */&lt;BR /&gt;static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget,&lt;BR /&gt;struct usb_endpoint_descriptor *fs,&lt;BR /&gt;struct usb_endpoint_descriptor *hs,&lt;BR /&gt;struct usb_endpoint_descriptor *ss)&lt;BR /&gt;{&lt;BR /&gt;switch (gadget-&amp;gt;speed) {&lt;BR /&gt;case USB_SPEED_SUPER:&lt;BR /&gt;return ss;&lt;BR /&gt;case USB_SPEED_HIGH:&lt;BR /&gt;return hs;&lt;BR /&gt;default:&lt;BR /&gt;return fs;&lt;BR /&gt;}&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/*-------------------------------------------------------------------------*/&lt;/P&gt;&lt;P&gt;static void printer_dev_free(struct kref *kref)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = container_of(kref, struct printer_dev, kref);&lt;/P&gt;&lt;P&gt;kfree(dev);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static struct usb_request *&lt;BR /&gt;printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags)&lt;BR /&gt;{&lt;BR /&gt;struct usb_request *req;&lt;/P&gt;&lt;P&gt;req = usb_ep_alloc_request(ep, gfp_flags);&lt;/P&gt;&lt;P&gt;if (req != NULL) {&lt;BR /&gt;req-&amp;gt;length = len;&lt;BR /&gt;req-&amp;gt;buf = kmalloc(len, gfp_flags);&lt;BR /&gt;if (req-&amp;gt;buf == NULL) {&lt;BR /&gt;usb_ep_free_request(ep, req);&lt;BR /&gt;return NULL;&lt;BR /&gt;}&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;return req;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void&lt;BR /&gt;printer_req_free(struct usb_ep *ep, struct usb_request *req)&lt;BR /&gt;{&lt;BR /&gt;if (ep != NULL &amp;amp;&amp;amp; req != NULL) {&lt;BR /&gt;kfree(req-&amp;gt;buf);&lt;BR /&gt;usb_ep_free_request(ep, req);&lt;BR /&gt;}&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/*-------------------------------------------------------------------------*/&lt;/P&gt;&lt;P&gt;static void rx_complete(struct usb_ep *ep, struct usb_request *req)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = ep-&amp;gt;driver_data;&lt;BR /&gt;int status = req-&amp;gt;status;&lt;BR /&gt;unsigned long flags;&lt;/P&gt;&lt;P&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;list_del_init(&amp;amp;req-&amp;gt;list); /* Remode from Active List */&lt;/P&gt;&lt;P&gt;switch (status) {&lt;/P&gt;&lt;P&gt;/* normal completion */&lt;BR /&gt;case 0:&lt;BR /&gt;if (req-&amp;gt;actual &amp;gt; 0) {&lt;BR /&gt;list_add_tail(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_buffers);&lt;BR /&gt;DBG(dev, "G_Printer : rx length %d\n", req-&amp;gt;actual);&lt;BR /&gt;} else {&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;}&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;/* software-driven interface shutdown */&lt;BR /&gt;case -ECONNRESET: /* unlink */&lt;BR /&gt;case -ESHUTDOWN: /* disconnect etc */&lt;BR /&gt;VDBG(dev, "rx shutdown, code %d\n", status);&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;/* for hardware automagic (such as pxa) */&lt;BR /&gt;case -ECONNABORTED: /* endpoint reset */&lt;BR /&gt;DBG(dev, "rx %s reset\n", ep-&amp;gt;name);&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;/* data overrun */&lt;BR /&gt;case -EOVERFLOW:&lt;BR /&gt;fallthrough;&lt;/P&gt;&lt;P&gt;default:&lt;BR /&gt;DBG(dev, "rx status %d\n", status);&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;break;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;wake_up_interruptible(&amp;amp;dev-&amp;gt;rx_wait);&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void tx_complete(struct usb_ep *ep, struct usb_request *req)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = ep-&amp;gt;driver_data;&lt;/P&gt;&lt;P&gt;switch (req-&amp;gt;status) {&lt;BR /&gt;default:&lt;BR /&gt;VDBG(dev, "tx err %d\n", req-&amp;gt;status);&lt;BR /&gt;fallthrough;&lt;BR /&gt;case -ECONNRESET: /* unlink */&lt;BR /&gt;case -ESHUTDOWN: /* disconnect etc */&lt;BR /&gt;break;&lt;BR /&gt;case 0:&lt;BR /&gt;break;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;spin_lock(&amp;amp;dev-&amp;gt;lock);&lt;BR /&gt;/* Take the request struct off the active list and put it on the&lt;BR /&gt;* free list.&lt;BR /&gt;*/&lt;BR /&gt;list_del_init(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;tx_reqs);&lt;BR /&gt;wake_up_interruptible(&amp;amp;dev-&amp;gt;tx_wait);&lt;BR /&gt;if (likely(list_empty(&amp;amp;dev-&amp;gt;tx_reqs_active)))&lt;BR /&gt;wake_up_interruptible(&amp;amp;dev-&amp;gt;tx_flush_wait);&lt;/P&gt;&lt;P&gt;spin_unlock(&amp;amp;dev-&amp;gt;lock);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/*-------------------------------------------------------------------------*/&lt;/P&gt;&lt;P&gt;static int&lt;BR /&gt;printer_open(struct inode *inode, struct file *fd)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev;&lt;BR /&gt;unsigned long flags;&lt;BR /&gt;int ret = -EBUSY;&lt;/P&gt;&lt;P&gt;dev = container_of(inode-&amp;gt;i_cdev, struct printer_dev, printer_cdev);&lt;/P&gt;&lt;P&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;interface &amp;lt; 0) {&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;return -ENODEV;&lt;BR /&gt;}&lt;BR /&gt;printk(KERN_INFO "Opening Sonosa Driver=================\n");&lt;BR /&gt;if (!dev-&amp;gt;printer_cdev_open) {&lt;BR /&gt;dev-&amp;gt;printer_cdev_open = 1;&lt;BR /&gt;fd-&amp;gt;private_data = dev;&lt;BR /&gt;ret = 0;&lt;BR /&gt;/* Change the printer status to show that it's on-line. */&lt;BR /&gt;dev-&amp;gt;printer_status |= PRINTER_SELECTED;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;kref_get(&amp;amp;dev-&amp;gt;kref);&lt;BR /&gt;DBG(dev, "Sonosa_open returned %x\n", ret);&lt;BR /&gt;return ret;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static int&lt;BR /&gt;printer_close(struct inode *inode, struct file *fd)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = fd-&amp;gt;private_data;&lt;BR /&gt;unsigned long flags;&lt;/P&gt;&lt;P&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;dev-&amp;gt;printer_cdev_open = 0;&lt;BR /&gt;fd-&amp;gt;private_data = NULL;&lt;BR /&gt;/* Change printer status to show that the printer is off-line. */&lt;BR /&gt;dev-&amp;gt;printer_status &amp;amp;= ~PRINTER_SELECTED;&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;kref_put(&amp;amp;dev-&amp;gt;kref, printer_dev_free);&lt;BR /&gt;DBG(dev, "Sonosa_close\n");&lt;/P&gt;&lt;P&gt;return 0;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* This function must be called with interrupts turned off. */&lt;BR /&gt;static void&lt;BR /&gt;setup_rx_reqs(struct printer_dev *dev)&lt;BR /&gt;{&lt;BR /&gt;struct usb_request *req;&lt;/P&gt;&lt;P&gt;while (likely(!list_empty(&amp;amp;dev-&amp;gt;rx_reqs))) {&lt;BR /&gt;int error;&lt;/P&gt;&lt;P&gt;req = container_of(dev-&amp;gt;rx_reqs.next,&lt;BR /&gt;struct usb_request, list);&lt;BR /&gt;list_del_init(&amp;amp;req-&amp;gt;list);&lt;/P&gt;&lt;P&gt;/* The USB Host sends us whatever amount of data it wants to&lt;BR /&gt;* so we always set the length field to the full USB_BUFSIZE.&lt;BR /&gt;* If the amount of data is more than the read() caller asked&lt;BR /&gt;* for it will be stored in the request buffer until it is&lt;BR /&gt;* asked for by read().&lt;BR /&gt;*/&lt;BR /&gt;req-&amp;gt;length = USB_BUFSIZE;&lt;BR /&gt;req-&amp;gt;complete = rx_complete;&lt;/P&gt;&lt;P&gt;/* here, we unlock, and only unlock, to avoid deadlock. */&lt;BR /&gt;spin_unlock(&amp;amp;dev-&amp;gt;lock);&lt;BR /&gt;error = usb_ep_queue(dev-&amp;gt;out_ep, req, GFP_ATOMIC);&lt;BR /&gt;spin_lock(&amp;amp;dev-&amp;gt;lock);&lt;BR /&gt;if (error) {&lt;BR /&gt;DBG(dev, "rx submit --&amp;gt; %d\n", error);&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;break;&lt;BR /&gt;}&lt;BR /&gt;/* if the req is empty, then add it into dev-&amp;gt;rx_reqs_active. */&lt;BR /&gt;else if (list_empty(&amp;amp;req-&amp;gt;list))&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs_active);&lt;BR /&gt;}&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static ssize_t&lt;BR /&gt;printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = fd-&amp;gt;private_data;&lt;BR /&gt;unsigned long flags;&lt;BR /&gt;size_t size;&lt;BR /&gt;size_t bytes_copied;&lt;BR /&gt;struct usb_request *req;&lt;BR /&gt;/* This is a pointer to the current USB rx request. */&lt;BR /&gt;struct usb_request *current_rx_req;&lt;BR /&gt;/* This is the number of bytes in the current rx buffer. */&lt;BR /&gt;size_t current_rx_bytes;&lt;BR /&gt;/* This is a pointer to the current rx buffer. */&lt;BR /&gt;u8 *current_rx_buf;&lt;/P&gt;&lt;P&gt;if (len == 0)&lt;BR /&gt;return -EINVAL;&lt;/P&gt;&lt;P&gt;DBG(dev, "sonosa_read trying to read %d bytes\n", (int)len);&lt;BR /&gt;printk(KERN_INFO "Sonosa Read=================:\n");&lt;BR /&gt;mutex_lock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;interface &amp;lt; 0) {&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return -ENODEV;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* We will use this flag later to check if a printer reset happened&lt;BR /&gt;* after we turn interrupts back on.&lt;BR /&gt;*/&lt;BR /&gt;dev-&amp;gt;reset_printer = 0;&lt;/P&gt;&lt;P&gt;setup_rx_reqs(dev);&lt;/P&gt;&lt;P&gt;bytes_copied = 0;&lt;BR /&gt;current_rx_req = dev-&amp;gt;current_rx_req;&lt;BR /&gt;current_rx_bytes = dev-&amp;gt;current_rx_bytes;&lt;BR /&gt;current_rx_buf = dev-&amp;gt;current_rx_buf;&lt;BR /&gt;dev-&amp;gt;current_rx_req = NULL;&lt;BR /&gt;dev-&amp;gt;current_rx_bytes = 0;&lt;BR /&gt;dev-&amp;gt;current_rx_buf = NULL;&lt;/P&gt;&lt;P&gt;/* Check if there is any data in the read buffers. Please note that&lt;BR /&gt;* current_rx_bytes is the number of bytes in the current rx buffer.&lt;BR /&gt;* If it is zero then check if there are any other rx_buffers that&lt;BR /&gt;* are on the completed list. We are only out of data if all rx&lt;BR /&gt;* buffers are empty.&lt;BR /&gt;*/&lt;BR /&gt;if ((current_rx_bytes == 0) &amp;amp;&amp;amp;&lt;BR /&gt;(likely(list_empty(&amp;amp;dev-&amp;gt;rx_buffers)))) {&lt;BR /&gt;/* Turn interrupts back on before sleeping. */&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;/*&lt;BR /&gt;* If no data is available check if this is a NON-Blocking&lt;BR /&gt;* call or not.&lt;BR /&gt;*/&lt;BR /&gt;if (fd-&amp;gt;f_flags &amp;amp; (O_NONBLOCK|O_NDELAY)) {&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return -EAGAIN;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* Sleep until data is available */&lt;BR /&gt;wait_event_interruptible(dev-&amp;gt;rx_wait,&lt;BR /&gt;(likely(!list_empty(&amp;amp;dev-&amp;gt;rx_buffers))));&lt;BR /&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* We have data to return then copy it to the caller's buffer.*/&lt;BR /&gt;while ((current_rx_bytes || likely(!list_empty(&amp;amp;dev-&amp;gt;rx_buffers)))&lt;BR /&gt;&amp;amp;&amp;amp; len) {&lt;BR /&gt;if (current_rx_bytes == 0) {&lt;BR /&gt;req = container_of(dev-&amp;gt;rx_buffers.next,&lt;BR /&gt;struct usb_request, list);&lt;BR /&gt;list_del_init(&amp;amp;req-&amp;gt;list);&lt;/P&gt;&lt;P&gt;if (req-&amp;gt;actual &amp;amp;&amp;amp; req-&amp;gt;buf) {&lt;BR /&gt;current_rx_req = req;&lt;BR /&gt;current_rx_bytes = req-&amp;gt;actual;&lt;BR /&gt;current_rx_buf = req-&amp;gt;buf;&lt;BR /&gt;} else {&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;continue;&lt;BR /&gt;}&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* Don't leave irqs off while doing memory copies */&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (len &amp;gt; current_rx_bytes)&lt;BR /&gt;size = current_rx_bytes;&lt;BR /&gt;else&lt;BR /&gt;size = len;&lt;/P&gt;&lt;P&gt;size -= copy_to_user(buf, current_rx_buf, size);&lt;BR /&gt;bytes_copied += size;&lt;BR /&gt;len -= size;&lt;BR /&gt;buf += size;&lt;/P&gt;&lt;P&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;/* We've disconnected or reset so return. */&lt;BR /&gt;if (dev-&amp;gt;reset_printer) {&lt;BR /&gt;list_add(&amp;amp;current_rx_req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return -EAGAIN;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* If we not returning all the data left in this RX request&lt;BR /&gt;* buffer then adjust the amount of data left in the buffer.&lt;BR /&gt;* Othewise if we are done with this RX request buffer then&lt;BR /&gt;* requeue it to get any incoming data from the USB host.&lt;BR /&gt;*/&lt;BR /&gt;if (size &amp;lt; current_rx_bytes) {&lt;BR /&gt;current_rx_bytes -= size;&lt;BR /&gt;current_rx_buf += size;&lt;BR /&gt;} else {&lt;BR /&gt;list_add(&amp;amp;current_rx_req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;current_rx_bytes = 0;&lt;BR /&gt;current_rx_buf = NULL;&lt;BR /&gt;current_rx_req = NULL;&lt;BR /&gt;}&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;dev-&amp;gt;current_rx_req = current_rx_req;&lt;BR /&gt;dev-&amp;gt;current_rx_bytes = current_rx_bytes;&lt;BR /&gt;dev-&amp;gt;current_rx_buf = current_rx_buf;&lt;/P&gt;&lt;P&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;/P&gt;&lt;P&gt;DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied);&lt;/P&gt;&lt;P&gt;if (bytes_copied)&lt;BR /&gt;return bytes_copied;&lt;BR /&gt;else&lt;BR /&gt;return -EAGAIN;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static ssize_t&lt;BR /&gt;printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = fd-&amp;gt;private_data;&lt;BR /&gt;unsigned long flags;&lt;BR /&gt;size_t size; /* Amount of data in a TX request. */&lt;BR /&gt;size_t bytes_copied = 0;&lt;BR /&gt;struct usb_request *req;&lt;BR /&gt;int value;&lt;/P&gt;&lt;P&gt;DBG(dev, "printer_write trying to send %d bytes\n", (int)len);&lt;/P&gt;&lt;P&gt;if (len == 0)&lt;BR /&gt;return -EINVAL;&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;interface &amp;lt; 0) {&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return -ENODEV;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* Check if a printer reset happens while we have interrupts on */&lt;BR /&gt;dev-&amp;gt;reset_printer = 0;&lt;/P&gt;&lt;P&gt;/* Check if there is any available write buffers */&lt;BR /&gt;if (likely(list_empty(&amp;amp;dev-&amp;gt;tx_reqs))) {&lt;BR /&gt;/* Turn interrupts back on before sleeping. */&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;/*&lt;BR /&gt;* If write buffers are available check if this is&lt;BR /&gt;* a NON-Blocking call or not.&lt;BR /&gt;*/&lt;BR /&gt;if (fd-&amp;gt;f_flags &amp;amp; (O_NONBLOCK|O_NDELAY)) {&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return -EAGAIN;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* Sleep until a write buffer is available */&lt;BR /&gt;wait_event_interruptible(dev-&amp;gt;tx_wait,&lt;BR /&gt;(likely(!list_empty(&amp;amp;dev-&amp;gt;tx_reqs))));&lt;BR /&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;while (likely(!list_empty(&amp;amp;dev-&amp;gt;tx_reqs)) &amp;amp;&amp;amp; len) {&lt;/P&gt;&lt;P&gt;if (len &amp;gt; USB_BUFSIZE)&lt;BR /&gt;size = USB_BUFSIZE;&lt;BR /&gt;else&lt;BR /&gt;size = len;&lt;/P&gt;&lt;P&gt;req = container_of(dev-&amp;gt;tx_reqs.next, struct usb_request,&lt;BR /&gt;list);&lt;BR /&gt;list_del_init(&amp;amp;req-&amp;gt;list);&lt;/P&gt;&lt;P&gt;req-&amp;gt;complete = tx_complete;&lt;BR /&gt;req-&amp;gt;length = size;&lt;/P&gt;&lt;P&gt;/* Check if we need to send a zero length packet. */&lt;BR /&gt;if (len &amp;gt; size)&lt;BR /&gt;/* They will be more TX requests so no yet. */&lt;BR /&gt;req-&amp;gt;zero = 0;&lt;BR /&gt;else&lt;BR /&gt;/* If the data amount is not a multiple of the&lt;BR /&gt;* maxpacket size then send a zero length packet.&lt;BR /&gt;*/&lt;BR /&gt;req-&amp;gt;zero = ((len % dev-&amp;gt;in_ep-&amp;gt;maxpacket) == 0);&lt;/P&gt;&lt;P&gt;/* Don't leave irqs off while doing memory copies */&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (copy_from_user(req-&amp;gt;buf, buf, size)) {&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;tx_reqs);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return bytes_copied;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;bytes_copied += size;&lt;BR /&gt;len -= size;&lt;BR /&gt;buf += size;&lt;/P&gt;&lt;P&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;/* We've disconnected or reset so free the req and buffer */&lt;BR /&gt;if (dev-&amp;gt;reset_printer) {&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;tx_reqs);&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return -EAGAIN;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;tx_reqs_active);&lt;/P&gt;&lt;P&gt;/* here, we unlock, and only unlock, to avoid deadlock. */&lt;BR /&gt;spin_unlock(&amp;amp;dev-&amp;gt;lock);&lt;BR /&gt;value = usb_ep_queue(dev-&amp;gt;in_ep, req, GFP_ATOMIC);&lt;BR /&gt;spin_lock(&amp;amp;dev-&amp;gt;lock);&lt;BR /&gt;if (value) {&lt;BR /&gt;list_move(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;tx_reqs);&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return -EAGAIN;&lt;BR /&gt;}&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;/P&gt;&lt;P&gt;DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied);&lt;/P&gt;&lt;P&gt;if (bytes_copied)&lt;BR /&gt;return bytes_copied;&lt;BR /&gt;else&lt;BR /&gt;return -EAGAIN;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static int&lt;BR /&gt;printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = fd-&amp;gt;private_data;&lt;BR /&gt;struct inode *inode = file_inode(fd);&lt;BR /&gt;unsigned long flags;&lt;BR /&gt;int tx_list_empty;&lt;/P&gt;&lt;P&gt;inode_lock(inode);&lt;BR /&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;interface &amp;lt; 0) {&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;inode_unlock(inode);&lt;BR /&gt;return -ENODEV;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;tx_list_empty = (likely(list_empty(&amp;amp;dev-&amp;gt;tx_reqs)));&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (!tx_list_empty) {&lt;BR /&gt;/* Sleep until all data has been sent */&lt;BR /&gt;wait_event_interruptible(dev-&amp;gt;tx_flush_wait,&lt;BR /&gt;(likely(list_empty(&amp;amp;dev-&amp;gt;tx_reqs_active))));&lt;BR /&gt;}&lt;BR /&gt;inode_unlock(inode);&lt;/P&gt;&lt;P&gt;return 0;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static __poll_t&lt;BR /&gt;printer_poll(struct file *fd, poll_table *wait)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = fd-&amp;gt;private_data;&lt;BR /&gt;unsigned long flags;&lt;BR /&gt;__poll_t status = 0;&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;interface &amp;lt; 0) {&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;return EPOLLERR | EPOLLHUP;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;setup_rx_reqs(dev);&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;mutex_unlock(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;/P&gt;&lt;P&gt;poll_wait(fd, &amp;amp;dev-&amp;gt;rx_wait, wait);&lt;BR /&gt;poll_wait(fd, &amp;amp;dev-&amp;gt;tx_wait, wait);&lt;/P&gt;&lt;P&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;if (likely(!list_empty(&amp;amp;dev-&amp;gt;tx_reqs)))&lt;BR /&gt;status |= EPOLLOUT | EPOLLWRNORM;&lt;/P&gt;&lt;P&gt;if (likely(dev-&amp;gt;current_rx_bytes) ||&lt;BR /&gt;likely(!list_empty(&amp;amp;dev-&amp;gt;rx_buffers)))&lt;BR /&gt;status |= EPOLLIN | EPOLLRDNORM;&lt;/P&gt;&lt;P&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;return status;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static long&lt;BR /&gt;printer_ioctl(struct file *fd, unsigned int code, unsigned long arg)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = fd-&amp;gt;private_data;&lt;BR /&gt;unsigned long flags;&lt;BR /&gt;int status = 0;&lt;/P&gt;&lt;P&gt;DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg);&lt;/P&gt;&lt;P&gt;/* handle ioctls */&lt;/P&gt;&lt;P&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;interface &amp;lt; 0) {&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;return -ENODEV;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;switch (code) {&lt;BR /&gt;case GADGET_GET_PRINTER_STATUS:&lt;BR /&gt;status = (int)dev-&amp;gt;printer_status;&lt;BR /&gt;break;&lt;BR /&gt;case GADGET_SET_PRINTER_STATUS:&lt;BR /&gt;dev-&amp;gt;printer_status = (u8)arg;&lt;BR /&gt;break;&lt;BR /&gt;default:&lt;BR /&gt;/* could not handle ioctl */&lt;BR /&gt;DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n",&lt;BR /&gt;code);&lt;BR /&gt;status = -ENOTTY;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;/P&gt;&lt;P&gt;return status;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* used after endpoint configuration */&lt;BR /&gt;static const struct file_operations printer_io_operations = {&lt;BR /&gt;.owner = THIS_MODULE,&lt;BR /&gt;.open = printer_open,&lt;BR /&gt;.read = printer_read,&lt;BR /&gt;.write = printer_write,&lt;BR /&gt;.fsync = printer_fsync,&lt;BR /&gt;.poll = printer_poll,&lt;BR /&gt;.unlocked_ioctl = printer_ioctl,&lt;BR /&gt;.release = printer_close,&lt;BR /&gt;.llseek = noop_llseek,&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;/*-------------------------------------------------------------------------*/&lt;/P&gt;&lt;P&gt;static int&lt;BR /&gt;set_printer_interface(struct printer_dev *dev)&lt;BR /&gt;{&lt;BR /&gt;int result = 0;&lt;/P&gt;&lt;P&gt;dev-&amp;gt;in_ep-&amp;gt;desc = ep_desc(dev-&amp;gt;gadget, &amp;amp;fs_ep_in_desc, &amp;amp;hs_ep_in_desc,&lt;BR /&gt;&amp;amp;ss_ep_in_desc);&lt;BR /&gt;dev-&amp;gt;in_ep-&amp;gt;driver_data = dev;&lt;/P&gt;&lt;P&gt;dev-&amp;gt;out_ep-&amp;gt;desc = ep_desc(dev-&amp;gt;gadget, &amp;amp;fs_ep_out_desc,&lt;BR /&gt;&amp;amp;hs_ep1_out_desc,&amp;amp;ss_ep_out_desc);&lt;BR /&gt;dev-&amp;gt;out_ep-&amp;gt;driver_data = dev;&lt;/P&gt;&lt;P&gt;result = usb_ep_enable(dev-&amp;gt;in_ep);&lt;BR /&gt;if (result != 0) {&lt;BR /&gt;DBG(dev, "enable %s --&amp;gt; %d\n", dev-&amp;gt;in_ep-&amp;gt;name, result);&lt;BR /&gt;printk(KERN_INFO, "Enabling Endpoints+++++++++++++++++++");&lt;BR /&gt;goto done;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;result = usb_ep_enable(dev-&amp;gt;out_ep);&lt;BR /&gt;if (result != 0) {&lt;BR /&gt;DBG(dev, "enable %s --&amp;gt; %d\n", dev-&amp;gt;out_ep-&amp;gt;name, result);&lt;BR /&gt;goto done;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;done:&lt;BR /&gt;/* on error, disable any endpoints */&lt;BR /&gt;if (result != 0) {&lt;BR /&gt;(void) usb_ep_disable(dev-&amp;gt;in_ep);&lt;BR /&gt;(void) usb_ep_disable(dev-&amp;gt;out_ep);&lt;BR /&gt;dev-&amp;gt;in_ep-&amp;gt;desc = NULL;&lt;BR /&gt;dev-&amp;gt;out_ep-&amp;gt;desc = NULL;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* caller is responsible for cleanup on error */&lt;BR /&gt;return result;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void printer_reset_interface(struct printer_dev *dev)&lt;BR /&gt;{&lt;BR /&gt;unsigned long flags;&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;interface &amp;lt; 0)&lt;BR /&gt;return;&lt;/P&gt;&lt;P&gt;DBG(dev, "%s\n", __func__);&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;in_ep-&amp;gt;desc)&lt;BR /&gt;usb_ep_disable(dev-&amp;gt;in_ep);&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;out_ep-&amp;gt;desc)&lt;BR /&gt;usb_ep_disable(dev-&amp;gt;out_ep);&lt;/P&gt;&lt;P&gt;spin_lock_irqsave(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;dev-&amp;gt;in_ep-&amp;gt;desc = NULL;&lt;BR /&gt;dev-&amp;gt;out_ep-&amp;gt;desc = NULL;&lt;BR /&gt;dev-&amp;gt;interface = -1;&lt;BR /&gt;spin_unlock_irqrestore(&amp;amp;dev-&amp;gt;lock, flags);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* Change our operational Interface. */&lt;BR /&gt;static int set_interface(struct printer_dev *dev, unsigned number)&lt;BR /&gt;{&lt;BR /&gt;int result = 0;&lt;/P&gt;&lt;P&gt;/* Free the current interface */&lt;BR /&gt;printer_reset_interface(dev);&lt;/P&gt;&lt;P&gt;result = set_printer_interface(dev);&lt;BR /&gt;if (result)&lt;BR /&gt;printer_reset_interface(dev);&lt;BR /&gt;else&lt;BR /&gt;dev-&amp;gt;interface = number;&lt;/P&gt;&lt;P&gt;if (!result)&lt;BR /&gt;INFO(dev, "Using interface %x\n", number);&lt;/P&gt;&lt;P&gt;return result;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void printer_soft_reset(struct printer_dev *dev)&lt;BR /&gt;{&lt;BR /&gt;struct usb_request *req;&lt;/P&gt;&lt;P&gt;INFO(dev, "Received Printer Reset Request\n");&lt;/P&gt;&lt;P&gt;if (usb_ep_disable(dev-&amp;gt;in_ep))&lt;BR /&gt;DBG(dev, "Failed to disable USB in_ep\n");&lt;BR /&gt;if (usb_ep_disable(dev-&amp;gt;out_ep))&lt;BR /&gt;DBG(dev, "Failed to disable USB out_ep\n");&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;current_rx_req != NULL) {&lt;BR /&gt;list_add(&amp;amp;dev-&amp;gt;current_rx_req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;dev-&amp;gt;current_rx_req = NULL;&lt;BR /&gt;}&lt;BR /&gt;dev-&amp;gt;current_rx_bytes = 0;&lt;BR /&gt;dev-&amp;gt;current_rx_buf = NULL;&lt;BR /&gt;dev-&amp;gt;reset_printer = 1;&lt;/P&gt;&lt;P&gt;while (likely(!(list_empty(&amp;amp;dev-&amp;gt;rx_buffers)))) {&lt;BR /&gt;req = container_of(dev-&amp;gt;rx_buffers.next, struct usb_request,&lt;BR /&gt;list);&lt;BR /&gt;list_del_init(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;while (likely(!(list_empty(&amp;amp;dev-&amp;gt;rx_reqs_active)))) {&lt;BR /&gt;req = container_of(dev-&amp;gt;rx_buffers.next, struct usb_request,&lt;BR /&gt;list);&lt;BR /&gt;list_del_init(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;while (likely(!(list_empty(&amp;amp;dev-&amp;gt;tx_reqs_active)))) {&lt;BR /&gt;req = container_of(dev-&amp;gt;tx_reqs_active.next,&lt;BR /&gt;struct usb_request, list);&lt;BR /&gt;list_del_init(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;tx_reqs);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;if (usb_ep_enable(dev-&amp;gt;in_ep))&lt;BR /&gt;DBG(dev, "Failed to enable USB in_ep\n");&lt;BR /&gt;if (usb_ep_enable(dev-&amp;gt;out_ep))&lt;BR /&gt;DBG(dev, "Failed to enable USB out_ep\n");&lt;/P&gt;&lt;P&gt;wake_up_interruptible(&amp;amp;dev-&amp;gt;rx_wait);&lt;BR /&gt;wake_up_interruptible(&amp;amp;dev-&amp;gt;tx_wait);&lt;BR /&gt;wake_up_interruptible(&amp;amp;dev-&amp;gt;tx_flush_wait);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/*-------------------------------------------------------------------------*/&lt;/P&gt;&lt;P&gt;static bool gprinter_req_match(struct usb_function *f,&lt;BR /&gt;const struct usb_ctrlrequest *ctrl,&lt;BR /&gt;bool config0)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = func_to_printer(f);&lt;BR /&gt;u16 w_index = le16_to_cpu(ctrl-&amp;gt;wIndex);&lt;BR /&gt;u16 w_value = le16_to_cpu(ctrl-&amp;gt;wValue);&lt;BR /&gt;u16 w_length = le16_to_cpu(ctrl-&amp;gt;wLength);&lt;/P&gt;&lt;P&gt;if (config0)&lt;BR /&gt;return false;&lt;/P&gt;&lt;P&gt;if ((ctrl-&amp;gt;bRequestType &amp;amp; USB_RECIP_MASK) != USB_RECIP_INTERFACE ||&lt;BR /&gt;(ctrl-&amp;gt;bRequestType &amp;amp; USB_TYPE_MASK) != USB_TYPE_CLASS)&lt;BR /&gt;return false;&lt;/P&gt;&lt;P&gt;switch (ctrl-&amp;gt;bRequest) {&lt;BR /&gt;case GET_DEVICE_ID:&lt;BR /&gt;w_index &amp;gt;&amp;gt;= 8;&lt;BR /&gt;if (USB_DIR_IN &amp;amp; ctrl-&amp;gt;bRequestType)&lt;BR /&gt;break;&lt;BR /&gt;return false;&lt;BR /&gt;case GET_PORT_STATUS:&lt;BR /&gt;if (!w_value &amp;amp;&amp;amp; w_length == 1 &amp;amp;&amp;amp;&lt;BR /&gt;(USB_DIR_IN &amp;amp; ctrl-&amp;gt;bRequestType))&lt;BR /&gt;break;&lt;BR /&gt;return false;&lt;BR /&gt;case SOFT_RESET:&lt;BR /&gt;if (!w_value &amp;amp;&amp;amp; !w_length &amp;amp;&amp;amp;&lt;BR /&gt;!(USB_DIR_IN &amp;amp; ctrl-&amp;gt;bRequestType))&lt;BR /&gt;break;&lt;BR /&gt;fallthrough;&lt;BR /&gt;default:&lt;BR /&gt;return false;&lt;BR /&gt;}&lt;BR /&gt;return w_index == dev-&amp;gt;interface;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/*&lt;BR /&gt;* The setup() callback implements all the ep0 functionality that's not&lt;BR /&gt;* handled lower down.&lt;BR /&gt;*/&lt;BR /&gt;static int printer_func_setup(struct usb_function *f,&lt;BR /&gt;const struct usb_ctrlrequest *ctrl)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = func_to_printer(f);&lt;BR /&gt;struct usb_composite_dev *cdev = f-&amp;gt;config-&amp;gt;cdev;&lt;BR /&gt;struct usb_request *req = cdev-&amp;gt;req;&lt;BR /&gt;u8 *buf = req-&amp;gt;buf;&lt;BR /&gt;int value = -EOPNOTSUPP;&lt;BR /&gt;u16 wIndex = le16_to_cpu(ctrl-&amp;gt;wIndex);&lt;BR /&gt;u16 wValue = le16_to_cpu(ctrl-&amp;gt;wValue);&lt;BR /&gt;u16 wLength = le16_to_cpu(ctrl-&amp;gt;wLength);&lt;/P&gt;&lt;P&gt;DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n",&lt;BR /&gt;ctrl-&amp;gt;bRequestType, ctrl-&amp;gt;bRequest, wValue, wIndex, wLength);&lt;/P&gt;&lt;P&gt;switch (ctrl-&amp;gt;bRequestType&amp;amp;USB_TYPE_MASK) {&lt;BR /&gt;case USB_TYPE_CLASS:&lt;BR /&gt;switch (ctrl-&amp;gt;bRequest) {&lt;BR /&gt;case GET_DEVICE_ID: /* Get the IEEE-1284 PNP String */&lt;BR /&gt;/* Only one printer interface is supported. */&lt;BR /&gt;if ((wIndex&amp;gt;&amp;gt;8) != dev-&amp;gt;interface)&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;if (!dev-&amp;gt;pnp_string) {&lt;BR /&gt;value = 0;&lt;BR /&gt;break;&lt;BR /&gt;}&lt;BR /&gt;value = strlen(dev-&amp;gt;pnp_string);&lt;BR /&gt;buf[0] = (value &amp;gt;&amp;gt; &lt;LI-EMOJI id="lia_smiling-face-with-sunglasses" title=":smiling_face_with_sunglasses:"&gt;&lt;/LI-EMOJI&gt; &amp;amp; 0xFF;&lt;BR /&gt;buf[1] = value &amp;amp; 0xFF;&lt;BR /&gt;memcpy(buf + 2, dev-&amp;gt;pnp_string, value);&lt;BR /&gt;DBG(dev, "1284 PNP String: %x %s\n", value,&lt;BR /&gt;dev-&amp;gt;pnp_string);&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;case GET_PORT_STATUS: /* Get Port Status */&lt;BR /&gt;/* Only one printer interface is supported. */&lt;BR /&gt;if (wIndex != dev-&amp;gt;interface)&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;buf[0] = dev-&amp;gt;printer_status;&lt;BR /&gt;value = min_t(u16, wLength, 1);&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;case SOFT_RESET: /* Soft Reset */&lt;BR /&gt;/* Only one printer interface is supported. */&lt;BR /&gt;if (wIndex != dev-&amp;gt;interface)&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;printer_soft_reset(dev);&lt;/P&gt;&lt;P&gt;value = 0;&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;default:&lt;BR /&gt;goto unknown;&lt;BR /&gt;}&lt;BR /&gt;break;&lt;/P&gt;&lt;P&gt;default:&lt;BR /&gt;unknown:&lt;BR /&gt;VDBG(dev,&lt;BR /&gt;"unknown ctrl req%02x.%02x v%04x i%04x l%d\n",&lt;BR /&gt;ctrl-&amp;gt;bRequestType, ctrl-&amp;gt;bRequest,&lt;BR /&gt;wValue, wIndex, wLength);&lt;BR /&gt;break;&lt;BR /&gt;}&lt;BR /&gt;/* host either stalls (value &amp;lt; 0) or reports success */&lt;BR /&gt;if (value &amp;gt;= 0) {&lt;BR /&gt;req-&amp;gt;length = value;&lt;BR /&gt;req-&amp;gt;zero = value &amp;lt; wLength;&lt;BR /&gt;value = usb_ep_queue(cdev-&amp;gt;gadget-&amp;gt;ep0, req, GFP_ATOMIC);&lt;BR /&gt;if (value &amp;lt; 0) {&lt;BR /&gt;ERROR(dev, "%s:%d Error!\n", __func__, __LINE__);&lt;BR /&gt;req-&amp;gt;status = 0;&lt;BR /&gt;}&lt;BR /&gt;}&lt;BR /&gt;return value;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static int printer_func_bind(struct usb_configuration *c,&lt;BR /&gt;struct usb_function *f)&lt;BR /&gt;{&lt;BR /&gt;struct usb_gadget *gadget = c-&amp;gt;cdev-&amp;gt;gadget;&lt;BR /&gt;struct printer_dev *dev = func_to_printer(f);&lt;BR /&gt;struct device *pdev;&lt;BR /&gt;struct usb_composite_dev *cdev = c-&amp;gt;cdev;&lt;BR /&gt;struct usb_ep *in_ep;&lt;BR /&gt;struct usb_ep *out_ep = NULL;&lt;BR /&gt;struct usb_request *req;&lt;BR /&gt;int id;&lt;BR /&gt;int ret;&lt;BR /&gt;u32 i;&lt;BR /&gt;dev_t devt;&lt;BR /&gt;id = usb_interface_id(c, f);&lt;BR /&gt;if (id &amp;lt; 0)&lt;BR /&gt;return id;&lt;BR /&gt;intf_desc.bInterfaceNumber = id;&lt;/P&gt;&lt;P&gt;/* finish hookup to lower layer ... */&lt;BR /&gt;dev-&amp;gt;gadget = gadget;&lt;/P&gt;&lt;P&gt;/* all we really need is bulk IN/OUT */&lt;BR /&gt;in_ep = usb_ep_autoconfig(cdev-&amp;gt;gadget, &amp;amp;fs_ep_in_desc);&lt;BR /&gt;if (!in_ep) {&lt;BR /&gt;autoconf_fail:&lt;BR /&gt;dev_err(&amp;amp;cdev-&amp;gt;gadget-&amp;gt;dev, "can't autoconfigure on %s\n",&lt;BR /&gt;cdev-&amp;gt;gadget-&amp;gt;name);&lt;BR /&gt;return -ENODEV;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;out_ep = usb_ep_autoconfig(cdev-&amp;gt;gadget, &amp;amp;fs_ep_out_desc);&lt;BR /&gt;if (!out_ep)&lt;BR /&gt;goto autoconf_fail;&lt;/P&gt;&lt;P&gt;/* assumes that all endpoints are dual-speed */&lt;BR /&gt;hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;&lt;BR /&gt;// hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;&lt;BR /&gt;hs_ep1_out_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;&lt;BR /&gt;// hs_ep1_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;&lt;BR /&gt;ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;&lt;BR /&gt;ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;&lt;/P&gt;&lt;P&gt;ret = usb_assign_descriptors(f, fs_printer_function,&lt;BR /&gt;hs_printer_function, ss_printer_function,&lt;BR /&gt;ss_printer_function);&lt;BR /&gt;if (ret)&lt;BR /&gt;return ret;&lt;/P&gt;&lt;P&gt;dev-&amp;gt;in_ep = in_ep;&lt;BR /&gt;dev-&amp;gt;out_ep = out_ep;&lt;/P&gt;&lt;P&gt;ret = -ENOMEM;&lt;BR /&gt;for (i = 0; i &amp;lt; dev-&amp;gt;q_len; i++) {&lt;BR /&gt;req = printer_req_alloc(dev-&amp;gt;in_ep, USB_BUFSIZE, GFP_KERNEL);&lt;BR /&gt;if (!req)&lt;BR /&gt;goto fail_tx_reqs;&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;tx_reqs);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;for (i = 0; i &amp;lt; dev-&amp;gt;q_len; i++) {&lt;BR /&gt;req = printer_req_alloc(dev-&amp;gt;out_ep, USB_BUFSIZE, GFP_KERNEL);&lt;BR /&gt;if (!req)&lt;BR /&gt;goto fail_rx_reqs;&lt;BR /&gt;list_add(&amp;amp;req-&amp;gt;list, &amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/* Setup the sysfs files for the printer gadget. */&lt;BR /&gt;devt = MKDEV(major, dev-&amp;gt;minor);&lt;BR /&gt;pdev = device_create(usb_gadget_class, NULL, devt,&lt;BR /&gt;NULL, "sonosa_acu%d", dev-&amp;gt;minor);&lt;BR /&gt;if (IS_ERR(pdev)) {&lt;BR /&gt;ERROR(dev, "Failed to create device: sonosa_acu\n");&lt;BR /&gt;ret = PTR_ERR(pdev);&lt;BR /&gt;goto fail_rx_reqs;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;/*&lt;BR /&gt;* Register a character device as an interface to a user mode&lt;BR /&gt;* program that handles the printer specific functionality.&lt;BR /&gt;*/&lt;BR /&gt;cdev_init(&amp;amp;dev-&amp;gt;printer_cdev, &amp;amp;printer_io_operations);&lt;BR /&gt;dev-&amp;gt;printer_cdev.owner = THIS_MODULE;&lt;BR /&gt;ret = cdev_add(&amp;amp;dev-&amp;gt;printer_cdev, devt, 1);&lt;BR /&gt;if (ret) {&lt;BR /&gt;ERROR(dev, "Failed to open char device\n");&lt;BR /&gt;goto fail_cdev_add;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;return 0;&lt;/P&gt;&lt;P&gt;fail_cdev_add:&lt;BR /&gt;device_destroy(usb_gadget_class, devt);&lt;/P&gt;&lt;P&gt;fail_rx_reqs:&lt;BR /&gt;while (!list_empty(&amp;amp;dev-&amp;gt;rx_reqs)) {&lt;BR /&gt;req = container_of(dev-&amp;gt;rx_reqs.next, struct usb_request, list);&lt;BR /&gt;list_del(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;printer_req_free(dev-&amp;gt;out_ep, req);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;fail_tx_reqs:&lt;BR /&gt;while (!list_empty(&amp;amp;dev-&amp;gt;tx_reqs)) {&lt;BR /&gt;req = container_of(dev-&amp;gt;tx_reqs.next, struct usb_request, list);&lt;BR /&gt;list_del(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;printer_req_free(dev-&amp;gt;in_ep, req);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;usb_free_all_descriptors(f);&lt;BR /&gt;return ret;&lt;/P&gt;&lt;P&gt;}&lt;/P&gt;&lt;P&gt;static int printer_func_set_alt(struct usb_function *f,&lt;BR /&gt;unsigned intf, unsigned alt)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = func_to_printer(f);&lt;BR /&gt;int ret = -ENOTSUPP;&lt;/P&gt;&lt;P&gt;if (!alt)&lt;BR /&gt;ret = set_interface(dev, intf);&lt;/P&gt;&lt;P&gt;return ret;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void printer_func_disable(struct usb_function *f)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = func_to_printer(f);&lt;/P&gt;&lt;P&gt;DBG(dev, "%s\n", __func__);&lt;/P&gt;&lt;P&gt;printer_reset_interface(dev);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static inline struct f_printer_opts&lt;BR /&gt;*to_f_printer_opts(struct config_item *item)&lt;BR /&gt;{&lt;BR /&gt;return container_of(to_config_group(item), struct f_printer_opts,&lt;BR /&gt;func_inst.group);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void printer_attr_release(struct config_item *item)&lt;BR /&gt;{&lt;BR /&gt;struct f_printer_opts *opts = to_f_printer_opts(item);&lt;/P&gt;&lt;P&gt;usb_put_function_instance(&amp;amp;opts-&amp;gt;func_inst);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static struct configfs_item_operations printer_item_ops = {&lt;BR /&gt;.release = printer_attr_release,&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static ssize_t f_printer_opts_pnp_string_show(struct config_item *item,&lt;BR /&gt;char *page)&lt;BR /&gt;{&lt;BR /&gt;struct f_printer_opts *opts = to_f_printer_opts(item);&lt;BR /&gt;int result = 0;&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;if (!opts-&amp;gt;pnp_string)&lt;BR /&gt;goto unlock;&lt;/P&gt;&lt;P&gt;result = strlcpy(page, opts-&amp;gt;pnp_string, PAGE_SIZE);&lt;BR /&gt;if (result &amp;gt;= PAGE_SIZE) {&lt;BR /&gt;result = PAGE_SIZE;&lt;BR /&gt;} else if (page[result - 1] != '\n' &amp;amp;&amp;amp; result + 1 &amp;lt; PAGE_SIZE) {&lt;BR /&gt;page[result++] = '\n';&lt;BR /&gt;page[result] = '\0';&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;unlock:&lt;BR /&gt;mutex_unlock(&amp;amp;opts-&amp;gt;lock);&lt;/P&gt;&lt;P&gt;return result;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static ssize_t f_printer_opts_pnp_string_store(struct config_item *item,&lt;BR /&gt;const char *page, size_t len)&lt;BR /&gt;{&lt;BR /&gt;struct f_printer_opts *opts = to_f_printer_opts(item);&lt;BR /&gt;char *new_pnp;&lt;BR /&gt;int result;&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;opts-&amp;gt;lock);&lt;/P&gt;&lt;P&gt;new_pnp = kstrndup(page, len, GFP_KERNEL);&lt;BR /&gt;if (!new_pnp) {&lt;BR /&gt;result = -ENOMEM;&lt;BR /&gt;goto unlock;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;if (opts-&amp;gt;pnp_string_allocated)&lt;BR /&gt;kfree(opts-&amp;gt;pnp_string);&lt;/P&gt;&lt;P&gt;opts-&amp;gt;pnp_string_allocated = true;&lt;BR /&gt;opts-&amp;gt;pnp_string = new_pnp;&lt;BR /&gt;result = len;&lt;BR /&gt;unlock:&lt;BR /&gt;mutex_unlock(&amp;amp;opts-&amp;gt;lock);&lt;/P&gt;&lt;P&gt;return result;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;CONFIGFS_ATTR(f_printer_opts_, pnp_string);&lt;/P&gt;&lt;P&gt;static ssize_t f_printer_opts_q_len_show(struct config_item *item,&lt;BR /&gt;char *page)&lt;BR /&gt;{&lt;BR /&gt;struct f_printer_opts *opts = to_f_printer_opts(item);&lt;BR /&gt;int result;&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;result = sprintf(page, "%d\n", opts-&amp;gt;q_len);&lt;BR /&gt;mutex_unlock(&amp;amp;opts-&amp;gt;lock);&lt;/P&gt;&lt;P&gt;return result;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static ssize_t f_printer_opts_q_len_store(struct config_item *item,&lt;BR /&gt;const char *page, size_t len)&lt;BR /&gt;{&lt;BR /&gt;struct f_printer_opts *opts = to_f_printer_opts(item);&lt;BR /&gt;int ret;&lt;BR /&gt;u16 num;&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;if (opts-&amp;gt;refcnt) {&lt;BR /&gt;ret = -EBUSY;&lt;BR /&gt;goto end;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;ret = kstrtou16(page, 0, &amp;amp;num);&lt;BR /&gt;if (ret)&lt;BR /&gt;goto end;&lt;/P&gt;&lt;P&gt;opts-&amp;gt;q_len = (unsigned)num;&lt;BR /&gt;ret = len;&lt;BR /&gt;end:&lt;BR /&gt;mutex_unlock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;return ret;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;CONFIGFS_ATTR(f_printer_opts_, q_len);&lt;/P&gt;&lt;P&gt;static struct configfs_attribute *printer_attrs[] = {&lt;BR /&gt;&amp;amp;f_printer_opts_attr_pnp_string,&lt;BR /&gt;&amp;amp;f_printer_opts_attr_q_len,&lt;BR /&gt;NULL,&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static const struct config_item_type printer_func_type = {&lt;BR /&gt;.ct_item_ops = &amp;amp;printer_item_ops,&lt;BR /&gt;.ct_attrs = printer_attrs,&lt;BR /&gt;.ct_owner = THIS_MODULE,&lt;BR /&gt;};&lt;/P&gt;&lt;P&gt;static inline int gprinter_get_minor(void)&lt;BR /&gt;{&lt;BR /&gt;int ret;&lt;/P&gt;&lt;P&gt;ret = ida_simple_get(&amp;amp;printer_ida, 0, 0, GFP_KERNEL);&lt;BR /&gt;if (ret &amp;gt;= PRINTER_MINORS) {&lt;BR /&gt;ida_simple_remove(&amp;amp;printer_ida, ret);&lt;BR /&gt;ret = -ENODEV;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;return ret;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static inline void gprinter_put_minor(int minor)&lt;BR /&gt;{&lt;BR /&gt;ida_simple_remove(&amp;amp;printer_ida, minor);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static int gprinter_setup(int);&lt;BR /&gt;static void gprinter_cleanup(void);&lt;/P&gt;&lt;P&gt;static void gprinter_free_inst(struct usb_function_instance *f)&lt;BR /&gt;{&lt;BR /&gt;struct f_printer_opts *opts;&lt;/P&gt;&lt;P&gt;opts = container_of(f, struct f_printer_opts, func_inst);&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;printer_ida_lock);&lt;/P&gt;&lt;P&gt;gprinter_put_minor(opts-&amp;gt;minor);&lt;BR /&gt;if (ida_is_empty(&amp;amp;printer_ida))&lt;BR /&gt;gprinter_cleanup();&lt;/P&gt;&lt;P&gt;mutex_unlock(&amp;amp;printer_ida_lock);&lt;/P&gt;&lt;P&gt;if (opts-&amp;gt;pnp_string_allocated)&lt;BR /&gt;kfree(opts-&amp;gt;pnp_string);&lt;BR /&gt;kfree(opts);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static struct usb_function_instance *gprinter_alloc_inst(void)&lt;BR /&gt;{&lt;BR /&gt;struct f_printer_opts *opts;&lt;BR /&gt;struct usb_function_instance *ret;&lt;BR /&gt;int status = 0;&lt;/P&gt;&lt;P&gt;opts = kzalloc(sizeof(*opts), GFP_KERNEL);&lt;BR /&gt;if (!opts)&lt;BR /&gt;return ERR_PTR(-ENOMEM);&lt;/P&gt;&lt;P&gt;mutex_init(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;opts-&amp;gt;func_inst.free_func_inst = gprinter_free_inst;&lt;BR /&gt;ret = &amp;amp;opts-&amp;gt;func_inst;&lt;/P&gt;&lt;P&gt;/* Make sure q_len is initialized, otherwise the bound device can't support read/write! */&lt;BR /&gt;opts-&amp;gt;q_len = DEFAULT_Q_LEN;&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;printer_ida_lock);&lt;/P&gt;&lt;P&gt;if (ida_is_empty(&amp;amp;printer_ida)) {&lt;BR /&gt;status = gprinter_setup(PRINTER_MINORS);&lt;BR /&gt;if (status) {&lt;BR /&gt;ret = ERR_PTR(status);&lt;BR /&gt;kfree(opts);&lt;BR /&gt;goto unlock;&lt;BR /&gt;}&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;opts-&amp;gt;minor = gprinter_get_minor();&lt;BR /&gt;if (opts-&amp;gt;minor &amp;lt; 0) {&lt;BR /&gt;ret = ERR_PTR(opts-&amp;gt;minor);&lt;BR /&gt;kfree(opts);&lt;BR /&gt;if (ida_is_empty(&amp;amp;printer_ida))&lt;BR /&gt;gprinter_cleanup();&lt;BR /&gt;goto unlock;&lt;BR /&gt;}&lt;BR /&gt;config_group_init_type_name(&amp;amp;opts-&amp;gt;func_inst.group, "",&lt;BR /&gt;&amp;amp;printer_func_type);&lt;/P&gt;&lt;P&gt;unlock:&lt;BR /&gt;mutex_unlock(&amp;amp;printer_ida_lock);&lt;BR /&gt;return ret;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void gprinter_free(struct usb_function *f)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev = func_to_printer(f);&lt;BR /&gt;struct f_printer_opts *opts;&lt;/P&gt;&lt;P&gt;opts = container_of(f-&amp;gt;fi, struct f_printer_opts, func_inst);&lt;/P&gt;&lt;P&gt;kref_put(&amp;amp;dev-&amp;gt;kref, printer_dev_free);&lt;BR /&gt;mutex_lock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;--opts-&amp;gt;refcnt;&lt;BR /&gt;mutex_unlock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void printer_func_unbind(struct usb_configuration *c,&lt;BR /&gt;struct usb_function *f)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev;&lt;BR /&gt;struct usb_request *req;&lt;/P&gt;&lt;P&gt;dev = func_to_printer(f);&lt;/P&gt;&lt;P&gt;device_destroy(usb_gadget_class, MKDEV(major, dev-&amp;gt;minor));&lt;/P&gt;&lt;P&gt;/* Remove Character Device */&lt;BR /&gt;cdev_del(&amp;amp;dev-&amp;gt;printer_cdev);&lt;/P&gt;&lt;P&gt;/* we must already have been disconnected ... no i/o may be active */&lt;BR /&gt;WARN_ON(!list_empty(&amp;amp;dev-&amp;gt;tx_reqs_active));&lt;BR /&gt;WARN_ON(!list_empty(&amp;amp;dev-&amp;gt;rx_reqs_active));&lt;/P&gt;&lt;P&gt;/* Free all memory for this driver. */&lt;BR /&gt;while (!list_empty(&amp;amp;dev-&amp;gt;tx_reqs)) {&lt;BR /&gt;req = container_of(dev-&amp;gt;tx_reqs.next, struct usb_request,&lt;BR /&gt;list);&lt;BR /&gt;list_del(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;printer_req_free(dev-&amp;gt;in_ep, req);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;if (dev-&amp;gt;current_rx_req != NULL)&lt;BR /&gt;printer_req_free(dev-&amp;gt;out_ep, dev-&amp;gt;current_rx_req);&lt;/P&gt;&lt;P&gt;while (!list_empty(&amp;amp;dev-&amp;gt;rx_reqs)) {&lt;BR /&gt;req = container_of(dev-&amp;gt;rx_reqs.next,&lt;BR /&gt;struct usb_request, list);&lt;BR /&gt;list_del(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;printer_req_free(dev-&amp;gt;out_ep, req);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;while (!list_empty(&amp;amp;dev-&amp;gt;rx_buffers)) {&lt;BR /&gt;req = container_of(dev-&amp;gt;rx_buffers.next,&lt;BR /&gt;struct usb_request, list);&lt;BR /&gt;list_del(&amp;amp;req-&amp;gt;list);&lt;BR /&gt;printer_req_free(dev-&amp;gt;out_ep, req);&lt;BR /&gt;}&lt;BR /&gt;usb_free_all_descriptors(f);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static struct usb_function *gprinter_alloc(struct usb_function_instance *fi)&lt;BR /&gt;{&lt;BR /&gt;struct printer_dev *dev;&lt;BR /&gt;struct f_printer_opts *opts;&lt;/P&gt;&lt;P&gt;opts = container_of(fi, struct f_printer_opts, func_inst);&lt;/P&gt;&lt;P&gt;mutex_lock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;if (opts-&amp;gt;minor &amp;gt;= minors) {&lt;BR /&gt;mutex_unlock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;return ERR_PTR(-ENOENT);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;dev = kzalloc(sizeof(*dev), GFP_KERNEL);&lt;BR /&gt;if (!dev) {&lt;BR /&gt;mutex_unlock(&amp;amp;opts-&amp;gt;lock);&lt;BR /&gt;return ERR_PTR(-ENOMEM);&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;kref_init(&amp;amp;dev-&amp;gt;kref);&lt;BR /&gt;++opts-&amp;gt;refcnt;&lt;BR /&gt;dev-&amp;gt;minor = opts-&amp;gt;minor;&lt;BR /&gt;dev-&amp;gt;pnp_string = opts-&amp;gt;pnp_string;&lt;BR /&gt;dev-&amp;gt;q_len = opts-&amp;gt;q_len;&lt;BR /&gt;mutex_unlock(&amp;amp;opts-&amp;gt;lock);&lt;/P&gt;&lt;P&gt;dev-&amp;gt;function.name = "printer";&lt;BR /&gt;dev-&amp;gt;function.bind = printer_func_bind;&lt;BR /&gt;dev-&amp;gt;function.setup = printer_func_setup;&lt;BR /&gt;dev-&amp;gt;function.unbind = printer_func_unbind;&lt;BR /&gt;dev-&amp;gt;function.set_alt = printer_func_set_alt;&lt;BR /&gt;dev-&amp;gt;function.disable = printer_func_disable;&lt;BR /&gt;dev-&amp;gt;function.req_match = gprinter_req_match;&lt;BR /&gt;dev-&amp;gt;function.free_func = gprinter_free;&lt;/P&gt;&lt;P&gt;INIT_LIST_HEAD(&amp;amp;dev-&amp;gt;tx_reqs);&lt;BR /&gt;INIT_LIST_HEAD(&amp;amp;dev-&amp;gt;rx_reqs);&lt;BR /&gt;INIT_LIST_HEAD(&amp;amp;dev-&amp;gt;rx_buffers);&lt;BR /&gt;INIT_LIST_HEAD(&amp;amp;dev-&amp;gt;tx_reqs_active);&lt;BR /&gt;INIT_LIST_HEAD(&amp;amp;dev-&amp;gt;rx_reqs_active);&lt;/P&gt;&lt;P&gt;spin_lock_init(&amp;amp;dev-&amp;gt;lock);&lt;BR /&gt;mutex_init(&amp;amp;dev-&amp;gt;lock_printer_io);&lt;BR /&gt;init_waitqueue_head(&amp;amp;dev-&amp;gt;rx_wait);&lt;BR /&gt;init_waitqueue_head(&amp;amp;dev-&amp;gt;tx_wait);&lt;BR /&gt;init_waitqueue_head(&amp;amp;dev-&amp;gt;tx_flush_wait);&lt;/P&gt;&lt;P&gt;dev-&amp;gt;interface = -1;&lt;BR /&gt;dev-&amp;gt;printer_cdev_open = 0;&lt;BR /&gt;dev-&amp;gt;printer_status = PRINTER_NOT_ERROR;&lt;BR /&gt;dev-&amp;gt;current_rx_req = NULL;&lt;BR /&gt;dev-&amp;gt;current_rx_bytes = 0;&lt;BR /&gt;dev-&amp;gt;current_rx_buf = NULL;&lt;/P&gt;&lt;P&gt;return &amp;amp;dev-&amp;gt;function;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc);&lt;BR /&gt;MODULE_LICENSE("GPL");&lt;BR /&gt;MODULE_AUTHOR("Craig Nadler");&lt;/P&gt;&lt;P&gt;static int gprinter_setup(int count)&lt;BR /&gt;{&lt;BR /&gt;int status;&lt;BR /&gt;dev_t devt;&lt;/P&gt;&lt;P&gt;usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget");&lt;BR /&gt;//usb_gadget_class = class_create(THIS_MODULE, "sonosa_acu");&lt;BR /&gt;if (IS_ERR(usb_gadget_class)) {&lt;BR /&gt;status = PTR_ERR(usb_gadget_class);&lt;BR /&gt;usb_gadget_class = NULL;&lt;BR /&gt;pr_err("unable to create usb_gadget class %d\n", status);&lt;BR /&gt;return status;&lt;BR /&gt;}&lt;BR /&gt;status = alloc_chrdev_region(&amp;amp;devt, 0, count, "sonosa_acu");&lt;BR /&gt;if (status) {&lt;BR /&gt;pr_err("alloc_chrdev_region %d\n", status);&lt;BR /&gt;class_destroy(usb_gadget_class);&lt;BR /&gt;usb_gadget_class = NULL;&lt;BR /&gt;return status;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;major = MAJOR(devt);&lt;BR /&gt;minors = count;&lt;/P&gt;&lt;P&gt;return status;&lt;BR /&gt;}&lt;/P&gt;&lt;P&gt;static void gprinter_cleanup(void)&lt;BR /&gt;{&lt;BR /&gt;if (major) {&lt;BR /&gt;unregister_chrdev_region(MKDEV(major, 0), minors);&lt;BR /&gt;major = minors = 0;&lt;BR /&gt;}&lt;BR /&gt;class_destroy(usb_gadget_class);&lt;BR /&gt;usb_gadget_class= NULL;&lt;BR /&gt;}&lt;/P&gt;</description>
      <pubDate>Fri, 10 Mar 2023 11:01:44 GMT</pubDate>
      <guid>https://community.nxp.com/t5/i-MX-Processors/UVC-Gadget-Driver-Issue/m-p/1613113#M202567</guid>
      <dc:creator>sunil_embex</dc:creator>
      <dc:date>2023-03-10T11:01:44Z</dc:date>
    </item>
  </channel>
</rss>

