| Start/ | End/ | |||
| True | False | - | Line | Source |
| 1 | /** | |||
| 2 | * Generic USB driver for report based interrupt in/out devices | |||
| 3 | * like LD Didactic's USB devices. LD Didactic's USB devices are | |||
| 4 | * HID devices which do not use HID report definitons (they use | |||
| 5 | * raw interrupt in and our reports only for communication). | |||
| 6 | * | |||
| 7 | * This driver uses a ring buffer for time critical reading of | |||
| 8 | * interrupt in reports and provides read and write methods for | |||
| 9 | * raw interrupt reports (similar to the Windows HID driver). | |||
| 10 | * Devices based on the book USB COMPLETE by Jan Axelson may need | |||
| 11 | * such a compatibility to the Windows HID driver. | |||
| 12 | * | |||
| 13 | * Copyright (C) 2005 Michael Hund <mhund@ld-didactic.de> | |||
| 14 | * | |||
| 15 | * This program is free software; you can redistribute it and/or | |||
| 16 | * modify it under the terms of the GNU General Public License as | |||
| 17 | * published by the Free Software Foundation; either version 2 of | |||
| 18 | * the License, or (at your option) any later version. | |||
| 19 | * | |||
| 20 | * Derived from Lego USB Tower driver | |||
| 21 | * Copyright (C) 2003 David Glance <advidgsf@sourceforge.net> | |||
| 22 | * 2001-2004 Juergen Stuber <starblue@users.sourceforge.net> | |||
| 23 | * | |||
| 24 | * V0.1 (mh) Initial version | |||
| 25 | * V0.11 (mh) Added raw support for HID 1.0 devices (no interrupt out endpoint) | |||
| 26 | * V0.12 (mh) Added kmalloc check for string buffer | |||
| 27 | * V0.13 (mh) Added support for LD X-Ray and Machine Test System | |||
| 28 | */ | |||
| 29 | ||||
| 30 | #include <linux/config.h> | |||
| 31 | #include <linux/kernel.h> | |||
| 32 | #include <linux/errno.h> | |||
| 33 | #include <linux/init.h> | |||
| 34 | #include <linux/slab.h> | |||
| 35 | #include <linux/module.h> | |||
| 36 | ||||
| 37 | #include <asm/uaccess.h> | |||
| 38 | #include <linux/input.h> | |||
| 39 | #include <linux/usb.h> | |||
| 40 | #include <linux/poll.h> | |||
| 41 | ||||
| 42 | /* Define these values to match your devices */ | |||
| 43 | #define USB_VENDOR_ID_LD 0x0f11 /* USB Vendor ID of LD Didactic GmbH */ | |||
| 44 | #define USB_DEVICE_ID_LD_CASSY 0x1000 /* USB Product ID of CASSY-S */ | |||
| 45 | #define USB_DEVICE_ID_LD_POCKETCASSY 0x1010 /* USB Product ID of Pocket-CASSY */ | |||
| 46 | #define USB_DEVICE_ID_LD_MOBILECASSY 0x1020 /* USB Product ID of Mobile-CASSY */ | |||
| 47 | #define USB_DEVICE_ID_LD_JWM 0x1080 /* USB Product ID of Joule and Wattmeter */ | |||
| 48 | #define USB_DEVICE_ID_LD_DMMP 0x1081 /* USB Product ID of Digital Multimeter P (reserved) */ | |||
| 49 | #define USB_DEVICE_ID_LD_UMIP 0x1090 /* USB Product ID of UMI P */ | |||
| 50 | #define USB_DEVICE_ID_LD_XRAY1 0x1100 /* USB Product ID of X-Ray Apparatus */ | |||
| 51 | #define USB_DEVICE_ID_LD_XRAY2 0x1101 /* USB Product ID of X-Ray Apparatus */ | |||
| 52 | #define USB_DEVICE_ID_LD_VIDEOCOM 0x1200 /* USB Product ID of VideoCom */ | |||
| 53 | #define USB_DEVICE_ID_LD_COM3LAB 0x2000 /* USB Product ID of COM3LAB */ | |||
| 54 | #define USB_DEVICE_ID_LD_TELEPORT 0x2010 /* USB Product ID of Terminal Adapter */ | |||
| 55 | #define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020 /* USB Product ID of Network Analyser */ | |||
| 56 | #define USB_DEVICE_ID_LD_POWERCONTROL 0x2030 /* USB Product ID of Converter Control Unit */ | |||
| 57 | #define USB_DEVICE_ID_LD_MACHINETEST 0x2040 /* USB Product ID of Machine Test System */ | |||
| 58 | ||||
| 59 | #define USB_VENDOR_ID_VERNIER 0x08f7 | |||
| 60 | #define USB_DEVICE_ID_VERNIER_LABPRO 0x0001 | |||
| 61 | #define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002 | |||
| 62 | #define USB_DEVICE_ID_VERNIER_SKIP 0x0003 | |||
| 63 | #define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004 | |||
| 64 | ||||
| 65 | ||||
| 66 | #ifdef CONFIG_USB_DYNAMIC_MINORS | |||
| 67 | #define USB_LD_MINOR_BASE 0 | |||
| 68 | #else | |||
| 69 | #define USB_LD_MINOR_BASE 176 | |||
| 70 | #endif | |||
| 71 | ||||
| 72 | /* table of devices that work with this driver */ | |||
| 73 | static struct usb_device_id ld_usb_table [] = { | |||
| 74 | { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) }, | |||
| 75 | { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) }, | |||
| 76 | { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY) }, | |||
| 77 | { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) }, | |||
| 78 | { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) }, | |||
| 79 | { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) }, | |||
| 80 | { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1) }, | |||
| 81 | { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2) }, | |||
| 82 | { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM) }, | |||
| 83 | { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB) }, | |||
| 84 | { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT) }, | |||
| 85 | { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER) }, | |||
| 86 | { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL) }, | |||
| 87 | { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST) }, | |||
| 88 | { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) }, | |||
| 89 | { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) }, | |||
| 90 | { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, | |||
| 91 | { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) }, | |||
| 92 | { } /* Terminating entry */ | |||
| 93 | }; | |||
| 94 | MODULE_DEVICE_TABLE(usb, ld_usb_table); | |||
| 95 | MODULE_VERSION("V0.13"); | |||
| 96 | MODULE_AUTHOR("Michael Hund <mhund@ld-didactic.de>"); | |||
| 97 | MODULE_DESCRIPTION("LD USB Driver"); | |||
| 98 | MODULE_LICENSE("GPL"); | |||
| 99 | MODULE_SUPPORTED_DEVICE("LD USB Devices"); | |||
| 100 | ||||
| 101 | #ifdef CONFIG_USB_DEBUG | |||
| 102 | static int debug = 1; | |||
| 103 | #else | |||
| 104 | static int debug = 0; | |||
| 105 | #endif | |||
| 106 | ||||
| 107 | /* Use our own dbg macro */ | |||
| 108 | #define dbg_info(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0) | |||
| 109 | ||||
| 110 | /* Module parameters */ | |||
| 0 | 0 | - | 111 | module_param(debug, int, S_IRUGO | S_IWUSR); |
| 0 | - | 111 | return ( & ( debug ) ) | |
| 112 | MODULE_PARM_DESC(debug, "Debug enabled or not"); | |||
| 113 | ||||
| 114 | /* All interrupt in transfers are collected in a ring buffer to | |||
| 115 | * avoid racing conditions and get better performance of the driver. | |||
| 116 | */ | |||
| 117 | static int ring_buffer_size = 128; | |||
| 0 | 0 | - | 118 | module_param(ring_buffer_size, int, 0); |
| 0 | - | 118 | return ( & ( ring_buffer_size ) ) | |
| 119 | MODULE_PARM_DESC(ring_buffer_size, "Read ring buffer size in reports"); | |||
| 120 | ||||
| 121 | /* The write_buffer can contain more than one interrupt out transfer. | |||
| 122 | */ | |||
| 123 | static int write_buffer_size = 10; | |||
| 0 | 0 | - | 124 | module_param(write_buffer_size, int, 0); |
| 0 | - | 124 | return ( & ( write_buffer_size ) ) | |
| 125 | MODULE_PARM_DESC(write_buffer_size, "Write buffer size in reports"); | |||
| 126 | ||||
| 127 | /* As of kernel version 2.6.4 ehci-hcd uses an | |||
| 128 | * "only one interrupt transfer per frame" shortcut | |||
| 129 | * to simplify the scheduling of periodic transfers. | |||
| 130 | * This conflicts with our standard 1ms intervals for in and out URBs. | |||
| 131 | * We use default intervals of 2ms for in and 2ms for out transfers, | |||
| 132 | * which should be fast enough. | |||
| 133 | * Increase the interval to allow more devices that do interrupt transfers, | |||
| 134 | * or set to 1 to use the standard interval from the endpoint descriptors. | |||
| 135 | */ | |||
| 136 | static int min_interrupt_in_interval = 2; | |||
| 0 | 0 | - | 137 | module_param(min_interrupt_in_interval, int, 0); |
| 0 | - | 137 | return ( & ( min_interrupt_in_interval ) ) | |
| 138 | MODULE_PARM_DESC(min_interrupt_in_interval, "Minimum interrupt in interval in ms"); | |||
| 139 | ||||
| 140 | static int min_interrupt_out_interval = 2; | |||
| 0 | 0 | - | 141 | module_param(min_interrupt_out_interval, int, 0); |
| 0 | - | 141 | return ( & ( min_interrupt_out_interval ) ) | |
| 142 | MODULE_PARM_DESC(min_interrupt_out_interval, "Minimum interrupt out interval in ms"); | |||
| 143 | ||||
| 144 | /* Structure to hold all of our device specific stuff */ | |||
| 145 | struct ld_usb { | |||
| 146 | struct semaphore sem; /* locks this structure */ | |||
| 147 | struct usb_interface* intf; /* save off the usb interface pointer */ | |||
| 148 | ||||
| 149 | int open_count; /* number of times this port has been opened */ | |||
| 150 | ||||
| 151 | char* ring_buffer; | |||
| 152 | unsigned int ring_head; | |||
| 153 | unsigned int ring_tail; | |||
| 154 | ||||
| 155 | wait_queue_head_t read_wait; | |||
| 156 | wait_queue_head_t write_wait; | |||
| 157 | ||||
| 158 | char* interrupt_in_buffer; | |||
| 159 | struct usb_endpoint_descriptor* interrupt_in_endpoint; | |||
| 160 | struct urb* interrupt_in_urb; | |||
| 161 | int interrupt_in_interval; | |||
| 162 | size_t interrupt_in_endpoint_size; | |||
| 163 | int interrupt_in_running; | |||
| 164 | int interrupt_in_done; | |||
| 165 | ||||
| 166 | char* interrupt_out_buffer; | |||
| 167 | struct usb_endpoint_descriptor* interrupt_out_endpoint; | |||
| 168 | struct urb* interrupt_out_urb; | |||
| 169 | int interrupt_out_interval; | |||
| 170 | size_t interrupt_out_endpoint_size; | |||
| 171 | int interrupt_out_busy; | |||
| 172 | }; | |||
| 173 | ||||
| 174 | /* prevent races between open() and disconnect() */ | |||
| 175 | static DECLARE_MUTEX(disconnect_sem); | |||
| 176 | ||||
| 177 | static struct usb_driver ld_usb_driver; | |||
| 178 | ||||
| 179 | /** | |||
| 180 | * ld_usb_abort_transfers | |||
| 181 | * aborts transfers and frees associated data structures | |||
| 182 | */ | |||
| 0 | 0 | - | 183 | static void ld_usb_abort_transfers(struct ld_usb *dev) |
| 184 | { | |||
| 185 | /* shutdown transfer */ | |||
| 0 | 0 | - | 186 | if (dev->interrupt_in_running) { |
| 187 | dev->interrupt_in_running = 0; | |||
| 0 | 0 | - | 188 | if (dev->intf) |
| 189 | usb_kill_urb(dev->interrupt_in_urb); | |||
| 190 | } | |||
| 0 | 0 | - | 191 | if (dev->interrupt_out_busy) |
| 0 | 0 | - | 192 | if (dev->intf) |
| 193 | usb_kill_urb(dev->interrupt_out_urb); | |||
| 194 | } | |||
| 195 | ||||
| 196 | /** | |||
| 197 | * ld_usb_delete | |||
| 198 | */ | |||
| 0 | 0 | - | 199 | static void ld_usb_delete(struct ld_usb *dev) |
| 200 | { | |||
| 201 | ld_usb_abort_transfers(dev); | |||
| 202 | ||||
| 203 | /* free data structures */ | |||
| 204 | usb_free_urb(dev->interrupt_in_urb); | |||
| 205 | usb_free_urb(dev->interrupt_out_urb); | |||
| 206 | kfree(dev->ring_buffer); | |||
| 207 | kfree(dev->interrupt_in_buffer); | |||
| 208 | kfree(dev->interrupt_out_buffer); | |||
| 209 | kfree(dev); | |||
| 210 | } | |||
| 211 | ||||
| 212 | /** | |||
| 213 | * ld_usb_interrupt_in_callback | |||
| 214 | */ | |||
| 0 | 0 | - | 215 | static void ld_usb_interrupt_in_callback(struct urb *urb, struct pt_regs *regs) |
| 216 | { | |||
| 217 | struct ld_usb *dev = urb->context; | |||
| 218 | size_t *actual_buffer; | |||
| 219 | unsigned int next_ring_head; | |||
| 220 | int retval; | |||
| 221 | ||||
| 0 | 0 | - | 222 | if (urb->status) { |
| 223 | if (urb->status == -ENOENT || | |||
| 224 | urb->status == -ECONNRESET || | |||
| 0 | 0 | - | 225 | urb->status == -ESHUTDOWN) { |
| 0 | - | 225 | T || _ || _ | |
| 0 | - | 225 | F || T || _ | |
| 0 | - | 225 | F || F || T | |
| 0 | - | 225 | F || F || F | |
| 0 | - | 226 | goto exit; | |
| 227 | } else { | |||
| 228 | dbg_info(&dev->intf->dev, "%s: nonzero status received: %d\n", | |||
| 0 | 0 | - | 228 | if (debug) |
| 0 | 0 | - | 228 | ternary-?: ( & dev -> intf -> dev ) ->.. |
| 0 | 0 | - | 228 | do-while (0) |
| 229 | __FUNCTION__, urb->status); | |||
| 0 | - | 230 | goto resubmit; /* maybe we can recover */ | |
| 231 | } | |||
| 232 | } | |||
| 233 | ||||
| 0 | 0 | - | 234 | if (urb->actual_length > 0) { |
| 235 | next_ring_head = (dev->ring_head+1) % ring_buffer_size; | |||
| 0 | 0 | - | 236 | if (next_ring_head != dev->ring_tail) { |
| 237 | actual_buffer = (size_t*)(dev->ring_buffer + dev->ring_head*(sizeof(size_t)+dev->interrupt_in_endpoint_size)); | |||
| 238 | /* actual_buffer gets urb->actual_length + interrupt_in_buffer */ | |||
| 239 | *actual_buffer = urb->actual_length; | |||
| 240 | memcpy(actual_buffer+1, dev->interrupt_in_buffer, urb->actual_length); | |||
| 241 | dev->ring_head = next_ring_head; | |||
| 242 | dbg_info(&dev->intf->dev, "%s: received %d bytes\n", | |||
| 0 | 0 | - | 242 | if (debug) |
| 0 | 0 | - | 242 | ternary-?: ( & dev -> intf -> dev ) ->.. |
| 0 | 0 | - | 242 | do-while (0) |
| 243 | __FUNCTION__, urb->actual_length); | |||
| 244 | } else | |||
| 245 | dev_warn(&dev->intf->dev, | |||
| 0 | 0 | - | 245 | ternary-?: ( & dev -> intf -> dev ) -> dri.. |
| 246 | "Ring buffer overflow, %d bytes dropped\n", | |||
| 247 | urb->actual_length); | |||
| 248 | } | |||
| 249 | ||||
| 250 | resubmit: | |||
| 251 | /* resubmit if we're still running */ | |||
| 0 | 0 | - | 252 | if (dev->interrupt_in_running && dev->intf) { |
| 0 | - | 252 | T && T | |
| 0 | - | 252 | T && F | |
| 0 | - | 252 | F && _ | |
| 253 | retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC); | |||
| 0 | 0 | - | 254 | if (retval) |
| 255 | dev_err(&dev->intf->dev, | |||
| 0 | 0 | - | 255 | ternary-?: ( & dev -> intf -> dev ) -> dri.. |
| 256 | "usb_submit_urb failed (%d)\n", retval); | |||
| 257 | } | |||
| 258 | ||||
| 259 | exit: | |||
| 260 | dev->interrupt_in_done = 1; | |||
| 261 | wake_up_interruptible(&dev->read_wait); | |||
| 262 | } | |||
| 263 | ||||
| 264 | /** | |||
| 265 | * ld_usb_interrupt_out_callback | |||
| 266 | */ | |||
| 0 | 0 | - | 267 | static void ld_usb_interrupt_out_callback(struct urb *urb, struct pt_regs *regs) |
| 268 | { | |||
| 269 | struct ld_usb *dev = urb->context; | |||
| 270 | ||||
| 271 | /* sync/async unlink faults aren't errors */ | |||
| 272 | if (urb->status && !(urb->status == -ENOENT || | |||
| 273 | urb->status == -ECONNRESET || | |||
| 0 | 0 | - | 274 | urb->status == -ESHUTDOWN)) |
| 0 | - | 274 | T && !(F || F || F) | |
| 0 | - | 274 | T && !(T || _ || _) | |
| 0 | - | 274 | T && !(F || T || _) | |
| 0 | - | 274 | T && !(F || F || T) | |
| 0 | - | 274 | F && !(_ || _ || _) | |
| 275 | dbg_info(&dev->intf->dev, | |||
| 0 | 0 | - | 275 | if (debug) |
| 0 | 0 | - | 275 | ternary-?: ( & dev -> intf -> dev ) -> d.. |
| 0 | 0 | - | 275 | do-while (0) |
| 276 | "%s - nonzero write interrupt status received: %d\n", | |||
| 277 | __FUNCTION__, urb->status); | |||
| 278 | ||||
| 279 | dev->interrupt_out_busy = 0; | |||
| 280 | wake_up_interruptible(&dev->write_wait); | |||
| 281 | } | |||
| 282 | ||||
| 283 | /** | |||
| 284 | * ld_usb_open | |||
| 285 | */ | |||
| 0 | 0 | - | 286 | static int ld_usb_open(struct inode *inode, struct file *file) |
| 287 | { | |||
| 288 | struct ld_usb *dev; | |||
| 289 | int subminor; | |||
| 290 | int retval = 0; | |||
| 291 | struct usb_interface *interface; | |||
| 292 | ||||
| 293 | nonseekable_open(inode, file); | |||
| 294 | subminor = iminor(inode); | |||
| 295 | ||||
| 296 | down(&disconnect_sem); | |||
| 297 | ||||
| 298 | interface = usb_find_interface(&ld_usb_driver, subminor); | |||
| 299 | ||||
| 0 | 0 | - | 300 | if (!interface) { |
| 301 | err("%s - error, can't find device for minor %d\n", | |||
| 302 | __FUNCTION__, subminor); | |||
| 303 | retval = -ENODEV; | |||
| 0 | - | 304 | goto unlock_disconnect_exit; | |
| 305 | } | |||
| 306 | ||||
| 307 | dev = usb_get_intfdata(interface); | |||
| 308 | ||||
| 0 | 0 | - | 309 | if (!dev) { |
| 310 | retval = -ENODEV; | |||
| 0 | - | 311 | goto unlock_disconnect_exit; | |
| 312 | } | |||
| 313 | ||||
| 314 | /* lock this device */ | |||
| 0 | 0 | - | 315 | if (down_interruptible(&dev->sem)) { |
| 316 | retval = -ERESTARTSYS; | |||
| 0 | - | 317 | goto unlock_disconnect_exit; | |
| 318 | } | |||
| 319 | ||||
| 320 | /* allow opening only once */ | |||
| 0 | 0 | - | 321 | if (dev->open_count) { |
| 322 | retval = -EBUSY; | |||
| 0 | - | 323 | goto unlock_exit; | |
| 324 | } | |||
| 325 | dev->open_count = 1; | |||
| 326 | ||||
| 327 | /* initialize in direction */ | |||
| 328 | dev->ring_head = 0; | |||
| 329 | dev->ring_tail = 0; | |||
| 330 | usb_fill_int_urb(dev->interrupt_in_urb, | |||
| 331 | interface_to_usbdev(interface), | |||
| 332 | usb_rcvintpipe(interface_to_usbdev(interface), | |||
| 333 | dev->interrupt_in_endpoint->bEndpointAddress), | |||
| 334 | dev->interrupt_in_buffer, | |||
| 335 | dev->interrupt_in_endpoint_size, | |||
| 336 | ld_usb_interrupt_in_callback, | |||
| 337 | dev, | |||
| 338 | dev->interrupt_in_interval); | |||
| 339 | ||||
| 340 | dev->interrupt_in_running = 1; | |||
| 341 | dev->interrupt_in_done = 0; | |||
| 342 | ||||
| 343 | retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); | |||
| 0 | 0 | - | 344 | if (retval) { |
| 345 | dev_err(&interface->dev, "Couldn't submit interrupt_in_urb %d\n", retval); | |||
| 0 | 0 | - | 345 | ternary-?: ( & interface -> dev ) -> driver |
| 346 | dev->interrupt_in_running = 0; | |||
| 347 | dev->open_count = 0; | |||
| 0 | - | 348 | goto unlock_exit; | |
| 349 | } | |||
| 350 | ||||
| 351 | /* save device in the file's private structure */ | |||
| 352 | file->private_data = dev; | |||
| 353 | ||||
| 354 | unlock_exit: | |||
| 355 | up(&dev->sem); | |||
| 356 | ||||
| 357 | unlock_disconnect_exit: | |||
| 358 | up(&disconnect_sem); | |||
| 359 | ||||
| 0 | - | 360 | return retval; | |
| 361 | } | |||
| 362 | ||||
| 363 | /** | |||
| 364 | * ld_usb_release | |||
| 365 | */ | |||
| 0 | 0 | - | 366 | static int ld_usb_release(struct inode *inode, struct file *file) |
| 367 | { | |||
| 368 | struct ld_usb *dev; | |||
| 369 | int retval = 0; | |||
| 370 | ||||
| 371 | dev = file->private_data; | |||
| 372 | ||||
| 0 | 0 | - | 373 | if (dev == NULL) { |
| 374 | retval = -ENODEV; | |||
| 0 | - | 375 | goto exit; | |
| 376 | } | |||
| 377 | ||||
| 0 | 0 | - | 378 | if (down_interruptible(&dev->sem)) { |
| 379 | retval = -ERESTARTSYS; | |||
| 0 | - | 380 | goto exit; | |
| 381 | } | |||
| 382 | ||||
| 0 | 0 | - | 383 | if (dev->open_count != 1) { |
| 384 | retval = -ENODEV; | |||
| 0 | - | 385 | goto unlock_exit; | |
| 386 | } | |||
| 0 | 0 | - | 387 | if (dev->intf == NULL) { |
| 388 | /* the device was unplugged before the file was released */ | |||
| 389 | up(&dev->sem); | |||
| 390 | /* unlock here as ld_usb_delete frees dev */ | |||
| 391 | ld_usb_delete(dev); | |||
| 0 | - | 392 | goto exit; | |
| 393 | } | |||
| 394 | ||||
| 395 | /* wait until write transfer is finished */ | |||
| 0 | 0 | - | 396 | if (dev->interrupt_out_busy) |
| 397 | wait_event_interruptible_timeout(dev->write_wait, !dev->interrupt_out_busy, 2 * HZ); | |||
| 398 | ld_usb_abort_transfers(dev); | |||
| 399 | dev->open_count = 0; | |||
| 400 | ||||
| 401 | unlock_exit: | |||
| 402 | up(&dev->sem); | |||
| 403 | ||||
| 404 | exit: | |||
| 0 | - | 405 | return retval; | |
| 406 | } | |||
| 407 | ||||
| 408 | /** | |||
| 409 | * ld_usb_poll | |||
| 410 | */ | |||
| 0 | 0 | - | 411 | static unsigned int ld_usb_poll(struct file *file, poll_table *wait) |
| 412 | { | |||
| 413 | struct ld_usb *dev; | |||
| 414 | unsigned int mask = 0; | |||
| 415 | ||||
| 416 | dev = file->private_data; | |||
| 417 | ||||
| 418 | poll_wait(file, &dev->read_wait, wait); | |||
| 419 | poll_wait(file, &dev->write_wait, wait); | |||
| 420 | ||||
| 0 | 0 | - | 421 | if (dev->ring_head != dev->ring_tail) |
| 422 | mask |= POLLIN | POLLRDNORM; | |||
| 0 | 0 | - | 423 | if (!dev->interrupt_out_busy) |
| 424 | mask |= POLLOUT | POLLWRNORM; | |||
| 425 | ||||
| 0 | - | 426 | return mask; | |
| 427 | } | |||
| 428 | ||||
| 429 | /** | |||
| 430 | * ld_usb_read | |||
| 431 | */ | |||
| 0 | 0 | - | 432 | static ssize_t ld_usb_read(struct file *file, char __user *buffer, size_t count, |
| 433 | loff_t *ppos) | |||
| 434 | { | |||
| 435 | struct ld_usb *dev; | |||
| 436 | size_t *actual_buffer; | |||
| 437 | size_t bytes_to_read; | |||
| 438 | int retval = 0; | |||
| 439 | ||||
| 440 | dev = file->private_data; | |||
| 441 | ||||
| 442 | /* verify that we actually have some data to read */ | |||
| 0 | 0 | - | 443 | if (count == 0) |
| 0 | - | 444 | goto exit; | |
| 445 | ||||
| 446 | /* lock this object */ | |||
| 0 | 0 | - | 447 | if (down_interruptible(&dev->sem)) { |
| 448 | retval = -ERESTARTSYS; | |||
| 0 | - | 449 | goto exit; | |
| 450 | } | |||
| 451 | ||||
| 452 | /* verify that the device wasn't unplugged */ | |||
| 0 | 0 | - | 453 | if (dev->intf == NULL) { |
| 454 | retval = -ENODEV; | |||
| 455 | err("No device or device unplugged %d\n", retval); | |||
| 0 | - | 456 | goto unlock_exit; | |
| 457 | } | |||
| 458 | ||||
| 459 | /* wait for data */ | |||
| 0 | 0 | - | 460 | if (dev->ring_head == dev->ring_tail) { |
| 0 | 0 | - | 461 | if (file->f_flags & O_NONBLOCK) { |
| 462 | retval = -EAGAIN; | |||
| 0 | - | 463 | goto unlock_exit; | |
| 464 | } | |||
| 465 | retval = wait_event_interruptible(dev->read_wait, dev->interrupt_in_done); | |||
| 0 | 0 | - | 466 | if (retval < 0) |
| 0 | - | 467 | goto unlock_exit; | |
| 468 | } | |||
| 469 | ||||
| 470 | /* actual_buffer contains actual_length + interrupt_in_buffer */ | |||
| 471 | actual_buffer = (size_t*)(dev->ring_buffer + dev->ring_tail*(sizeof(size_t)+dev->interrupt_in_endpoint_size)); | |||
| 472 | bytes_to_read = min(count, *actual_buffer); | |||
| 0 | 0 | - | 473 | if (bytes_to_read < *actual_buffer) |
| 474 | dev_warn(&dev->intf->dev, "Read buffer overflow, %zd bytes dropped\n", | |||
| 0 | 0 | - | 474 | ternary-?: ( & dev -> intf -> dev ) -> driver |
| 475 | *actual_buffer-bytes_to_read); | |||
| 476 | ||||
| 477 | /* copy one interrupt_in_buffer from ring_buffer into userspace */ | |||
| 0 | 0 | - | 478 | if (copy_to_user(buffer, actual_buffer+1, bytes_to_read)) { |
| 479 | retval = -EFAULT; | |||
| 0 | - | 480 | goto unlock_exit; | |
| 481 | } | |||
| 482 | dev->ring_tail = (dev->ring_tail+1) % ring_buffer_size; | |||
| 483 | ||||
| 484 | retval = bytes_to_read; | |||
| 485 | ||||
| 486 | unlock_exit: | |||
| 487 | /* unlock the device */ | |||
| 488 | up(&dev->sem); | |||
| 489 | ||||
| 490 | exit: | |||
| 0 | - | 491 | return retval; | |
| 492 | } | |||
| 493 | ||||
| 494 | /** | |||
| 495 | * ld_usb_write | |||
| 496 | */ | |||
| 0 | 0 | - | 497 | static ssize_t ld_usb_write(struct file *file, const char __user *buffer, |
| 498 | size_t count, loff_t *ppos) | |||
| 499 | { | |||
| 500 | struct ld_usb *dev; | |||
| 501 | size_t bytes_to_write; | |||
| 502 | int retval = 0; | |||
| 503 | ||||
| 504 | dev = file->private_data; | |||
| 505 | ||||
| 506 | /* verify that we actually have some data to write */ | |||
| 0 | 0 | - | 507 | if (count == 0) |
| 0 | - | 508 | goto exit; | |
| 509 | ||||
| 510 | /* lock this object */ | |||
| 0 | 0 | - | 511 | if (down_interruptible(&dev->sem)) { |
| 512 | retval = -ERESTARTSYS; | |||
| 0 | - | 513 | goto exit; | |