18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * adutux - driver for ADU devices from Ontrak Control Systems 48c2ecf20Sopenharmony_ci * This is an experimental driver. Use at your own risk. 58c2ecf20Sopenharmony_ci * This driver is not supported by Ontrak Control Systems. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2003 John Homppi (SCO, leave this notice here) 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * derived from the Lego USB Tower driver 0.56: 108c2ecf20Sopenharmony_ci * Copyright (c) 2003 David Glance <davidgsf@sourceforge.net> 118c2ecf20Sopenharmony_ci * 2001 Juergen Stuber <stuber@loria.fr> 128c2ecf20Sopenharmony_ci * that was derived from USB Skeleton driver - 0.5 138c2ecf20Sopenharmony_ci * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 218c2ecf20Sopenharmony_ci#include <linux/errno.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/usb.h> 258c2ecf20Sopenharmony_ci#include <linux/mutex.h> 268c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "John Homppi" 298c2ecf20Sopenharmony_ci#define DRIVER_DESC "adutux (see www.ontrak.net)" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* Define these values to match your device */ 328c2ecf20Sopenharmony_ci#define ADU_VENDOR_ID 0x0a07 338c2ecf20Sopenharmony_ci#define ADU_PRODUCT_ID 0x0064 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* table of devices that work with this driver */ 368c2ecf20Sopenharmony_cistatic const struct usb_device_id device_table[] = { 378c2ecf20Sopenharmony_ci { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID) }, /* ADU100 */ 388c2ecf20Sopenharmony_ci { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+20) }, /* ADU120 */ 398c2ecf20Sopenharmony_ci { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+30) }, /* ADU130 */ 408c2ecf20Sopenharmony_ci { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+100) }, /* ADU200 */ 418c2ecf20Sopenharmony_ci { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+108) }, /* ADU208 */ 428c2ecf20Sopenharmony_ci { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+118) }, /* ADU218 */ 438c2ecf20Sopenharmony_ci { } /* Terminating entry */ 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_DYNAMIC_MINORS 498c2ecf20Sopenharmony_ci#define ADU_MINOR_BASE 0 508c2ecf20Sopenharmony_ci#else 518c2ecf20Sopenharmony_ci#define ADU_MINOR_BASE 67 528c2ecf20Sopenharmony_ci#endif 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* we can have up to this number of device plugged in at once */ 558c2ecf20Sopenharmony_ci#define MAX_DEVICES 16 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define COMMAND_TIMEOUT (2*HZ) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* 608c2ecf20Sopenharmony_ci * The locking scheme is a vanilla 3-lock: 618c2ecf20Sopenharmony_ci * adu_device.buflock: A spinlock, covers what IRQs touch. 628c2ecf20Sopenharmony_ci * adutux_mutex: A Static lock to cover open_count. It would also cover 638c2ecf20Sopenharmony_ci * any globals, but we don't have them in 2.6. 648c2ecf20Sopenharmony_ci * adu_device.mtx: A mutex to hold across sleepers like copy_from_user. 658c2ecf20Sopenharmony_ci * It covers all of adu_device, except the open_count 668c2ecf20Sopenharmony_ci * and what .buflock covers. 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* Structure to hold all of our device specific stuff */ 708c2ecf20Sopenharmony_cistruct adu_device { 718c2ecf20Sopenharmony_ci struct mutex mtx; 728c2ecf20Sopenharmony_ci struct usb_device *udev; /* save off the usb device pointer */ 738c2ecf20Sopenharmony_ci struct usb_interface *interface; 748c2ecf20Sopenharmony_ci unsigned int minor; /* the starting minor number for this device */ 758c2ecf20Sopenharmony_ci char serial_number[8]; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci int open_count; /* number of times this port has been opened */ 788c2ecf20Sopenharmony_ci unsigned long disconnected:1; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci char *read_buffer_primary; 818c2ecf20Sopenharmony_ci int read_buffer_length; 828c2ecf20Sopenharmony_ci char *read_buffer_secondary; 838c2ecf20Sopenharmony_ci int secondary_head; 848c2ecf20Sopenharmony_ci int secondary_tail; 858c2ecf20Sopenharmony_ci spinlock_t buflock; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci wait_queue_head_t read_wait; 888c2ecf20Sopenharmony_ci wait_queue_head_t write_wait; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci char *interrupt_in_buffer; 918c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *interrupt_in_endpoint; 928c2ecf20Sopenharmony_ci struct urb *interrupt_in_urb; 938c2ecf20Sopenharmony_ci int read_urb_finished; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci char *interrupt_out_buffer; 968c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *interrupt_out_endpoint; 978c2ecf20Sopenharmony_ci struct urb *interrupt_out_urb; 988c2ecf20Sopenharmony_ci int out_urb_finished; 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(adutux_mutex); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic struct usb_driver adu_driver; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic inline void adu_debug_data(struct device *dev, const char *function, 1068c2ecf20Sopenharmony_ci int size, const unsigned char *data) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - length = %d, data = %*ph\n", 1098c2ecf20Sopenharmony_ci function, size, size, data); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* 1138c2ecf20Sopenharmony_ci * adu_abort_transfers 1148c2ecf20Sopenharmony_ci * aborts transfers and frees associated data structures 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_cistatic void adu_abort_transfers(struct adu_device *dev) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci unsigned long flags; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (dev->disconnected) 1218c2ecf20Sopenharmony_ci return; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* shutdown transfer */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* XXX Anchor these instead */ 1268c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->buflock, flags); 1278c2ecf20Sopenharmony_ci if (!dev->read_urb_finished) { 1288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->buflock, flags); 1298c2ecf20Sopenharmony_ci usb_kill_urb(dev->interrupt_in_urb); 1308c2ecf20Sopenharmony_ci } else 1318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->buflock, flags); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->buflock, flags); 1348c2ecf20Sopenharmony_ci if (!dev->out_urb_finished) { 1358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->buflock, flags); 1368c2ecf20Sopenharmony_ci wait_event_timeout(dev->write_wait, dev->out_urb_finished, 1378c2ecf20Sopenharmony_ci COMMAND_TIMEOUT); 1388c2ecf20Sopenharmony_ci usb_kill_urb(dev->interrupt_out_urb); 1398c2ecf20Sopenharmony_ci } else 1408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->buflock, flags); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic void adu_delete(struct adu_device *dev) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci /* free data structures */ 1468c2ecf20Sopenharmony_ci usb_free_urb(dev->interrupt_in_urb); 1478c2ecf20Sopenharmony_ci usb_free_urb(dev->interrupt_out_urb); 1488c2ecf20Sopenharmony_ci kfree(dev->read_buffer_primary); 1498c2ecf20Sopenharmony_ci kfree(dev->read_buffer_secondary); 1508c2ecf20Sopenharmony_ci kfree(dev->interrupt_in_buffer); 1518c2ecf20Sopenharmony_ci kfree(dev->interrupt_out_buffer); 1528c2ecf20Sopenharmony_ci usb_put_dev(dev->udev); 1538c2ecf20Sopenharmony_ci kfree(dev); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void adu_interrupt_in_callback(struct urb *urb) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct adu_device *dev = urb->context; 1598c2ecf20Sopenharmony_ci int status = urb->status; 1608c2ecf20Sopenharmony_ci unsigned long flags; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci adu_debug_data(&dev->udev->dev, __func__, 1638c2ecf20Sopenharmony_ci urb->actual_length, urb->transfer_buffer); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->buflock, flags); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (status != 0) { 1688c2ecf20Sopenharmony_ci if ((status != -ENOENT) && (status != -ECONNRESET) && 1698c2ecf20Sopenharmony_ci (status != -ESHUTDOWN)) { 1708c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, 1718c2ecf20Sopenharmony_ci "%s : nonzero status received: %d\n", 1728c2ecf20Sopenharmony_ci __func__, status); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci goto exit; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (urb->actual_length > 0 && dev->interrupt_in_buffer[0] != 0x00) { 1788c2ecf20Sopenharmony_ci if (dev->read_buffer_length < 1798c2ecf20Sopenharmony_ci (4 * usb_endpoint_maxp(dev->interrupt_in_endpoint)) - 1808c2ecf20Sopenharmony_ci (urb->actual_length)) { 1818c2ecf20Sopenharmony_ci memcpy (dev->read_buffer_primary + 1828c2ecf20Sopenharmony_ci dev->read_buffer_length, 1838c2ecf20Sopenharmony_ci dev->interrupt_in_buffer, urb->actual_length); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci dev->read_buffer_length += urb->actual_length; 1868c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev,"%s reading %d\n", __func__, 1878c2ecf20Sopenharmony_ci urb->actual_length); 1888c2ecf20Sopenharmony_ci } else { 1898c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev,"%s : read_buffer overflow\n", 1908c2ecf20Sopenharmony_ci __func__); 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ciexit: 1958c2ecf20Sopenharmony_ci dev->read_urb_finished = 1; 1968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->buflock, flags); 1978c2ecf20Sopenharmony_ci /* always wake up so we recover from errors */ 1988c2ecf20Sopenharmony_ci wake_up_interruptible(&dev->read_wait); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic void adu_interrupt_out_callback(struct urb *urb) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct adu_device *dev = urb->context; 2048c2ecf20Sopenharmony_ci int status = urb->status; 2058c2ecf20Sopenharmony_ci unsigned long flags; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci adu_debug_data(&dev->udev->dev, __func__, 2088c2ecf20Sopenharmony_ci urb->actual_length, urb->transfer_buffer); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (status != 0) { 2118c2ecf20Sopenharmony_ci if ((status != -ENOENT) && 2128c2ecf20Sopenharmony_ci (status != -ESHUTDOWN) && 2138c2ecf20Sopenharmony_ci (status != -ECONNRESET)) { 2148c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, 2158c2ecf20Sopenharmony_ci "%s :nonzero status received: %d\n", __func__, 2168c2ecf20Sopenharmony_ci status); 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci return; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->buflock, flags); 2228c2ecf20Sopenharmony_ci dev->out_urb_finished = 1; 2238c2ecf20Sopenharmony_ci wake_up(&dev->write_wait); 2248c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->buflock, flags); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic int adu_open(struct inode *inode, struct file *file) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct adu_device *dev = NULL; 2308c2ecf20Sopenharmony_ci struct usb_interface *interface; 2318c2ecf20Sopenharmony_ci int subminor; 2328c2ecf20Sopenharmony_ci int retval; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci subminor = iminor(inode); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci retval = mutex_lock_interruptible(&adutux_mutex); 2378c2ecf20Sopenharmony_ci if (retval) 2388c2ecf20Sopenharmony_ci goto exit_no_lock; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci interface = usb_find_interface(&adu_driver, subminor); 2418c2ecf20Sopenharmony_ci if (!interface) { 2428c2ecf20Sopenharmony_ci pr_err("%s - error, can't find device for minor %d\n", 2438c2ecf20Sopenharmony_ci __func__, subminor); 2448c2ecf20Sopenharmony_ci retval = -ENODEV; 2458c2ecf20Sopenharmony_ci goto exit_no_device; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci dev = usb_get_intfdata(interface); 2498c2ecf20Sopenharmony_ci if (!dev) { 2508c2ecf20Sopenharmony_ci retval = -ENODEV; 2518c2ecf20Sopenharmony_ci goto exit_no_device; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* check that nobody else is using the device */ 2558c2ecf20Sopenharmony_ci if (dev->open_count) { 2568c2ecf20Sopenharmony_ci retval = -EBUSY; 2578c2ecf20Sopenharmony_ci goto exit_no_device; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci ++dev->open_count; 2618c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, "%s: open count %d\n", __func__, 2628c2ecf20Sopenharmony_ci dev->open_count); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* save device in the file's private structure */ 2658c2ecf20Sopenharmony_ci file->private_data = dev; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* initialize in direction */ 2688c2ecf20Sopenharmony_ci dev->read_buffer_length = 0; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* fixup first read by having urb waiting for it */ 2718c2ecf20Sopenharmony_ci usb_fill_int_urb(dev->interrupt_in_urb, dev->udev, 2728c2ecf20Sopenharmony_ci usb_rcvintpipe(dev->udev, 2738c2ecf20Sopenharmony_ci dev->interrupt_in_endpoint->bEndpointAddress), 2748c2ecf20Sopenharmony_ci dev->interrupt_in_buffer, 2758c2ecf20Sopenharmony_ci usb_endpoint_maxp(dev->interrupt_in_endpoint), 2768c2ecf20Sopenharmony_ci adu_interrupt_in_callback, dev, 2778c2ecf20Sopenharmony_ci dev->interrupt_in_endpoint->bInterval); 2788c2ecf20Sopenharmony_ci dev->read_urb_finished = 0; 2798c2ecf20Sopenharmony_ci if (usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL)) 2808c2ecf20Sopenharmony_ci dev->read_urb_finished = 1; 2818c2ecf20Sopenharmony_ci /* we ignore failure */ 2828c2ecf20Sopenharmony_ci /* end of fixup for first read */ 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* initialize out direction */ 2858c2ecf20Sopenharmony_ci dev->out_urb_finished = 1; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci retval = 0; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ciexit_no_device: 2908c2ecf20Sopenharmony_ci mutex_unlock(&adutux_mutex); 2918c2ecf20Sopenharmony_ciexit_no_lock: 2928c2ecf20Sopenharmony_ci return retval; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic void adu_release_internal(struct adu_device *dev) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci /* decrement our usage count for the device */ 2988c2ecf20Sopenharmony_ci --dev->open_count; 2998c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, "%s : open count %d\n", __func__, 3008c2ecf20Sopenharmony_ci dev->open_count); 3018c2ecf20Sopenharmony_ci if (dev->open_count <= 0) { 3028c2ecf20Sopenharmony_ci adu_abort_transfers(dev); 3038c2ecf20Sopenharmony_ci dev->open_count = 0; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int adu_release(struct inode *inode, struct file *file) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct adu_device *dev; 3108c2ecf20Sopenharmony_ci int retval = 0; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (file == NULL) { 3138c2ecf20Sopenharmony_ci retval = -ENODEV; 3148c2ecf20Sopenharmony_ci goto exit; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci dev = file->private_data; 3188c2ecf20Sopenharmony_ci if (dev == NULL) { 3198c2ecf20Sopenharmony_ci retval = -ENODEV; 3208c2ecf20Sopenharmony_ci goto exit; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci mutex_lock(&adutux_mutex); /* not interruptible */ 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (dev->open_count <= 0) { 3268c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, "%s : device not opened\n", __func__); 3278c2ecf20Sopenharmony_ci retval = -ENODEV; 3288c2ecf20Sopenharmony_ci goto unlock; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci adu_release_internal(dev); 3328c2ecf20Sopenharmony_ci if (dev->disconnected) { 3338c2ecf20Sopenharmony_ci /* the device was unplugged before the file was released */ 3348c2ecf20Sopenharmony_ci if (!dev->open_count) /* ... and we're the last user */ 3358c2ecf20Sopenharmony_ci adu_delete(dev); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ciunlock: 3388c2ecf20Sopenharmony_ci mutex_unlock(&adutux_mutex); 3398c2ecf20Sopenharmony_ciexit: 3408c2ecf20Sopenharmony_ci return retval; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic ssize_t adu_read(struct file *file, __user char *buffer, size_t count, 3448c2ecf20Sopenharmony_ci loff_t *ppos) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct adu_device *dev; 3478c2ecf20Sopenharmony_ci size_t bytes_read = 0; 3488c2ecf20Sopenharmony_ci size_t bytes_to_read = count; 3498c2ecf20Sopenharmony_ci int retval = 0; 3508c2ecf20Sopenharmony_ci int timeout = 0; 3518c2ecf20Sopenharmony_ci int should_submit = 0; 3528c2ecf20Sopenharmony_ci unsigned long flags; 3538c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci dev = file->private_data; 3568c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&dev->mtx)) 3578c2ecf20Sopenharmony_ci return -ERESTARTSYS; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* verify that the device wasn't unplugged */ 3608c2ecf20Sopenharmony_ci if (dev->disconnected) { 3618c2ecf20Sopenharmony_ci retval = -ENODEV; 3628c2ecf20Sopenharmony_ci pr_err("No device or device unplugged %d\n", retval); 3638c2ecf20Sopenharmony_ci goto exit; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* verify that some data was requested */ 3678c2ecf20Sopenharmony_ci if (count == 0) { 3688c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, "%s : read request of 0 bytes\n", 3698c2ecf20Sopenharmony_ci __func__); 3708c2ecf20Sopenharmony_ci goto exit; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci timeout = COMMAND_TIMEOUT; 3748c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, "%s : about to start looping\n", __func__); 3758c2ecf20Sopenharmony_ci while (bytes_to_read) { 3768c2ecf20Sopenharmony_ci size_t data_in_secondary = dev->secondary_tail - dev->secondary_head; 3778c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, 3788c2ecf20Sopenharmony_ci "%s : while, data_in_secondary=%zu, status=%d\n", 3798c2ecf20Sopenharmony_ci __func__, data_in_secondary, 3808c2ecf20Sopenharmony_ci dev->interrupt_in_urb->status); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (data_in_secondary) { 3838c2ecf20Sopenharmony_ci /* drain secondary buffer */ 3848c2ecf20Sopenharmony_ci size_t amount = min(bytes_to_read, data_in_secondary); 3858c2ecf20Sopenharmony_ci if (copy_to_user(buffer, dev->read_buffer_secondary+dev->secondary_head, amount)) { 3868c2ecf20Sopenharmony_ci retval = -EFAULT; 3878c2ecf20Sopenharmony_ci goto exit; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci dev->secondary_head += amount; 3908c2ecf20Sopenharmony_ci bytes_read += amount; 3918c2ecf20Sopenharmony_ci bytes_to_read -= amount; 3928c2ecf20Sopenharmony_ci } else { 3938c2ecf20Sopenharmony_ci /* we check the primary buffer */ 3948c2ecf20Sopenharmony_ci spin_lock_irqsave (&dev->buflock, flags); 3958c2ecf20Sopenharmony_ci if (dev->read_buffer_length) { 3968c2ecf20Sopenharmony_ci /* we secure access to the primary */ 3978c2ecf20Sopenharmony_ci char *tmp; 3988c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, 3998c2ecf20Sopenharmony_ci "%s : swap, read_buffer_length = %d\n", 4008c2ecf20Sopenharmony_ci __func__, dev->read_buffer_length); 4018c2ecf20Sopenharmony_ci tmp = dev->read_buffer_secondary; 4028c2ecf20Sopenharmony_ci dev->read_buffer_secondary = dev->read_buffer_primary; 4038c2ecf20Sopenharmony_ci dev->read_buffer_primary = tmp; 4048c2ecf20Sopenharmony_ci dev->secondary_head = 0; 4058c2ecf20Sopenharmony_ci dev->secondary_tail = dev->read_buffer_length; 4068c2ecf20Sopenharmony_ci dev->read_buffer_length = 0; 4078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->buflock, flags); 4088c2ecf20Sopenharmony_ci /* we have a free buffer so use it */ 4098c2ecf20Sopenharmony_ci should_submit = 1; 4108c2ecf20Sopenharmony_ci } else { 4118c2ecf20Sopenharmony_ci /* even the primary was empty - we may need to do IO */ 4128c2ecf20Sopenharmony_ci if (!dev->read_urb_finished) { 4138c2ecf20Sopenharmony_ci /* somebody is doing IO */ 4148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->buflock, flags); 4158c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, 4168c2ecf20Sopenharmony_ci "%s : submitted already\n", 4178c2ecf20Sopenharmony_ci __func__); 4188c2ecf20Sopenharmony_ci } else { 4198c2ecf20Sopenharmony_ci /* we must initiate input */ 4208c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, 4218c2ecf20Sopenharmony_ci "%s : initiate input\n", 4228c2ecf20Sopenharmony_ci __func__); 4238c2ecf20Sopenharmony_ci dev->read_urb_finished = 0; 4248c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->buflock, flags); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci usb_fill_int_urb(dev->interrupt_in_urb, dev->udev, 4278c2ecf20Sopenharmony_ci usb_rcvintpipe(dev->udev, 4288c2ecf20Sopenharmony_ci dev->interrupt_in_endpoint->bEndpointAddress), 4298c2ecf20Sopenharmony_ci dev->interrupt_in_buffer, 4308c2ecf20Sopenharmony_ci usb_endpoint_maxp(dev->interrupt_in_endpoint), 4318c2ecf20Sopenharmony_ci adu_interrupt_in_callback, 4328c2ecf20Sopenharmony_ci dev, 4338c2ecf20Sopenharmony_ci dev->interrupt_in_endpoint->bInterval); 4348c2ecf20Sopenharmony_ci retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); 4358c2ecf20Sopenharmony_ci if (retval) { 4368c2ecf20Sopenharmony_ci dev->read_urb_finished = 1; 4378c2ecf20Sopenharmony_ci if (retval == -ENOMEM) { 4388c2ecf20Sopenharmony_ci retval = bytes_read ? bytes_read : -ENOMEM; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, 4418c2ecf20Sopenharmony_ci "%s : submit failed\n", 4428c2ecf20Sopenharmony_ci __func__); 4438c2ecf20Sopenharmony_ci goto exit; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* we wait for I/O to complete */ 4488c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 4498c2ecf20Sopenharmony_ci add_wait_queue(&dev->read_wait, &wait); 4508c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->buflock, flags); 4518c2ecf20Sopenharmony_ci if (!dev->read_urb_finished) { 4528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->buflock, flags); 4538c2ecf20Sopenharmony_ci timeout = schedule_timeout(COMMAND_TIMEOUT); 4548c2ecf20Sopenharmony_ci } else { 4558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->buflock, flags); 4568c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci remove_wait_queue(&dev->read_wait, &wait); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (timeout <= 0) { 4618c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, 4628c2ecf20Sopenharmony_ci "%s : timeout\n", __func__); 4638c2ecf20Sopenharmony_ci retval = bytes_read ? bytes_read : -ETIMEDOUT; 4648c2ecf20Sopenharmony_ci goto exit; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (signal_pending(current)) { 4688c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, 4698c2ecf20Sopenharmony_ci "%s : signal pending\n", 4708c2ecf20Sopenharmony_ci __func__); 4718c2ecf20Sopenharmony_ci retval = bytes_read ? bytes_read : -EINTR; 4728c2ecf20Sopenharmony_ci goto exit; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci retval = bytes_read; 4798c2ecf20Sopenharmony_ci /* if the primary buffer is empty then use it */ 4808c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->buflock, flags); 4818c2ecf20Sopenharmony_ci if (should_submit && dev->read_urb_finished) { 4828c2ecf20Sopenharmony_ci dev->read_urb_finished = 0; 4838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->buflock, flags); 4848c2ecf20Sopenharmony_ci usb_fill_int_urb(dev->interrupt_in_urb, dev->udev, 4858c2ecf20Sopenharmony_ci usb_rcvintpipe(dev->udev, 4868c2ecf20Sopenharmony_ci dev->interrupt_in_endpoint->bEndpointAddress), 4878c2ecf20Sopenharmony_ci dev->interrupt_in_buffer, 4888c2ecf20Sopenharmony_ci usb_endpoint_maxp(dev->interrupt_in_endpoint), 4898c2ecf20Sopenharmony_ci adu_interrupt_in_callback, 4908c2ecf20Sopenharmony_ci dev, 4918c2ecf20Sopenharmony_ci dev->interrupt_in_endpoint->bInterval); 4928c2ecf20Sopenharmony_ci if (usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL) != 0) 4938c2ecf20Sopenharmony_ci dev->read_urb_finished = 1; 4948c2ecf20Sopenharmony_ci /* we ignore failure */ 4958c2ecf20Sopenharmony_ci } else { 4968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->buflock, flags); 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ciexit: 5008c2ecf20Sopenharmony_ci /* unlock the device */ 5018c2ecf20Sopenharmony_ci mutex_unlock(&dev->mtx); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci return retval; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic ssize_t adu_write(struct file *file, const __user char *buffer, 5078c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(waita, current); 5108c2ecf20Sopenharmony_ci struct adu_device *dev; 5118c2ecf20Sopenharmony_ci size_t bytes_written = 0; 5128c2ecf20Sopenharmony_ci size_t bytes_to_write; 5138c2ecf20Sopenharmony_ci size_t buffer_size; 5148c2ecf20Sopenharmony_ci unsigned long flags; 5158c2ecf20Sopenharmony_ci int retval; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci dev = file->private_data; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci retval = mutex_lock_interruptible(&dev->mtx); 5208c2ecf20Sopenharmony_ci if (retval) 5218c2ecf20Sopenharmony_ci goto exit_nolock; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* verify that the device wasn't unplugged */ 5248c2ecf20Sopenharmony_ci if (dev->disconnected) { 5258c2ecf20Sopenharmony_ci retval = -ENODEV; 5268c2ecf20Sopenharmony_ci pr_err("No device or device unplugged %d\n", retval); 5278c2ecf20Sopenharmony_ci goto exit; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* verify that we actually have some data to write */ 5318c2ecf20Sopenharmony_ci if (count == 0) { 5328c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, "%s : write request of 0 bytes\n", 5338c2ecf20Sopenharmony_ci __func__); 5348c2ecf20Sopenharmony_ci goto exit; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci while (count > 0) { 5388c2ecf20Sopenharmony_ci add_wait_queue(&dev->write_wait, &waita); 5398c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 5408c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->buflock, flags); 5418c2ecf20Sopenharmony_ci if (!dev->out_urb_finished) { 5428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->buflock, flags); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci mutex_unlock(&dev->mtx); 5458c2ecf20Sopenharmony_ci if (signal_pending(current)) { 5468c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, "%s : interrupted\n", 5478c2ecf20Sopenharmony_ci __func__); 5488c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 5498c2ecf20Sopenharmony_ci retval = -EINTR; 5508c2ecf20Sopenharmony_ci goto exit_onqueue; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci if (schedule_timeout(COMMAND_TIMEOUT) == 0) { 5538c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, 5548c2ecf20Sopenharmony_ci "%s - command timed out.\n", __func__); 5558c2ecf20Sopenharmony_ci retval = -ETIMEDOUT; 5568c2ecf20Sopenharmony_ci goto exit_onqueue; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci remove_wait_queue(&dev->write_wait, &waita); 5598c2ecf20Sopenharmony_ci retval = mutex_lock_interruptible(&dev->mtx); 5608c2ecf20Sopenharmony_ci if (retval) { 5618c2ecf20Sopenharmony_ci retval = bytes_written ? bytes_written : retval; 5628c2ecf20Sopenharmony_ci goto exit_nolock; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, 5668c2ecf20Sopenharmony_ci "%s : in progress, count = %zd\n", 5678c2ecf20Sopenharmony_ci __func__, count); 5688c2ecf20Sopenharmony_ci } else { 5698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->buflock, flags); 5708c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 5718c2ecf20Sopenharmony_ci remove_wait_queue(&dev->write_wait, &waita); 5728c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, "%s : sending, count = %zd\n", 5738c2ecf20Sopenharmony_ci __func__, count); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* write the data into interrupt_out_buffer from userspace */ 5768c2ecf20Sopenharmony_ci buffer_size = usb_endpoint_maxp(dev->interrupt_out_endpoint); 5778c2ecf20Sopenharmony_ci bytes_to_write = count > buffer_size ? buffer_size : count; 5788c2ecf20Sopenharmony_ci dev_dbg(&dev->udev->dev, 5798c2ecf20Sopenharmony_ci "%s : buffer_size = %zd, count = %zd, bytes_to_write = %zd\n", 5808c2ecf20Sopenharmony_ci __func__, buffer_size, count, bytes_to_write); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write) != 0) { 5838c2ecf20Sopenharmony_ci retval = -EFAULT; 5848c2ecf20Sopenharmony_ci goto exit; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* send off the urb */ 5888c2ecf20Sopenharmony_ci usb_fill_int_urb( 5898c2ecf20Sopenharmony_ci dev->interrupt_out_urb, 5908c2ecf20Sopenharmony_ci dev->udev, 5918c2ecf20Sopenharmony_ci usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress), 5928c2ecf20Sopenharmony_ci dev->interrupt_out_buffer, 5938c2ecf20Sopenharmony_ci bytes_to_write, 5948c2ecf20Sopenharmony_ci adu_interrupt_out_callback, 5958c2ecf20Sopenharmony_ci dev, 5968c2ecf20Sopenharmony_ci dev->interrupt_out_endpoint->bInterval); 5978c2ecf20Sopenharmony_ci dev->interrupt_out_urb->actual_length = bytes_to_write; 5988c2ecf20Sopenharmony_ci dev->out_urb_finished = 0; 5998c2ecf20Sopenharmony_ci retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL); 6008c2ecf20Sopenharmony_ci if (retval < 0) { 6018c2ecf20Sopenharmony_ci dev->out_urb_finished = 1; 6028c2ecf20Sopenharmony_ci dev_err(&dev->udev->dev, "Couldn't submit " 6038c2ecf20Sopenharmony_ci "interrupt_out_urb %d\n", retval); 6048c2ecf20Sopenharmony_ci goto exit; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci buffer += bytes_to_write; 6088c2ecf20Sopenharmony_ci count -= bytes_to_write; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci bytes_written += bytes_to_write; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci mutex_unlock(&dev->mtx); 6148c2ecf20Sopenharmony_ci return bytes_written; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ciexit: 6178c2ecf20Sopenharmony_ci mutex_unlock(&dev->mtx); 6188c2ecf20Sopenharmony_ciexit_nolock: 6198c2ecf20Sopenharmony_ci return retval; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ciexit_onqueue: 6228c2ecf20Sopenharmony_ci remove_wait_queue(&dev->write_wait, &waita); 6238c2ecf20Sopenharmony_ci return retval; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci/* file operations needed when we register this driver */ 6278c2ecf20Sopenharmony_cistatic const struct file_operations adu_fops = { 6288c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6298c2ecf20Sopenharmony_ci .read = adu_read, 6308c2ecf20Sopenharmony_ci .write = adu_write, 6318c2ecf20Sopenharmony_ci .open = adu_open, 6328c2ecf20Sopenharmony_ci .release = adu_release, 6338c2ecf20Sopenharmony_ci .llseek = noop_llseek, 6348c2ecf20Sopenharmony_ci}; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci/* 6378c2ecf20Sopenharmony_ci * usb class driver info in order to get a minor number from the usb core, 6388c2ecf20Sopenharmony_ci * and to have the device registered with devfs and the driver core 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_cistatic struct usb_class_driver adu_class = { 6418c2ecf20Sopenharmony_ci .name = "usb/adutux%d", 6428c2ecf20Sopenharmony_ci .fops = &adu_fops, 6438c2ecf20Sopenharmony_ci .minor_base = ADU_MINOR_BASE, 6448c2ecf20Sopenharmony_ci}; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci/* 6478c2ecf20Sopenharmony_ci * adu_probe 6488c2ecf20Sopenharmony_ci * 6498c2ecf20Sopenharmony_ci * Called by the usb core when a new device is connected that it thinks 6508c2ecf20Sopenharmony_ci * this driver might be interested in. 6518c2ecf20Sopenharmony_ci */ 6528c2ecf20Sopenharmony_cistatic int adu_probe(struct usb_interface *interface, 6538c2ecf20Sopenharmony_ci const struct usb_device_id *id) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(interface); 6568c2ecf20Sopenharmony_ci struct adu_device *dev = NULL; 6578c2ecf20Sopenharmony_ci int retval = -ENOMEM; 6588c2ecf20Sopenharmony_ci int in_end_size; 6598c2ecf20Sopenharmony_ci int out_end_size; 6608c2ecf20Sopenharmony_ci int res; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci /* allocate memory for our device state and initialize it */ 6638c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(struct adu_device), GFP_KERNEL); 6648c2ecf20Sopenharmony_ci if (!dev) 6658c2ecf20Sopenharmony_ci return -ENOMEM; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci mutex_init(&dev->mtx); 6688c2ecf20Sopenharmony_ci spin_lock_init(&dev->buflock); 6698c2ecf20Sopenharmony_ci dev->udev = usb_get_dev(udev); 6708c2ecf20Sopenharmony_ci init_waitqueue_head(&dev->read_wait); 6718c2ecf20Sopenharmony_ci init_waitqueue_head(&dev->write_wait); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci res = usb_find_common_endpoints_reverse(interface->cur_altsetting, 6748c2ecf20Sopenharmony_ci NULL, NULL, 6758c2ecf20Sopenharmony_ci &dev->interrupt_in_endpoint, 6768c2ecf20Sopenharmony_ci &dev->interrupt_out_endpoint); 6778c2ecf20Sopenharmony_ci if (res) { 6788c2ecf20Sopenharmony_ci dev_err(&interface->dev, "interrupt endpoints not found\n"); 6798c2ecf20Sopenharmony_ci retval = res; 6808c2ecf20Sopenharmony_ci goto error; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci in_end_size = usb_endpoint_maxp(dev->interrupt_in_endpoint); 6848c2ecf20Sopenharmony_ci out_end_size = usb_endpoint_maxp(dev->interrupt_out_endpoint); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci dev->read_buffer_primary = kmalloc((4 * in_end_size), GFP_KERNEL); 6878c2ecf20Sopenharmony_ci if (!dev->read_buffer_primary) 6888c2ecf20Sopenharmony_ci goto error; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci /* debug code prime the buffer */ 6918c2ecf20Sopenharmony_ci memset(dev->read_buffer_primary, 'a', in_end_size); 6928c2ecf20Sopenharmony_ci memset(dev->read_buffer_primary + in_end_size, 'b', in_end_size); 6938c2ecf20Sopenharmony_ci memset(dev->read_buffer_primary + (2 * in_end_size), 'c', in_end_size); 6948c2ecf20Sopenharmony_ci memset(dev->read_buffer_primary + (3 * in_end_size), 'd', in_end_size); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci dev->read_buffer_secondary = kmalloc((4 * in_end_size), GFP_KERNEL); 6978c2ecf20Sopenharmony_ci if (!dev->read_buffer_secondary) 6988c2ecf20Sopenharmony_ci goto error; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* debug code prime the buffer */ 7018c2ecf20Sopenharmony_ci memset(dev->read_buffer_secondary, 'e', in_end_size); 7028c2ecf20Sopenharmony_ci memset(dev->read_buffer_secondary + in_end_size, 'f', in_end_size); 7038c2ecf20Sopenharmony_ci memset(dev->read_buffer_secondary + (2 * in_end_size), 'g', in_end_size); 7048c2ecf20Sopenharmony_ci memset(dev->read_buffer_secondary + (3 * in_end_size), 'h', in_end_size); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci dev->interrupt_in_buffer = kmalloc(in_end_size, GFP_KERNEL); 7078c2ecf20Sopenharmony_ci if (!dev->interrupt_in_buffer) 7088c2ecf20Sopenharmony_ci goto error; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci /* debug code prime the buffer */ 7118c2ecf20Sopenharmony_ci memset(dev->interrupt_in_buffer, 'i', in_end_size); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); 7148c2ecf20Sopenharmony_ci if (!dev->interrupt_in_urb) 7158c2ecf20Sopenharmony_ci goto error; 7168c2ecf20Sopenharmony_ci dev->interrupt_out_buffer = kmalloc(out_end_size, GFP_KERNEL); 7178c2ecf20Sopenharmony_ci if (!dev->interrupt_out_buffer) 7188c2ecf20Sopenharmony_ci goto error; 7198c2ecf20Sopenharmony_ci dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); 7208c2ecf20Sopenharmony_ci if (!dev->interrupt_out_urb) 7218c2ecf20Sopenharmony_ci goto error; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (!usb_string(udev, udev->descriptor.iSerialNumber, dev->serial_number, 7248c2ecf20Sopenharmony_ci sizeof(dev->serial_number))) { 7258c2ecf20Sopenharmony_ci dev_err(&interface->dev, "Could not retrieve serial number\n"); 7268c2ecf20Sopenharmony_ci retval = -EIO; 7278c2ecf20Sopenharmony_ci goto error; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci dev_dbg(&interface->dev,"serial_number=%s", dev->serial_number); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* we can register the device now, as it is ready */ 7328c2ecf20Sopenharmony_ci usb_set_intfdata(interface, dev); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci retval = usb_register_dev(interface, &adu_class); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (retval) { 7378c2ecf20Sopenharmony_ci /* something prevented us from registering this driver */ 7388c2ecf20Sopenharmony_ci dev_err(&interface->dev, "Not able to get a minor for this device.\n"); 7398c2ecf20Sopenharmony_ci usb_set_intfdata(interface, NULL); 7408c2ecf20Sopenharmony_ci goto error; 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci dev->minor = interface->minor; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci /* let the user know what node this device is now attached to */ 7468c2ecf20Sopenharmony_ci dev_info(&interface->dev, "ADU%d %s now attached to /dev/usb/adutux%d\n", 7478c2ecf20Sopenharmony_ci le16_to_cpu(udev->descriptor.idProduct), dev->serial_number, 7488c2ecf20Sopenharmony_ci (dev->minor - ADU_MINOR_BASE)); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci return 0; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cierror: 7538c2ecf20Sopenharmony_ci adu_delete(dev); 7548c2ecf20Sopenharmony_ci return retval; 7558c2ecf20Sopenharmony_ci} 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci/* 7588c2ecf20Sopenharmony_ci * adu_disconnect 7598c2ecf20Sopenharmony_ci * 7608c2ecf20Sopenharmony_ci * Called by the usb core when the device is removed from the system. 7618c2ecf20Sopenharmony_ci */ 7628c2ecf20Sopenharmony_cistatic void adu_disconnect(struct usb_interface *interface) 7638c2ecf20Sopenharmony_ci{ 7648c2ecf20Sopenharmony_ci struct adu_device *dev; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci dev = usb_get_intfdata(interface); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci usb_deregister_dev(interface, &adu_class); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci usb_poison_urb(dev->interrupt_in_urb); 7718c2ecf20Sopenharmony_ci usb_poison_urb(dev->interrupt_out_urb); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci mutex_lock(&adutux_mutex); 7748c2ecf20Sopenharmony_ci usb_set_intfdata(interface, NULL); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci mutex_lock(&dev->mtx); /* not interruptible */ 7778c2ecf20Sopenharmony_ci dev->disconnected = 1; 7788c2ecf20Sopenharmony_ci mutex_unlock(&dev->mtx); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci /* if the device is not opened, then we clean up right now */ 7818c2ecf20Sopenharmony_ci if (!dev->open_count) 7828c2ecf20Sopenharmony_ci adu_delete(dev); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci mutex_unlock(&adutux_mutex); 7858c2ecf20Sopenharmony_ci} 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci/* usb specific object needed to register this driver with the usb subsystem */ 7888c2ecf20Sopenharmony_cistatic struct usb_driver adu_driver = { 7898c2ecf20Sopenharmony_ci .name = "adutux", 7908c2ecf20Sopenharmony_ci .probe = adu_probe, 7918c2ecf20Sopenharmony_ci .disconnect = adu_disconnect, 7928c2ecf20Sopenharmony_ci .id_table = device_table, 7938c2ecf20Sopenharmony_ci}; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_cimodule_usb_driver(adu_driver); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 7988c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 7998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 800