/** * usb_alphatrack_disconnect * * Called by the usb core when the device is removed from the system. */ static void usb_alphatrack_disconnect(struct usb_interface *intf) { struct usb_alphatrack *dev; int minor; mutex_lock(&disconnect_mutex); dev = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL); down(&dev->sem); minor = intf->minor; /* give back our minor */ usb_deregister_dev(intf, &usb_alphatrack_class); /* if the device is not opened, then we clean up right now */ if (!dev->open_count) { up(&dev->sem); usb_alphatrack_delete(dev); } else { dev->intf = NULL; up(&dev->sem); } atomic_set(&dev->writes_pending, 0); mutex_unlock(&disconnect_mutex); dev_info(&intf->dev, "Alphatrack Surface #%d now disconnected\n", (minor - USB_ALPHATRACK_MINOR_BASE)); }
/** * usb_alphatrack_release */ static int usb_alphatrack_release(struct inode *inode, struct file *file) { struct usb_alphatrack *dev; int retval = 0; dev = file->private_data; if (dev == NULL) { retval = -ENODEV; goto exit; } if (down_interruptible(&dev->sem)) { retval = -ERESTARTSYS; goto exit; } if (dev->open_count != 1) { retval = -ENODEV; goto unlock_exit; } if (dev->intf == NULL) { /* the device was unplugged before the file was released */ up(&dev->sem); /* unlock here as usb_alphatrack_delete frees dev */ usb_alphatrack_delete(dev); retval = -ENODEV; goto exit; } /* wait until write transfer is finished */ if (dev->interrupt_out_busy) wait_event_interruptible_timeout(dev->write_wait, !dev->interrupt_out_busy, 2 * HZ); usb_alphatrack_abort_transfers(dev); dev->open_count = 0; unlock_exit: up(&dev->sem); exit: return retval; }
static int usb_alphatrack_release(struct inode *inode, struct file *file) { struct usb_alphatrack *dev; int retval = 0; dev = file->private_data; if (dev == NULL) { retval = -ENODEV; goto exit; } if (down_interruptible(&dev->sem)) { retval = -ERESTARTSYS; goto exit; } if (dev->open_count != 1) { retval = -ENODEV; goto unlock_exit; } if (dev->intf == NULL) { up(&dev->sem); usb_alphatrack_delete(dev); retval = -ENODEV; goto exit; } if (dev->interrupt_out_busy) wait_event_interruptible_timeout(dev->write_wait, !dev->interrupt_out_busy, 2 * HZ); usb_alphatrack_abort_transfers(dev); dev->open_count = 0; unlock_exit: up(&dev->sem); exit: return retval; }
/** * usb_alphatrack_probe * * Called by the usb core when a new device is connected that it thinks * this driver might be interested in. */ static int usb_alphatrack_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(intf); struct usb_alphatrack *dev = NULL; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; int i; int true_size; int retval = -ENOMEM; /* allocate memory for our device state and intialize it */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { dev_err(&intf->dev, "Out of memory\n"); goto exit; } init_MUTEX(&dev->sem); dev->intf = intf; init_waitqueue_head(&dev->read_wait); init_waitqueue_head(&dev->write_wait); iface_desc = intf->cur_altsetting; /* set up the endpoint information */ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; if (usb_endpoint_is_int_in(endpoint)) dev->interrupt_in_endpoint = endpoint; if (usb_endpoint_is_int_out(endpoint)) dev->interrupt_out_endpoint = endpoint; } if (dev->interrupt_in_endpoint == NULL) { dev_err(&intf->dev, "Interrupt in endpoint not found\n"); goto error; } if (dev->interrupt_out_endpoint == NULL) dev_warn(&intf->dev, "Interrupt out endpoint not found" "(using control endpoint instead)\n"); dev->interrupt_in_endpoint_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize); if (dev->interrupt_in_endpoint_size != 64) dev_warn(&intf->dev, "Interrupt in endpoint size is not 64!\n"); if (ring_buffer_size == 0) ring_buffer_size = RING_BUFFER_SIZE; true_size = min(ring_buffer_size, RING_BUFFER_SIZE); /* FIXME - there are more usb_alloc routines for dma correctness. Needed? */ dev->ring_buffer = kmalloc((true_size * sizeof(struct alphatrack_icmd)), GFP_KERNEL); if (!dev->ring_buffer) { dev_err(&intf->dev, "Couldn't allocate input ring_buffer of size %d\n", true_size); goto error; } dev->interrupt_in_buffer = kmalloc(dev->interrupt_in_endpoint_size, GFP_KERNEL); if (!dev->interrupt_in_buffer) { dev_err(&intf->dev, "Couldn't allocate interrupt_in_buffer\n"); goto error; } dev->oldi_buffer = kmalloc(dev->interrupt_in_endpoint_size, GFP_KERNEL); if (!dev->oldi_buffer) { dev_err(&intf->dev, "Couldn't allocate old buffer\n"); goto error; } dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->interrupt_in_urb) { dev_err(&intf->dev, "Couldn't allocate interrupt_in_urb\n"); goto error; } dev->interrupt_out_endpoint_size = dev->interrupt_out_endpoint ? le16_to_cpu(dev-> interrupt_out_endpoint-> wMaxPacketSize) : udev-> descriptor.bMaxPacketSize0; if (dev->interrupt_out_endpoint_size != 64) dev_warn(&intf->dev, "Interrupt out endpoint size is not 64!)\n"); if (write_buffer_size == 0) write_buffer_size = WRITE_BUFFER_SIZE; true_size = min(write_buffer_size, WRITE_BUFFER_SIZE); dev->interrupt_out_buffer = kmalloc(true_size * dev->interrupt_out_endpoint_size, GFP_KERNEL); if (!dev->interrupt_out_buffer) { dev_err(&intf->dev, "Couldn't allocate interrupt_out_buffer\n"); goto error; } dev->write_buffer = kmalloc(sizeof(struct alphatrack_ocmd) * true_size, GFP_KERNEL); if (!dev->write_buffer) { dev_err(&intf->dev, "Couldn't allocate write_buffer \n"); goto error; } dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->interrupt_out_urb) { dev_err(&intf->dev, "Couldn't allocate interrupt_out_urb\n"); goto error; } dev->interrupt_in_interval = min_interrupt_in_interval > dev->interrupt_in_endpoint-> bInterval ? min_interrupt_in_interval : dev->interrupt_in_endpoint-> bInterval; if (dev->interrupt_out_endpoint) dev->interrupt_out_interval = min_interrupt_out_interval > dev->interrupt_out_endpoint-> bInterval ? min_interrupt_out_interval : dev-> interrupt_out_endpoint->bInterval; /* we can register the device now, as it is ready */ usb_set_intfdata(intf, dev); atomic_set(&dev->writes_pending, 0); retval = usb_register_dev(intf, &usb_alphatrack_class); if (retval) { /* something prevented us from registering this driver */ dev_err(&intf->dev, "Not able to get a minor for this device.\n"); usb_set_intfdata(intf, NULL); goto error; } /* let the user know what node this device is now attached to */ dev_info(&intf->dev, "Alphatrack Device #%d now attached to major %d minor %d\n", (intf->minor - USB_ALPHATRACK_MINOR_BASE), USB_MAJOR, intf->minor); exit: return retval; error: usb_alphatrack_delete(dev); return retval; }