summaryrefslogtreecommitdiffstats
path: root/drivers/usb/misc/chaoskey.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-03-17 22:24:26 +0100
committerLinus Torvalds <torvalds@linux-foundation.org>2016-03-17 22:24:26 +0100
commit48d10bda1f2c69980601a61194015bb0790fb7ab (patch)
treee4ea2021560b1f18b335f6e8e20761fb9514cd1b /drivers/usb/misc/chaoskey.c
parentMerge tag 'tty-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/greg... (diff)
parentMerge tag 'usb-serial-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/g... (diff)
downloadlinux-48d10bda1f2c69980601a61194015bb0790fb7ab.tar.xz
linux-48d10bda1f2c69980601a61194015bb0790fb7ab.zip
Merge tag 'usb-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB updates from Greg KH: "Here is the big USB patchset for 4.6-rc1. The normal mess is here, gadget and xhci fixes and updates, and lots of other driver updates and cleanups as well. Full details are in the shortlog. All have been in linux-next for a while with no reported issues" * tag 'usb-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (266 commits) USB: core: let USB device know device node usb: devio: Add ioctl to disallow detaching kernel USB drivers. usb: gadget: f_acm: Fix configfs attr name usb: udc: lpc32xx: remove USB PLL and USB OTG clock management usb: udc: lpc32xx: remove direct access to clock controller registers usb: udc: lpc32xx: switch to clock prepare/unprepare model usb: renesas_usbhs: gadget: fix giveback status code in usbhsg_pipe_disable() usb: gadget: renesas_usb3: Use ARCH_RENESAS usb: dwc2: Fix issues in dwc2_complete_non_isoc_xfer_ddma() usb: dwc2: Add support for Lantiq ARX and XRX SoCs usb: phy: generic: Handle late registration of gadget usb: gadget: bdc_udc: fix race condition in bdc_udc_exit() usb: musb: core: added missing const qualifier to musb_hdrc_platform_data::config usb: dwc2: Move host-specific core functions into hcd.c usb: dwc2: Move register save and restore functions usb: dwc2: Use kmem_cache_free() usb: dwc2: host: If using uframe scheduler, end splits better usb: dwc2: host: Totally redo the microframe scheduler usb: dwc2: host: Properly set even/odd frame usb: dwc2: host: Add dwc2_hcd_get_future_frame_number() call ...
Diffstat (limited to 'drivers/usb/misc/chaoskey.c')
-rw-r--r--drivers/usb/misc/chaoskey.c122
1 files changed, 83 insertions, 39 deletions
diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c
index 23c794813e6a..76350e4ee807 100644
--- a/drivers/usb/misc/chaoskey.c
+++ b/drivers/usb/misc/chaoskey.c
@@ -73,6 +73,8 @@ static const struct usb_device_id chaoskey_table[] = {
};
MODULE_DEVICE_TABLE(usb, chaoskey_table);
+static void chaos_read_callback(struct urb *urb);
+
/* Driver-local specific stuff */
struct chaoskey {
struct usb_interface *interface;
@@ -80,7 +82,8 @@ struct chaoskey {
struct mutex lock;
struct mutex rng_lock;
int open; /* open count */
- int present; /* device not disconnected */
+ bool present; /* device not disconnected */
+ bool reading; /* ongoing IO */
int size; /* size of buf */
int valid; /* bytes of buf read */
int used; /* bytes of buf consumed */
@@ -88,15 +91,19 @@ struct chaoskey {
struct hwrng hwrng; /* Embedded struct for hwrng */
int hwrng_registered; /* registered with hwrng API */
wait_queue_head_t wait_q; /* for timeouts */
+ struct urb *urb; /* for performing IO */
char *buf;
};
static void chaoskey_free(struct chaoskey *dev)
{
- usb_dbg(dev->interface, "free");
- kfree(dev->name);
- kfree(dev->buf);
- kfree(dev);
+ if (dev) {
+ usb_dbg(dev->interface, "free");
+ usb_free_urb(dev->urb);
+ kfree(dev->name);
+ kfree(dev->buf);
+ kfree(dev);
+ }
}
static int chaoskey_probe(struct usb_interface *interface,
@@ -107,7 +114,7 @@ static int chaoskey_probe(struct usb_interface *interface,
int i;
int in_ep = -1;
struct chaoskey *dev;
- int result;
+ int result = -ENOMEM;
int size;
usb_dbg(interface, "probe %s-%s", udev->product, udev->serial);
@@ -142,14 +149,25 @@ static int chaoskey_probe(struct usb_interface *interface,
dev = kzalloc(sizeof(struct chaoskey), GFP_KERNEL);
if (dev == NULL)
- return -ENOMEM;
+ goto out;
dev->buf = kmalloc(size, GFP_KERNEL);
- if (dev->buf == NULL) {
- kfree(dev);
- return -ENOMEM;
- }
+ if (dev->buf == NULL)
+ goto out;
+
+ dev->urb = usb_alloc_urb(0, GFP_KERNEL);
+
+ if (!dev->urb)
+ goto out;
+
+ usb_fill_bulk_urb(dev->urb,
+ udev,
+ usb_rcvbulkpipe(udev, in_ep),
+ dev->buf,
+ size,
+ chaos_read_callback,
+ dev);
/* Construct a name using the product and serial values. Each
* device needs a unique name for the hwrng code
@@ -158,11 +176,8 @@ static int chaoskey_probe(struct usb_interface *interface,
if (udev->product && udev->serial) {
dev->name = kmalloc(strlen(udev->product) + 1 +
strlen(udev->serial) + 1, GFP_KERNEL);
- if (dev->name == NULL) {
- kfree(dev->buf);
- kfree(dev);
- return -ENOMEM;
- }
+ if (dev->name == NULL)
+ goto out;
strcpy(dev->name, udev->product);
strcat(dev->name, "-");
@@ -186,9 +201,7 @@ static int chaoskey_probe(struct usb_interface *interface,
result = usb_register_dev(interface, &chaoskey_class);
if (result) {
usb_err(interface, "Unable to allocate minor number.");
- usb_set_intfdata(interface, NULL);
- chaoskey_free(dev);
- return result;
+ goto out;
}
dev->hwrng.name = dev->name ? dev->name : chaoskey_driver.name;
@@ -215,6 +228,11 @@ static int chaoskey_probe(struct usb_interface *interface,
usb_dbg(interface, "chaoskey probe success, size %d", dev->size);
return 0;
+
+out:
+ usb_set_intfdata(interface, NULL);
+ chaoskey_free(dev);
+ return result;
}
static void chaoskey_disconnect(struct usb_interface *interface)
@@ -237,6 +255,7 @@ static void chaoskey_disconnect(struct usb_interface *interface)
mutex_lock(&dev->lock);
dev->present = 0;
+ usb_poison_urb(dev->urb);
if (!dev->open) {
mutex_unlock(&dev->lock);
@@ -311,14 +330,33 @@ static int chaoskey_release(struct inode *inode, struct file *file)
return 0;
}
+static void chaos_read_callback(struct urb *urb)
+{
+ struct chaoskey *dev = urb->context;
+ int status = urb->status;
+
+ usb_dbg(dev->interface, "callback status (%d)", status);
+
+ if (status == 0)
+ dev->valid = urb->actual_length;
+ else
+ dev->valid = 0;
+
+ dev->used = 0;
+
+ /* must be seen first before validity is announced */
+ smp_wmb();
+
+ dev->reading = false;
+ wake_up(&dev->wait_q);
+}
+
/* Fill the buffer. Called with dev->lock held
*/
static int _chaoskey_fill(struct chaoskey *dev)
{
DEFINE_WAIT(wait);
int result;
- int this_read;
- struct usb_device *udev = interface_to_usbdev(dev->interface);
usb_dbg(dev->interface, "fill");
@@ -343,21 +381,31 @@ static int _chaoskey_fill(struct chaoskey *dev)
return result;
}
- result = usb_bulk_msg(udev,
- usb_rcvbulkpipe(udev, dev->in_ep),
- dev->buf, dev->size, &this_read,
- NAK_TIMEOUT);
+ dev->reading = true;
+ result = usb_submit_urb(dev->urb, GFP_KERNEL);
+ if (result < 0) {
+ result = usb_translate_errors(result);
+ dev->reading = false;
+ goto out;
+ }
+
+ result = wait_event_interruptible_timeout(
+ dev->wait_q,
+ !dev->reading,
+ NAK_TIMEOUT);
+
+ if (result < 0)
+ goto out;
+ if (result == 0)
+ result = -ETIMEDOUT;
+ else
+ result = dev->valid;
+out:
/* Let the device go back to sleep eventually */
usb_autopm_put_interface(dev->interface);
- if (result == 0) {
- dev->valid = this_read;
- dev->used = 0;
- }
-
- usb_dbg(dev->interface, "bulk_msg result %d this_read %d",
- result, this_read);
+ usb_dbg(dev->interface, "read %d bytes", dev->valid);
return result;
}
@@ -395,13 +443,7 @@ static ssize_t chaoskey_read(struct file *file,
goto bail;
if (dev->valid == dev->used) {
result = _chaoskey_fill(dev);
- if (result) {
- mutex_unlock(&dev->lock);
- goto bail;
- }
-
- /* Read returned zero bytes */
- if (dev->used == dev->valid) {
+ if (result < 0) {
mutex_unlock(&dev->lock);
goto bail;
}
@@ -435,6 +477,8 @@ bail:
return read_count;
}
usb_dbg(dev->interface, "empty read, result %d", result);
+ if (result == -ETIMEDOUT)
+ result = -EAGAIN;
return result;
}