162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Native support for the I/O-Warrior USB devices 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2003-2005, 2020 Code Mercenaries GmbH 662306a36Sopenharmony_ci * written by Christian Lucht <lucht@codemercs.com> and 762306a36Sopenharmony_ci * Christoph Jung <jung@codemercs.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * based on 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci * usb-skeleton.c by Greg Kroah-Hartman <greg@kroah.com> 1262306a36Sopenharmony_ci * brlvger.c by Stephane Dalton <sdalton@videotron.ca> 1362306a36Sopenharmony_ci * and Stephane Doyon <s.doyon@videotron.ca> 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * Released under the GPLv2. 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/usb.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/sched.h> 2262306a36Sopenharmony_ci#include <linux/mutex.h> 2362306a36Sopenharmony_ci#include <linux/poll.h> 2462306a36Sopenharmony_ci#include <linux/usb/iowarrior.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define DRIVER_AUTHOR "Christian Lucht <lucht@codemercs.com>" 2762306a36Sopenharmony_ci#define DRIVER_DESC "USB IO-Warrior driver" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define USB_VENDOR_ID_CODEMERCS 1984 3062306a36Sopenharmony_ci/* low speed iowarrior */ 3162306a36Sopenharmony_ci#define USB_DEVICE_ID_CODEMERCS_IOW40 0x1500 3262306a36Sopenharmony_ci#define USB_DEVICE_ID_CODEMERCS_IOW24 0x1501 3362306a36Sopenharmony_ci#define USB_DEVICE_ID_CODEMERCS_IOWPV1 0x1511 3462306a36Sopenharmony_ci#define USB_DEVICE_ID_CODEMERCS_IOWPV2 0x1512 3562306a36Sopenharmony_ci/* full speed iowarrior */ 3662306a36Sopenharmony_ci#define USB_DEVICE_ID_CODEMERCS_IOW56 0x1503 3762306a36Sopenharmony_ci/* fuller speed iowarrior */ 3862306a36Sopenharmony_ci#define USB_DEVICE_ID_CODEMERCS_IOW28 0x1504 3962306a36Sopenharmony_ci#define USB_DEVICE_ID_CODEMERCS_IOW28L 0x1505 4062306a36Sopenharmony_ci#define USB_DEVICE_ID_CODEMERCS_IOW100 0x1506 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* OEMed devices */ 4362306a36Sopenharmony_ci#define USB_DEVICE_ID_CODEMERCS_IOW24SAG 0x158a 4462306a36Sopenharmony_ci#define USB_DEVICE_ID_CODEMERCS_IOW56AM 0x158b 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Get a minor range for your devices from the usb maintainer */ 4762306a36Sopenharmony_ci#ifdef CONFIG_USB_DYNAMIC_MINORS 4862306a36Sopenharmony_ci#define IOWARRIOR_MINOR_BASE 0 4962306a36Sopenharmony_ci#else 5062306a36Sopenharmony_ci#define IOWARRIOR_MINOR_BASE 208 // SKELETON_MINOR_BASE 192 + 16, not official yet 5162306a36Sopenharmony_ci#endif 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* interrupt input queue size */ 5462306a36Sopenharmony_ci#define MAX_INTERRUPT_BUFFER 16 5562306a36Sopenharmony_ci/* 5662306a36Sopenharmony_ci maximum number of urbs that are submitted for writes at the same time, 5762306a36Sopenharmony_ci this applies to the IOWarrior56 only! 5862306a36Sopenharmony_ci IOWarrior24 and IOWarrior40 use synchronous usb_control_msg calls. 5962306a36Sopenharmony_ci*/ 6062306a36Sopenharmony_ci#define MAX_WRITES_IN_FLIGHT 4 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 6362306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 6462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic struct usb_driver iowarrior_driver; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/*--------------*/ 6962306a36Sopenharmony_ci/* data */ 7062306a36Sopenharmony_ci/*--------------*/ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* Structure to hold all of our device specific stuff */ 7362306a36Sopenharmony_cistruct iowarrior { 7462306a36Sopenharmony_ci struct mutex mutex; /* locks this structure */ 7562306a36Sopenharmony_ci struct usb_device *udev; /* save off the usb device pointer */ 7662306a36Sopenharmony_ci struct usb_interface *interface; /* the interface for this device */ 7762306a36Sopenharmony_ci unsigned char minor; /* the starting minor number for this device */ 7862306a36Sopenharmony_ci struct usb_endpoint_descriptor *int_out_endpoint; /* endpoint for reading (needed for IOW56 only) */ 7962306a36Sopenharmony_ci struct usb_endpoint_descriptor *int_in_endpoint; /* endpoint for reading */ 8062306a36Sopenharmony_ci struct urb *int_in_urb; /* the urb for reading data */ 8162306a36Sopenharmony_ci unsigned char *int_in_buffer; /* buffer for data to be read */ 8262306a36Sopenharmony_ci unsigned char serial_number; /* to detect lost packages */ 8362306a36Sopenharmony_ci unsigned char *read_queue; /* size is MAX_INTERRUPT_BUFFER * packet size */ 8462306a36Sopenharmony_ci wait_queue_head_t read_wait; 8562306a36Sopenharmony_ci wait_queue_head_t write_wait; /* wait-queue for writing to the device */ 8662306a36Sopenharmony_ci atomic_t write_busy; /* number of write-urbs submitted */ 8762306a36Sopenharmony_ci atomic_t read_idx; 8862306a36Sopenharmony_ci atomic_t intr_idx; 8962306a36Sopenharmony_ci atomic_t overflow_flag; /* signals an index 'rollover' */ 9062306a36Sopenharmony_ci int present; /* this is 1 as long as the device is connected */ 9162306a36Sopenharmony_ci int opened; /* this is 1 if the device is currently open */ 9262306a36Sopenharmony_ci char chip_serial[9]; /* the serial number string of the chip connected */ 9362306a36Sopenharmony_ci int report_size; /* number of bytes in a report */ 9462306a36Sopenharmony_ci u16 product_id; 9562306a36Sopenharmony_ci struct usb_anchor submitted; 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/*--------------*/ 9962306a36Sopenharmony_ci/* globals */ 10062306a36Sopenharmony_ci/*--------------*/ 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#define USB_REQ_GET_REPORT 0x01 10362306a36Sopenharmony_ci//#if 0 10462306a36Sopenharmony_cistatic int usb_get_report(struct usb_device *dev, 10562306a36Sopenharmony_ci struct usb_host_interface *inter, unsigned char type, 10662306a36Sopenharmony_ci unsigned char id, void *buf, int size) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 10962306a36Sopenharmony_ci USB_REQ_GET_REPORT, 11062306a36Sopenharmony_ci USB_DIR_IN | USB_TYPE_CLASS | 11162306a36Sopenharmony_ci USB_RECIP_INTERFACE, (type << 8) + id, 11262306a36Sopenharmony_ci inter->desc.bInterfaceNumber, buf, size, 11362306a36Sopenharmony_ci USB_CTRL_GET_TIMEOUT); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci//#endif 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#define USB_REQ_SET_REPORT 0x09 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int usb_set_report(struct usb_interface *intf, unsigned char type, 12062306a36Sopenharmony_ci unsigned char id, void *buf, int size) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci return usb_control_msg(interface_to_usbdev(intf), 12362306a36Sopenharmony_ci usb_sndctrlpipe(interface_to_usbdev(intf), 0), 12462306a36Sopenharmony_ci USB_REQ_SET_REPORT, 12562306a36Sopenharmony_ci USB_TYPE_CLASS | USB_RECIP_INTERFACE, 12662306a36Sopenharmony_ci (type << 8) + id, 12762306a36Sopenharmony_ci intf->cur_altsetting->desc.bInterfaceNumber, buf, 12862306a36Sopenharmony_ci size, 1000); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/*---------------------*/ 13262306a36Sopenharmony_ci/* driver registration */ 13362306a36Sopenharmony_ci/*---------------------*/ 13462306a36Sopenharmony_ci/* table of devices that work with this driver */ 13562306a36Sopenharmony_cistatic const struct usb_device_id iowarrior_ids[] = { 13662306a36Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40)}, 13762306a36Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24)}, 13862306a36Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV1)}, 13962306a36Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV2)}, 14062306a36Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW56)}, 14162306a36Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24SAG)}, 14262306a36Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW56AM)}, 14362306a36Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW28)}, 14462306a36Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW28L)}, 14562306a36Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW100)}, 14662306a36Sopenharmony_ci {} /* Terminating entry */ 14762306a36Sopenharmony_ci}; 14862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, iowarrior_ids); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* 15162306a36Sopenharmony_ci * USB callback handler for reading data 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_cistatic void iowarrior_callback(struct urb *urb) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct iowarrior *dev = urb->context; 15662306a36Sopenharmony_ci int intr_idx; 15762306a36Sopenharmony_ci int read_idx; 15862306a36Sopenharmony_ci int aux_idx; 15962306a36Sopenharmony_ci int offset; 16062306a36Sopenharmony_ci int status = urb->status; 16162306a36Sopenharmony_ci int retval; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci switch (status) { 16462306a36Sopenharmony_ci case 0: 16562306a36Sopenharmony_ci /* success */ 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci case -ECONNRESET: 16862306a36Sopenharmony_ci case -ENOENT: 16962306a36Sopenharmony_ci case -ESHUTDOWN: 17062306a36Sopenharmony_ci return; 17162306a36Sopenharmony_ci default: 17262306a36Sopenharmony_ci goto exit; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci intr_idx = atomic_read(&dev->intr_idx); 17662306a36Sopenharmony_ci /* aux_idx become previous intr_idx */ 17762306a36Sopenharmony_ci aux_idx = (intr_idx == 0) ? (MAX_INTERRUPT_BUFFER - 1) : (intr_idx - 1); 17862306a36Sopenharmony_ci read_idx = atomic_read(&dev->read_idx); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* queue is not empty and it's interface 0 */ 18162306a36Sopenharmony_ci if ((intr_idx != read_idx) 18262306a36Sopenharmony_ci && (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0)) { 18362306a36Sopenharmony_ci /* + 1 for serial number */ 18462306a36Sopenharmony_ci offset = aux_idx * (dev->report_size + 1); 18562306a36Sopenharmony_ci if (!memcmp 18662306a36Sopenharmony_ci (dev->read_queue + offset, urb->transfer_buffer, 18762306a36Sopenharmony_ci dev->report_size)) { 18862306a36Sopenharmony_ci /* equal values on interface 0 will be ignored */ 18962306a36Sopenharmony_ci goto exit; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* aux_idx become next intr_idx */ 19462306a36Sopenharmony_ci aux_idx = (intr_idx == (MAX_INTERRUPT_BUFFER - 1)) ? 0 : (intr_idx + 1); 19562306a36Sopenharmony_ci if (read_idx == aux_idx) { 19662306a36Sopenharmony_ci /* queue full, dropping oldest input */ 19762306a36Sopenharmony_ci read_idx = (++read_idx == MAX_INTERRUPT_BUFFER) ? 0 : read_idx; 19862306a36Sopenharmony_ci atomic_set(&dev->read_idx, read_idx); 19962306a36Sopenharmony_ci atomic_set(&dev->overflow_flag, 1); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* +1 for serial number */ 20362306a36Sopenharmony_ci offset = intr_idx * (dev->report_size + 1); 20462306a36Sopenharmony_ci memcpy(dev->read_queue + offset, urb->transfer_buffer, 20562306a36Sopenharmony_ci dev->report_size); 20662306a36Sopenharmony_ci *(dev->read_queue + offset + (dev->report_size)) = dev->serial_number++; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci atomic_set(&dev->intr_idx, aux_idx); 20962306a36Sopenharmony_ci /* tell the blocking read about the new data */ 21062306a36Sopenharmony_ci wake_up_interruptible(&dev->read_wait); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ciexit: 21362306a36Sopenharmony_ci retval = usb_submit_urb(urb, GFP_ATOMIC); 21462306a36Sopenharmony_ci if (retval) 21562306a36Sopenharmony_ci dev_err(&dev->interface->dev, "%s - usb_submit_urb failed with result %d\n", 21662306a36Sopenharmony_ci __func__, retval); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci/* 22162306a36Sopenharmony_ci * USB Callback handler for write-ops 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_cistatic void iowarrior_write_callback(struct urb *urb) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct iowarrior *dev; 22662306a36Sopenharmony_ci int status = urb->status; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci dev = urb->context; 22962306a36Sopenharmony_ci /* sync/async unlink faults aren't errors */ 23062306a36Sopenharmony_ci if (status && 23162306a36Sopenharmony_ci !(status == -ENOENT || 23262306a36Sopenharmony_ci status == -ECONNRESET || status == -ESHUTDOWN)) { 23362306a36Sopenharmony_ci dev_dbg(&dev->interface->dev, 23462306a36Sopenharmony_ci "nonzero write bulk status received: %d\n", status); 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci /* free up our allocated buffer */ 23762306a36Sopenharmony_ci usb_free_coherent(urb->dev, urb->transfer_buffer_length, 23862306a36Sopenharmony_ci urb->transfer_buffer, urb->transfer_dma); 23962306a36Sopenharmony_ci /* tell a waiting writer the interrupt-out-pipe is available again */ 24062306a36Sopenharmony_ci atomic_dec(&dev->write_busy); 24162306a36Sopenharmony_ci wake_up_interruptible(&dev->write_wait); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci/* 24562306a36Sopenharmony_ci * iowarrior_delete 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_cistatic inline void iowarrior_delete(struct iowarrior *dev) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor); 25062306a36Sopenharmony_ci kfree(dev->int_in_buffer); 25162306a36Sopenharmony_ci usb_free_urb(dev->int_in_urb); 25262306a36Sopenharmony_ci kfree(dev->read_queue); 25362306a36Sopenharmony_ci usb_put_intf(dev->interface); 25462306a36Sopenharmony_ci kfree(dev); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/*---------------------*/ 25862306a36Sopenharmony_ci/* fops implementation */ 25962306a36Sopenharmony_ci/*---------------------*/ 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic int read_index(struct iowarrior *dev) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci int intr_idx, read_idx; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci read_idx = atomic_read(&dev->read_idx); 26662306a36Sopenharmony_ci intr_idx = atomic_read(&dev->intr_idx); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return (read_idx == intr_idx ? -1 : read_idx); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/* 27262306a36Sopenharmony_ci * iowarrior_read 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_cistatic ssize_t iowarrior_read(struct file *file, char __user *buffer, 27562306a36Sopenharmony_ci size_t count, loff_t *ppos) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct iowarrior *dev; 27862306a36Sopenharmony_ci int read_idx; 27962306a36Sopenharmony_ci int offset; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci dev = file->private_data; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* verify that the device wasn't unplugged */ 28462306a36Sopenharmony_ci if (!dev || !dev->present) 28562306a36Sopenharmony_ci return -ENODEV; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n", 28862306a36Sopenharmony_ci dev->minor, count); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* read count must be packet size (+ time stamp) */ 29162306a36Sopenharmony_ci if ((count != dev->report_size) 29262306a36Sopenharmony_ci && (count != (dev->report_size + 1))) 29362306a36Sopenharmony_ci return -EINVAL; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* repeat until no buffer overrun in callback handler occur */ 29662306a36Sopenharmony_ci do { 29762306a36Sopenharmony_ci atomic_set(&dev->overflow_flag, 0); 29862306a36Sopenharmony_ci if ((read_idx = read_index(dev)) == -1) { 29962306a36Sopenharmony_ci /* queue empty */ 30062306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) 30162306a36Sopenharmony_ci return -EAGAIN; 30262306a36Sopenharmony_ci else { 30362306a36Sopenharmony_ci //next line will return when there is either new data, or the device is unplugged 30462306a36Sopenharmony_ci int r = wait_event_interruptible(dev->read_wait, 30562306a36Sopenharmony_ci (!dev->present 30662306a36Sopenharmony_ci || (read_idx = 30762306a36Sopenharmony_ci read_index 30862306a36Sopenharmony_ci (dev)) != 30962306a36Sopenharmony_ci -1)); 31062306a36Sopenharmony_ci if (r) { 31162306a36Sopenharmony_ci //we were interrupted by a signal 31262306a36Sopenharmony_ci return -ERESTART; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci if (!dev->present) { 31562306a36Sopenharmony_ci //The device was unplugged 31662306a36Sopenharmony_ci return -ENODEV; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci if (read_idx == -1) { 31962306a36Sopenharmony_ci // Can this happen ??? 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci offset = read_idx * (dev->report_size + 1); 32662306a36Sopenharmony_ci if (copy_to_user(buffer, dev->read_queue + offset, count)) { 32762306a36Sopenharmony_ci return -EFAULT; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci } while (atomic_read(&dev->overflow_flag)); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci read_idx = ++read_idx == MAX_INTERRUPT_BUFFER ? 0 : read_idx; 33262306a36Sopenharmony_ci atomic_set(&dev->read_idx, read_idx); 33362306a36Sopenharmony_ci return count; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci/* 33762306a36Sopenharmony_ci * iowarrior_write 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_cistatic ssize_t iowarrior_write(struct file *file, 34062306a36Sopenharmony_ci const char __user *user_buffer, 34162306a36Sopenharmony_ci size_t count, loff_t *ppos) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct iowarrior *dev; 34462306a36Sopenharmony_ci int retval = 0; 34562306a36Sopenharmony_ci char *buf = NULL; /* for IOW24 and IOW56 we need a buffer */ 34662306a36Sopenharmony_ci struct urb *int_out_urb = NULL; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci dev = file->private_data; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci mutex_lock(&dev->mutex); 35162306a36Sopenharmony_ci /* verify that the device wasn't unplugged */ 35262306a36Sopenharmony_ci if (!dev->present) { 35362306a36Sopenharmony_ci retval = -ENODEV; 35462306a36Sopenharmony_ci goto exit; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n", 35762306a36Sopenharmony_ci dev->minor, count); 35862306a36Sopenharmony_ci /* if count is 0 we're already done */ 35962306a36Sopenharmony_ci if (count == 0) { 36062306a36Sopenharmony_ci retval = 0; 36162306a36Sopenharmony_ci goto exit; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci /* We only accept full reports */ 36462306a36Sopenharmony_ci if (count != dev->report_size) { 36562306a36Sopenharmony_ci retval = -EINVAL; 36662306a36Sopenharmony_ci goto exit; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci switch (dev->product_id) { 36962306a36Sopenharmony_ci case USB_DEVICE_ID_CODEMERCS_IOW24: 37062306a36Sopenharmony_ci case USB_DEVICE_ID_CODEMERCS_IOW24SAG: 37162306a36Sopenharmony_ci case USB_DEVICE_ID_CODEMERCS_IOWPV1: 37262306a36Sopenharmony_ci case USB_DEVICE_ID_CODEMERCS_IOWPV2: 37362306a36Sopenharmony_ci case USB_DEVICE_ID_CODEMERCS_IOW40: 37462306a36Sopenharmony_ci /* IOW24 and IOW40 use a synchronous call */ 37562306a36Sopenharmony_ci buf = memdup_user(user_buffer, count); 37662306a36Sopenharmony_ci if (IS_ERR(buf)) { 37762306a36Sopenharmony_ci retval = PTR_ERR(buf); 37862306a36Sopenharmony_ci goto exit; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci retval = usb_set_report(dev->interface, 2, 0, buf, count); 38162306a36Sopenharmony_ci kfree(buf); 38262306a36Sopenharmony_ci goto exit; 38362306a36Sopenharmony_ci case USB_DEVICE_ID_CODEMERCS_IOW56: 38462306a36Sopenharmony_ci case USB_DEVICE_ID_CODEMERCS_IOW56AM: 38562306a36Sopenharmony_ci case USB_DEVICE_ID_CODEMERCS_IOW28: 38662306a36Sopenharmony_ci case USB_DEVICE_ID_CODEMERCS_IOW28L: 38762306a36Sopenharmony_ci case USB_DEVICE_ID_CODEMERCS_IOW100: 38862306a36Sopenharmony_ci /* The IOW56 uses asynchronous IO and more urbs */ 38962306a36Sopenharmony_ci if (atomic_read(&dev->write_busy) == MAX_WRITES_IN_FLIGHT) { 39062306a36Sopenharmony_ci /* Wait until we are below the limit for submitted urbs */ 39162306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 39262306a36Sopenharmony_ci retval = -EAGAIN; 39362306a36Sopenharmony_ci goto exit; 39462306a36Sopenharmony_ci } else { 39562306a36Sopenharmony_ci retval = wait_event_interruptible(dev->write_wait, 39662306a36Sopenharmony_ci (!dev->present || (atomic_read (&dev-> write_busy) < MAX_WRITES_IN_FLIGHT))); 39762306a36Sopenharmony_ci if (retval) { 39862306a36Sopenharmony_ci /* we were interrupted by a signal */ 39962306a36Sopenharmony_ci retval = -ERESTART; 40062306a36Sopenharmony_ci goto exit; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci if (!dev->present) { 40362306a36Sopenharmony_ci /* The device was unplugged */ 40462306a36Sopenharmony_ci retval = -ENODEV; 40562306a36Sopenharmony_ci goto exit; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci if (!dev->opened) { 40862306a36Sopenharmony_ci /* We were closed while waiting for an URB */ 40962306a36Sopenharmony_ci retval = -ENODEV; 41062306a36Sopenharmony_ci goto exit; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci atomic_inc(&dev->write_busy); 41562306a36Sopenharmony_ci int_out_urb = usb_alloc_urb(0, GFP_KERNEL); 41662306a36Sopenharmony_ci if (!int_out_urb) { 41762306a36Sopenharmony_ci retval = -ENOMEM; 41862306a36Sopenharmony_ci goto error_no_urb; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci buf = usb_alloc_coherent(dev->udev, dev->report_size, 42162306a36Sopenharmony_ci GFP_KERNEL, &int_out_urb->transfer_dma); 42262306a36Sopenharmony_ci if (!buf) { 42362306a36Sopenharmony_ci retval = -ENOMEM; 42462306a36Sopenharmony_ci dev_dbg(&dev->interface->dev, 42562306a36Sopenharmony_ci "Unable to allocate buffer\n"); 42662306a36Sopenharmony_ci goto error_no_buffer; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci usb_fill_int_urb(int_out_urb, dev->udev, 42962306a36Sopenharmony_ci usb_sndintpipe(dev->udev, 43062306a36Sopenharmony_ci dev->int_out_endpoint->bEndpointAddress), 43162306a36Sopenharmony_ci buf, dev->report_size, 43262306a36Sopenharmony_ci iowarrior_write_callback, dev, 43362306a36Sopenharmony_ci dev->int_out_endpoint->bInterval); 43462306a36Sopenharmony_ci int_out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 43562306a36Sopenharmony_ci if (copy_from_user(buf, user_buffer, count)) { 43662306a36Sopenharmony_ci retval = -EFAULT; 43762306a36Sopenharmony_ci goto error; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci usb_anchor_urb(int_out_urb, &dev->submitted); 44062306a36Sopenharmony_ci retval = usb_submit_urb(int_out_urb, GFP_KERNEL); 44162306a36Sopenharmony_ci if (retval) { 44262306a36Sopenharmony_ci dev_dbg(&dev->interface->dev, 44362306a36Sopenharmony_ci "submit error %d for urb nr.%d\n", 44462306a36Sopenharmony_ci retval, atomic_read(&dev->write_busy)); 44562306a36Sopenharmony_ci usb_unanchor_urb(int_out_urb); 44662306a36Sopenharmony_ci goto error; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci /* submit was ok */ 44962306a36Sopenharmony_ci retval = count; 45062306a36Sopenharmony_ci usb_free_urb(int_out_urb); 45162306a36Sopenharmony_ci goto exit; 45262306a36Sopenharmony_ci default: 45362306a36Sopenharmony_ci /* what do we have here ? An unsupported Product-ID ? */ 45462306a36Sopenharmony_ci dev_err(&dev->interface->dev, "%s - not supported for product=0x%x\n", 45562306a36Sopenharmony_ci __func__, dev->product_id); 45662306a36Sopenharmony_ci retval = -EFAULT; 45762306a36Sopenharmony_ci goto exit; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_cierror: 46062306a36Sopenharmony_ci usb_free_coherent(dev->udev, dev->report_size, buf, 46162306a36Sopenharmony_ci int_out_urb->transfer_dma); 46262306a36Sopenharmony_cierror_no_buffer: 46362306a36Sopenharmony_ci usb_free_urb(int_out_urb); 46462306a36Sopenharmony_cierror_no_urb: 46562306a36Sopenharmony_ci atomic_dec(&dev->write_busy); 46662306a36Sopenharmony_ci wake_up_interruptible(&dev->write_wait); 46762306a36Sopenharmony_ciexit: 46862306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 46962306a36Sopenharmony_ci return retval; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci/* 47362306a36Sopenharmony_ci * iowarrior_ioctl 47462306a36Sopenharmony_ci */ 47562306a36Sopenharmony_cistatic long iowarrior_ioctl(struct file *file, unsigned int cmd, 47662306a36Sopenharmony_ci unsigned long arg) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct iowarrior *dev = NULL; 47962306a36Sopenharmony_ci __u8 *buffer; 48062306a36Sopenharmony_ci __u8 __user *user_buffer; 48162306a36Sopenharmony_ci int retval; 48262306a36Sopenharmony_ci int io_res; /* checks for bytes read/written and copy_to/from_user results */ 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci dev = file->private_data; 48562306a36Sopenharmony_ci if (!dev) 48662306a36Sopenharmony_ci return -ENODEV; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci buffer = kzalloc(dev->report_size, GFP_KERNEL); 48962306a36Sopenharmony_ci if (!buffer) 49062306a36Sopenharmony_ci return -ENOMEM; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci mutex_lock(&dev->mutex); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* verify that the device wasn't unplugged */ 49562306a36Sopenharmony_ci if (!dev->present) { 49662306a36Sopenharmony_ci retval = -ENODEV; 49762306a36Sopenharmony_ci goto error_out; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci dev_dbg(&dev->interface->dev, "minor %d, cmd 0x%.4x, arg %ld\n", 50162306a36Sopenharmony_ci dev->minor, cmd, arg); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci retval = 0; 50462306a36Sopenharmony_ci io_res = 0; 50562306a36Sopenharmony_ci switch (cmd) { 50662306a36Sopenharmony_ci case IOW_WRITE: 50762306a36Sopenharmony_ci if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW24 || 50862306a36Sopenharmony_ci dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW24SAG || 50962306a36Sopenharmony_ci dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV1 || 51062306a36Sopenharmony_ci dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV2 || 51162306a36Sopenharmony_ci dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW40) { 51262306a36Sopenharmony_ci user_buffer = (__u8 __user *)arg; 51362306a36Sopenharmony_ci io_res = copy_from_user(buffer, user_buffer, 51462306a36Sopenharmony_ci dev->report_size); 51562306a36Sopenharmony_ci if (io_res) { 51662306a36Sopenharmony_ci retval = -EFAULT; 51762306a36Sopenharmony_ci } else { 51862306a36Sopenharmony_ci io_res = usb_set_report(dev->interface, 2, 0, 51962306a36Sopenharmony_ci buffer, 52062306a36Sopenharmony_ci dev->report_size); 52162306a36Sopenharmony_ci if (io_res < 0) 52262306a36Sopenharmony_ci retval = io_res; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci } else { 52562306a36Sopenharmony_ci retval = -EINVAL; 52662306a36Sopenharmony_ci dev_err(&dev->interface->dev, 52762306a36Sopenharmony_ci "ioctl 'IOW_WRITE' is not supported for product=0x%x.\n", 52862306a36Sopenharmony_ci dev->product_id); 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci break; 53162306a36Sopenharmony_ci case IOW_READ: 53262306a36Sopenharmony_ci user_buffer = (__u8 __user *)arg; 53362306a36Sopenharmony_ci io_res = usb_get_report(dev->udev, 53462306a36Sopenharmony_ci dev->interface->cur_altsetting, 1, 0, 53562306a36Sopenharmony_ci buffer, dev->report_size); 53662306a36Sopenharmony_ci if (io_res < 0) 53762306a36Sopenharmony_ci retval = io_res; 53862306a36Sopenharmony_ci else { 53962306a36Sopenharmony_ci io_res = copy_to_user(user_buffer, buffer, dev->report_size); 54062306a36Sopenharmony_ci if (io_res) 54162306a36Sopenharmony_ci retval = -EFAULT; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci break; 54462306a36Sopenharmony_ci case IOW_GETINFO: 54562306a36Sopenharmony_ci { 54662306a36Sopenharmony_ci /* Report available information for the device */ 54762306a36Sopenharmony_ci struct iowarrior_info info; 54862306a36Sopenharmony_ci /* needed for power consumption */ 54962306a36Sopenharmony_ci struct usb_config_descriptor *cfg_descriptor = &dev->udev->actconfig->desc; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci memset(&info, 0, sizeof(info)); 55262306a36Sopenharmony_ci /* directly from the descriptor */ 55362306a36Sopenharmony_ci info.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); 55462306a36Sopenharmony_ci info.product = dev->product_id; 55562306a36Sopenharmony_ci info.revision = le16_to_cpu(dev->udev->descriptor.bcdDevice); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* 0==UNKNOWN, 1==LOW(usb1.1) ,2=FULL(usb1.1), 3=HIGH(usb2.0) */ 55862306a36Sopenharmony_ci info.speed = dev->udev->speed; 55962306a36Sopenharmony_ci info.if_num = dev->interface->cur_altsetting->desc.bInterfaceNumber; 56062306a36Sopenharmony_ci info.report_size = dev->report_size; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* serial number string has been read earlier 8 chars or empty string */ 56362306a36Sopenharmony_ci memcpy(info.serial, dev->chip_serial, 56462306a36Sopenharmony_ci sizeof(dev->chip_serial)); 56562306a36Sopenharmony_ci if (cfg_descriptor == NULL) { 56662306a36Sopenharmony_ci info.power = -1; /* no information available */ 56762306a36Sopenharmony_ci } else { 56862306a36Sopenharmony_ci /* the MaxPower is stored in units of 2mA to make it fit into a byte-value */ 56962306a36Sopenharmony_ci info.power = cfg_descriptor->bMaxPower * 2; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci io_res = copy_to_user((struct iowarrior_info __user *)arg, &info, 57262306a36Sopenharmony_ci sizeof(struct iowarrior_info)); 57362306a36Sopenharmony_ci if (io_res) 57462306a36Sopenharmony_ci retval = -EFAULT; 57562306a36Sopenharmony_ci break; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci default: 57862306a36Sopenharmony_ci /* return that we did not understand this ioctl call */ 57962306a36Sopenharmony_ci retval = -ENOTTY; 58062306a36Sopenharmony_ci break; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_cierror_out: 58362306a36Sopenharmony_ci /* unlock the device */ 58462306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 58562306a36Sopenharmony_ci kfree(buffer); 58662306a36Sopenharmony_ci return retval; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci/* 59062306a36Sopenharmony_ci * iowarrior_open 59162306a36Sopenharmony_ci */ 59262306a36Sopenharmony_cistatic int iowarrior_open(struct inode *inode, struct file *file) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci struct iowarrior *dev = NULL; 59562306a36Sopenharmony_ci struct usb_interface *interface; 59662306a36Sopenharmony_ci int subminor; 59762306a36Sopenharmony_ci int retval = 0; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci subminor = iminor(inode); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci interface = usb_find_interface(&iowarrior_driver, subminor); 60262306a36Sopenharmony_ci if (!interface) { 60362306a36Sopenharmony_ci pr_err("%s - error, can't find device for minor %d\n", 60462306a36Sopenharmony_ci __func__, subminor); 60562306a36Sopenharmony_ci return -ENODEV; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci dev = usb_get_intfdata(interface); 60962306a36Sopenharmony_ci if (!dev) 61062306a36Sopenharmony_ci return -ENODEV; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci mutex_lock(&dev->mutex); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* Only one process can open each device, no sharing. */ 61562306a36Sopenharmony_ci if (dev->opened) { 61662306a36Sopenharmony_ci retval = -EBUSY; 61762306a36Sopenharmony_ci goto out; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* setup interrupt handler for receiving values */ 62162306a36Sopenharmony_ci if ((retval = usb_submit_urb(dev->int_in_urb, GFP_KERNEL)) < 0) { 62262306a36Sopenharmony_ci dev_err(&interface->dev, "Error %d while submitting URB\n", retval); 62362306a36Sopenharmony_ci retval = -EFAULT; 62462306a36Sopenharmony_ci goto out; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci /* increment our usage count for the driver */ 62762306a36Sopenharmony_ci ++dev->opened; 62862306a36Sopenharmony_ci /* save our object in the file's private structure */ 62962306a36Sopenharmony_ci file->private_data = dev; 63062306a36Sopenharmony_ci retval = 0; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ciout: 63362306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 63462306a36Sopenharmony_ci return retval; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci/* 63862306a36Sopenharmony_ci * iowarrior_release 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_cistatic int iowarrior_release(struct inode *inode, struct file *file) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci struct iowarrior *dev; 64362306a36Sopenharmony_ci int retval = 0; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci dev = file->private_data; 64662306a36Sopenharmony_ci if (!dev) 64762306a36Sopenharmony_ci return -ENODEV; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* lock our device */ 65262306a36Sopenharmony_ci mutex_lock(&dev->mutex); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (dev->opened <= 0) { 65562306a36Sopenharmony_ci retval = -ENODEV; /* close called more than once */ 65662306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 65762306a36Sopenharmony_ci } else { 65862306a36Sopenharmony_ci dev->opened = 0; /* we're closing now */ 65962306a36Sopenharmony_ci retval = 0; 66062306a36Sopenharmony_ci if (dev->present) { 66162306a36Sopenharmony_ci /* 66262306a36Sopenharmony_ci The device is still connected so we only shutdown 66362306a36Sopenharmony_ci pending read-/write-ops. 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_ci usb_kill_urb(dev->int_in_urb); 66662306a36Sopenharmony_ci wake_up_interruptible(&dev->read_wait); 66762306a36Sopenharmony_ci wake_up_interruptible(&dev->write_wait); 66862306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 66962306a36Sopenharmony_ci } else { 67062306a36Sopenharmony_ci /* The device was unplugged, cleanup resources */ 67162306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 67262306a36Sopenharmony_ci iowarrior_delete(dev); 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci return retval; 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic __poll_t iowarrior_poll(struct file *file, poll_table * wait) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci struct iowarrior *dev = file->private_data; 68162306a36Sopenharmony_ci __poll_t mask = 0; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (!dev->present) 68462306a36Sopenharmony_ci return EPOLLERR | EPOLLHUP; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci poll_wait(file, &dev->read_wait, wait); 68762306a36Sopenharmony_ci poll_wait(file, &dev->write_wait, wait); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (!dev->present) 69062306a36Sopenharmony_ci return EPOLLERR | EPOLLHUP; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (read_index(dev) != -1) 69362306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci if (atomic_read(&dev->write_busy) < MAX_WRITES_IN_FLIGHT) 69662306a36Sopenharmony_ci mask |= EPOLLOUT | EPOLLWRNORM; 69762306a36Sopenharmony_ci return mask; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci/* 70162306a36Sopenharmony_ci * File operations needed when we register this driver. 70262306a36Sopenharmony_ci * This assumes that this driver NEEDS file operations, 70362306a36Sopenharmony_ci * of course, which means that the driver is expected 70462306a36Sopenharmony_ci * to have a node in the /dev directory. If the USB 70562306a36Sopenharmony_ci * device were for a network interface then the driver 70662306a36Sopenharmony_ci * would use "struct net_driver" instead, and a serial 70762306a36Sopenharmony_ci * device would use "struct tty_driver". 70862306a36Sopenharmony_ci */ 70962306a36Sopenharmony_cistatic const struct file_operations iowarrior_fops = { 71062306a36Sopenharmony_ci .owner = THIS_MODULE, 71162306a36Sopenharmony_ci .write = iowarrior_write, 71262306a36Sopenharmony_ci .read = iowarrior_read, 71362306a36Sopenharmony_ci .unlocked_ioctl = iowarrior_ioctl, 71462306a36Sopenharmony_ci .open = iowarrior_open, 71562306a36Sopenharmony_ci .release = iowarrior_release, 71662306a36Sopenharmony_ci .poll = iowarrior_poll, 71762306a36Sopenharmony_ci .llseek = noop_llseek, 71862306a36Sopenharmony_ci}; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic char *iowarrior_devnode(const struct device *dev, umode_t *mode) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci/* 72662306a36Sopenharmony_ci * usb class driver info in order to get a minor number from the usb core, 72762306a36Sopenharmony_ci * and to have the device registered with devfs and the driver core 72862306a36Sopenharmony_ci */ 72962306a36Sopenharmony_cistatic struct usb_class_driver iowarrior_class = { 73062306a36Sopenharmony_ci .name = "iowarrior%d", 73162306a36Sopenharmony_ci .devnode = iowarrior_devnode, 73262306a36Sopenharmony_ci .fops = &iowarrior_fops, 73362306a36Sopenharmony_ci .minor_base = IOWARRIOR_MINOR_BASE, 73462306a36Sopenharmony_ci}; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci/*---------------------------------*/ 73762306a36Sopenharmony_ci/* probe and disconnect functions */ 73862306a36Sopenharmony_ci/*---------------------------------*/ 73962306a36Sopenharmony_ci/* 74062306a36Sopenharmony_ci * iowarrior_probe 74162306a36Sopenharmony_ci * 74262306a36Sopenharmony_ci * Called by the usb core when a new device is connected that it thinks 74362306a36Sopenharmony_ci * this driver might be interested in. 74462306a36Sopenharmony_ci */ 74562306a36Sopenharmony_cistatic int iowarrior_probe(struct usb_interface *interface, 74662306a36Sopenharmony_ci const struct usb_device_id *id) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(interface); 74962306a36Sopenharmony_ci struct iowarrior *dev = NULL; 75062306a36Sopenharmony_ci struct usb_host_interface *iface_desc; 75162306a36Sopenharmony_ci int retval = -ENOMEM; 75262306a36Sopenharmony_ci int res; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* allocate memory for our device state and initialize it */ 75562306a36Sopenharmony_ci dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL); 75662306a36Sopenharmony_ci if (!dev) 75762306a36Sopenharmony_ci return retval; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci mutex_init(&dev->mutex); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci atomic_set(&dev->intr_idx, 0); 76262306a36Sopenharmony_ci atomic_set(&dev->read_idx, 0); 76362306a36Sopenharmony_ci atomic_set(&dev->overflow_flag, 0); 76462306a36Sopenharmony_ci init_waitqueue_head(&dev->read_wait); 76562306a36Sopenharmony_ci atomic_set(&dev->write_busy, 0); 76662306a36Sopenharmony_ci init_waitqueue_head(&dev->write_wait); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci dev->udev = udev; 76962306a36Sopenharmony_ci dev->interface = usb_get_intf(interface); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci iface_desc = interface->cur_altsetting; 77262306a36Sopenharmony_ci dev->product_id = le16_to_cpu(udev->descriptor.idProduct); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci init_usb_anchor(&dev->submitted); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci res = usb_find_last_int_in_endpoint(iface_desc, &dev->int_in_endpoint); 77762306a36Sopenharmony_ci if (res) { 77862306a36Sopenharmony_ci dev_err(&interface->dev, "no interrupt-in endpoint found\n"); 77962306a36Sopenharmony_ci retval = res; 78062306a36Sopenharmony_ci goto error; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if ((dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56) || 78462306a36Sopenharmony_ci (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56AM) || 78562306a36Sopenharmony_ci (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW28) || 78662306a36Sopenharmony_ci (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW28L) || 78762306a36Sopenharmony_ci (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW100)) { 78862306a36Sopenharmony_ci res = usb_find_last_int_out_endpoint(iface_desc, 78962306a36Sopenharmony_ci &dev->int_out_endpoint); 79062306a36Sopenharmony_ci if (res) { 79162306a36Sopenharmony_ci dev_err(&interface->dev, "no interrupt-out endpoint found\n"); 79262306a36Sopenharmony_ci retval = res; 79362306a36Sopenharmony_ci goto error; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci /* we have to check the report_size often, so remember it in the endianness suitable for our machine */ 79862306a36Sopenharmony_ci dev->report_size = usb_endpoint_maxp(dev->int_in_endpoint); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci /* 80162306a36Sopenharmony_ci * Some devices need the report size to be different than the 80262306a36Sopenharmony_ci * endpoint size. 80362306a36Sopenharmony_ci */ 80462306a36Sopenharmony_ci if (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) { 80562306a36Sopenharmony_ci switch (dev->product_id) { 80662306a36Sopenharmony_ci case USB_DEVICE_ID_CODEMERCS_IOW56: 80762306a36Sopenharmony_ci case USB_DEVICE_ID_CODEMERCS_IOW56AM: 80862306a36Sopenharmony_ci dev->report_size = 7; 80962306a36Sopenharmony_ci break; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci case USB_DEVICE_ID_CODEMERCS_IOW28: 81262306a36Sopenharmony_ci case USB_DEVICE_ID_CODEMERCS_IOW28L: 81362306a36Sopenharmony_ci dev->report_size = 4; 81462306a36Sopenharmony_ci break; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci case USB_DEVICE_ID_CODEMERCS_IOW100: 81762306a36Sopenharmony_ci dev->report_size = 12; 81862306a36Sopenharmony_ci break; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci /* create the urb and buffer for reading */ 82362306a36Sopenharmony_ci dev->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); 82462306a36Sopenharmony_ci if (!dev->int_in_urb) 82562306a36Sopenharmony_ci goto error; 82662306a36Sopenharmony_ci dev->int_in_buffer = kmalloc(dev->report_size, GFP_KERNEL); 82762306a36Sopenharmony_ci if (!dev->int_in_buffer) 82862306a36Sopenharmony_ci goto error; 82962306a36Sopenharmony_ci usb_fill_int_urb(dev->int_in_urb, dev->udev, 83062306a36Sopenharmony_ci usb_rcvintpipe(dev->udev, 83162306a36Sopenharmony_ci dev->int_in_endpoint->bEndpointAddress), 83262306a36Sopenharmony_ci dev->int_in_buffer, dev->report_size, 83362306a36Sopenharmony_ci iowarrior_callback, dev, 83462306a36Sopenharmony_ci dev->int_in_endpoint->bInterval); 83562306a36Sopenharmony_ci /* create an internal buffer for interrupt data from the device */ 83662306a36Sopenharmony_ci dev->read_queue = 83762306a36Sopenharmony_ci kmalloc_array(dev->report_size + 1, MAX_INTERRUPT_BUFFER, 83862306a36Sopenharmony_ci GFP_KERNEL); 83962306a36Sopenharmony_ci if (!dev->read_queue) 84062306a36Sopenharmony_ci goto error; 84162306a36Sopenharmony_ci /* Get the serial-number of the chip */ 84262306a36Sopenharmony_ci memset(dev->chip_serial, 0x00, sizeof(dev->chip_serial)); 84362306a36Sopenharmony_ci usb_string(udev, udev->descriptor.iSerialNumber, dev->chip_serial, 84462306a36Sopenharmony_ci sizeof(dev->chip_serial)); 84562306a36Sopenharmony_ci if (strlen(dev->chip_serial) != 8) 84662306a36Sopenharmony_ci memset(dev->chip_serial, 0x00, sizeof(dev->chip_serial)); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci /* Set the idle timeout to 0, if this is interface 0 */ 84962306a36Sopenharmony_ci if (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) { 85062306a36Sopenharmony_ci usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 85162306a36Sopenharmony_ci 0x0A, 85262306a36Sopenharmony_ci USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, 85362306a36Sopenharmony_ci 0, NULL, 0, USB_CTRL_SET_TIMEOUT); 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci /* allow device read and ioctl */ 85662306a36Sopenharmony_ci dev->present = 1; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci /* we can register the device now, as it is ready */ 85962306a36Sopenharmony_ci usb_set_intfdata(interface, dev); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci retval = usb_register_dev(interface, &iowarrior_class); 86262306a36Sopenharmony_ci if (retval) { 86362306a36Sopenharmony_ci /* something prevented us from registering this driver */ 86462306a36Sopenharmony_ci dev_err(&interface->dev, "Not able to get a minor for this device.\n"); 86562306a36Sopenharmony_ci goto error; 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci dev->minor = interface->minor; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci /* let the user know what node this device is now attached to */ 87162306a36Sopenharmony_ci dev_info(&interface->dev, "IOWarrior product=0x%x, serial=%s interface=%d " 87262306a36Sopenharmony_ci "now attached to iowarrior%d\n", dev->product_id, dev->chip_serial, 87362306a36Sopenharmony_ci iface_desc->desc.bInterfaceNumber, dev->minor - IOWARRIOR_MINOR_BASE); 87462306a36Sopenharmony_ci return retval; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_cierror: 87762306a36Sopenharmony_ci iowarrior_delete(dev); 87862306a36Sopenharmony_ci return retval; 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci/* 88262306a36Sopenharmony_ci * iowarrior_disconnect 88362306a36Sopenharmony_ci * 88462306a36Sopenharmony_ci * Called by the usb core when the device is removed from the system. 88562306a36Sopenharmony_ci */ 88662306a36Sopenharmony_cistatic void iowarrior_disconnect(struct usb_interface *interface) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci struct iowarrior *dev = usb_get_intfdata(interface); 88962306a36Sopenharmony_ci int minor = dev->minor; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci usb_deregister_dev(interface, &iowarrior_class); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci mutex_lock(&dev->mutex); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* prevent device read, write and ioctl */ 89662306a36Sopenharmony_ci dev->present = 0; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (dev->opened) { 89962306a36Sopenharmony_ci /* There is a process that holds a filedescriptor to the device , 90062306a36Sopenharmony_ci so we only shutdown read-/write-ops going on. 90162306a36Sopenharmony_ci Deleting the device is postponed until close() was called. 90262306a36Sopenharmony_ci */ 90362306a36Sopenharmony_ci usb_kill_urb(dev->int_in_urb); 90462306a36Sopenharmony_ci usb_kill_anchored_urbs(&dev->submitted); 90562306a36Sopenharmony_ci wake_up_interruptible(&dev->read_wait); 90662306a36Sopenharmony_ci wake_up_interruptible(&dev->write_wait); 90762306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 90862306a36Sopenharmony_ci } else { 90962306a36Sopenharmony_ci /* no process is using the device, cleanup now */ 91062306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 91162306a36Sopenharmony_ci iowarrior_delete(dev); 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci dev_info(&interface->dev, "I/O-Warror #%d now disconnected\n", 91562306a36Sopenharmony_ci minor - IOWARRIOR_MINOR_BASE); 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci/* usb specific object needed to register this driver with the usb subsystem */ 91962306a36Sopenharmony_cistatic struct usb_driver iowarrior_driver = { 92062306a36Sopenharmony_ci .name = "iowarrior", 92162306a36Sopenharmony_ci .probe = iowarrior_probe, 92262306a36Sopenharmony_ci .disconnect = iowarrior_disconnect, 92362306a36Sopenharmony_ci .id_table = iowarrior_ids, 92462306a36Sopenharmony_ci}; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_cimodule_usb_driver(iowarrior_driver); 927