USB: wusbcore: correct spelling mistakes in comments and error string
[deliverable/linux.git] / drivers / usb / wusbcore / security.c
index dd88441c8f7891e0d242e1107d6ff585a30999e4..c322dca5ee4b890f59f540526b3c43970debe1a8 100644 (file)
 #include <linux/export.h>
 #include "wusbhc.h"
 
-static void wusbhc_set_gtk_callback(struct urb *urb);
-static void wusbhc_gtk_rekey_done_work(struct work_struct *work);
+static void wusbhc_gtk_rekey_work(struct work_struct *work);
 
 int wusbhc_sec_create(struct wusbhc *wusbhc)
 {
        wusbhc->gtk.descr.bLength = sizeof(wusbhc->gtk.descr) + sizeof(wusbhc->gtk.data);
        wusbhc->gtk.descr.bDescriptorType = USB_DT_KEY;
        wusbhc->gtk.descr.bReserved = 0;
+       wusbhc->gtk_index = 0;
 
-       wusbhc->gtk_index = wusb_key_index(0, WUSB_KEY_INDEX_TYPE_GTK,
-                                          WUSB_KEY_INDEX_ORIGINATOR_HOST);
-
-       INIT_WORK(&wusbhc->gtk_rekey_done_work, wusbhc_gtk_rekey_done_work);
+       INIT_WORK(&wusbhc->gtk_rekey_work, wusbhc_gtk_rekey_work);
 
        return 0;
 }
@@ -59,7 +56,7 @@ void wusbhc_sec_destroy(struct wusbhc *wusbhc)
  * @wusb_dev: the device whose PTK the TKID is for
  *            (or NULL for a TKID for a GTK)
  *
- * The generated TKID consist of two parts: the device's authenicated
+ * The generated TKID consists of two parts: the device's authenticated
  * address (or 0 or a GTK); and an incrementing number.  This ensures
  * that TKIDs cannot be shared between devices and by the time the
  * incrementing number wraps around the older TKIDs will no longer be
@@ -113,7 +110,7 @@ int wusbhc_sec_start(struct wusbhc *wusbhc)
        wusbhc_generate_gtk(wusbhc);
 
        result = wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid,
-                                &wusbhc->gtk.descr.bKeyData, key_size);
+                               &wusbhc->gtk.descr.bKeyData, key_size);
        if (result < 0)
                dev_err(wusbhc->dev, "cannot set GTK for the host: %d\n",
                        result);
@@ -129,7 +126,7 @@ int wusbhc_sec_start(struct wusbhc *wusbhc)
  */
 void wusbhc_sec_stop(struct wusbhc *wusbhc)
 {
-       cancel_work_sync(&wusbhc->gtk_rekey_done_work);
+       cancel_work_sync(&wusbhc->gtk_rekey_work);
 }
 
 
@@ -168,7 +165,7 @@ static int wusb_dev_set_encryption(struct usb_device *usb_dev, int value)
        result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
                        USB_REQ_SET_ENCRYPTION,
                        USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
-                       value, 0, NULL, 0, 1000 /* FIXME: arbitrary */);
+                       value, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
        if (result < 0)
                dev_err(dev, "Can't set device's WUSB encryption to "
                        "%s (value %d): %d\n",
@@ -185,14 +182,16 @@ static int wusb_dev_set_encryption(struct usb_device *usb_dev, int value)
 static int wusb_dev_set_gtk(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
 {
        struct usb_device *usb_dev = wusb_dev->usb_dev;
+       u8 key_index = wusb_key_index(wusbhc->gtk_index,
+               WUSB_KEY_INDEX_TYPE_GTK, WUSB_KEY_INDEX_ORIGINATOR_HOST);
 
        return usb_control_msg(
                usb_dev, usb_sndctrlpipe(usb_dev, 0),
                USB_REQ_SET_DESCRIPTOR,
                USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
-               USB_DT_KEY << 8 | wusbhc->gtk_index, 0,
+               USB_DT_KEY << 8 | key_index, 0,
                &wusbhc->gtk.descr, wusbhc->gtk.descr.bLength,
-               1000);
+               USB_CTRL_SET_TIMEOUT);
 }
 
 
@@ -302,8 +301,9 @@ int wusb_dev_update_address(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
 
        /* Set address 0 */
        result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
-                                USB_REQ_SET_ADDRESS, 0,
-                                0, 0, NULL, 0, 1000 /* FIXME: arbitrary */);
+                       USB_REQ_SET_ADDRESS,
+                       USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+                        0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
        if (result < 0) {
                dev_err(dev, "auth failed: can't set address 0: %d\n",
                        result);
@@ -317,9 +317,10 @@ int wusb_dev_update_address(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
 
        /* Set new (authenticated) address. */
        result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
-                                USB_REQ_SET_ADDRESS, 0,
-                                new_address, 0, NULL, 0,
-                                1000 /* FIXME: arbitrary */);
+                       USB_REQ_SET_ADDRESS,
+                       USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+                       new_address, 0, NULL, 0,
+                       USB_CTRL_SET_TIMEOUT);
        if (result < 0) {
                dev_err(dev, "auth failed: can't set address %u: %d\n",
                        new_address, result);
@@ -382,7 +383,7 @@ int wusb_dev_4way_handshake(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev,
                usb_dev, usb_sndctrlpipe(usb_dev, 0),
                USB_REQ_SET_HANDSHAKE,
                USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
-               1, 0, &hs[0], sizeof(hs[0]), 1000 /* FIXME: arbitrary */);
+               1, 0, &hs[0], sizeof(hs[0]), USB_CTRL_SET_TIMEOUT);
        if (result < 0) {
                dev_err(dev, "Handshake1: request failed: %d\n", result);
                goto error_hs1;
@@ -393,7 +394,7 @@ int wusb_dev_4way_handshake(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev,
                usb_dev, usb_rcvctrlpipe(usb_dev, 0),
                USB_REQ_GET_HANDSHAKE,
                USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
-               2, 0, &hs[1], sizeof(hs[1]), 1000 /* FIXME: arbitrary */);
+               2, 0, &hs[1], sizeof(hs[1]), USB_CTRL_GET_TIMEOUT);
        if (result < 0) {
                dev_err(dev, "Handshake2: request failed: %d\n", result);
                goto error_hs2;
@@ -470,7 +471,7 @@ int wusb_dev_4way_handshake(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev,
                usb_dev, usb_sndctrlpipe(usb_dev, 0),
                USB_REQ_SET_HANDSHAKE,
                USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
-               3, 0, &hs[2], sizeof(hs[2]), 1000 /* FIXME: arbitrary */);
+               3, 0, &hs[2], sizeof(hs[2]), USB_CTRL_SET_TIMEOUT);
        if (result < 0) {
                dev_err(dev, "Handshake3: request failed: %d\n", result);
                goto error_hs3;
@@ -520,24 +521,55 @@ error_kzalloc:
  * Once all connected and authenticated devices have received the new
  * GTK, switch the host to using it.
  */
-static void wusbhc_gtk_rekey_done_work(struct work_struct *work)
+static void wusbhc_gtk_rekey_work(struct work_struct *work)
 {
-       struct wusbhc *wusbhc = container_of(work, struct wusbhc, gtk_rekey_done_work);
+       struct wusbhc *wusbhc = container_of(work,
+                                       struct wusbhc, gtk_rekey_work);
        size_t key_size = sizeof(wusbhc->gtk.data);
+       int port_idx;
+       struct wusb_dev *wusb_dev, *wusb_dev_next;
+       LIST_HEAD(rekey_list);
 
        mutex_lock(&wusbhc->mutex);
+       /* generate the new key */
+       wusbhc_generate_gtk(wusbhc);
+       /* roll the gtk index. */
+       wusbhc->gtk_index = (wusbhc->gtk_index + 1) % (WUSB_KEY_INDEX_MAX + 1);
+       /*
+        * Save all connected devices on a list while holding wusbhc->mutex and
+        * take a reference to each one.  Then submit the set key request to
+        * them after releasing the lock in order to avoid a deadlock.
+        */
+       for (port_idx = 0; port_idx < wusbhc->ports_max; port_idx++) {
+               wusb_dev = wusbhc->port[port_idx].wusb_dev;
+               if (!wusb_dev || !wusb_dev->usb_dev
+                       || !wusb_dev->usb_dev->authenticated)
+                       continue;
 
-       if (--wusbhc->pending_set_gtks == 0)
-               wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid, &wusbhc->gtk.descr.bKeyData, key_size);
-
+               wusb_dev_get(wusb_dev);
+               list_add_tail(&wusb_dev->rekey_node, &rekey_list);
+       }
        mutex_unlock(&wusbhc->mutex);
-}
 
-static void wusbhc_set_gtk_callback(struct urb *urb)
-{
-       struct wusbhc *wusbhc = urb->context;
+       /* Submit the rekey requests without holding wusbhc->mutex. */
+       list_for_each_entry_safe(wusb_dev, wusb_dev_next, &rekey_list,
+               rekey_node) {
+               list_del_init(&wusb_dev->rekey_node);
+               dev_dbg(&wusb_dev->usb_dev->dev, "%s: rekey device at port %d\n",
+                       __func__, wusb_dev->port_idx);
 
-       queue_work(wusbd, &wusbhc->gtk_rekey_done_work);
+               if (wusb_dev_set_gtk(wusbhc, wusb_dev) < 0) {
+                       dev_err(&wusb_dev->usb_dev->dev, "%s: rekey device at port %d failed\n",
+                               __func__, wusb_dev->port_idx);
+               }
+               wusb_dev_put(wusb_dev);
+       }
+
+       /* Switch the host controller to use the new GTK. */
+       mutex_lock(&wusbhc->mutex);
+       wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid,
+               &wusbhc->gtk.descr.bKeyData, key_size);
+       mutex_unlock(&wusbhc->mutex);
 }
 
 /**
@@ -553,26 +585,12 @@ static void wusbhc_set_gtk_callback(struct urb *urb)
  */
 void wusbhc_gtk_rekey(struct wusbhc *wusbhc)
 {
-       static const size_t key_size = sizeof(wusbhc->gtk.data);
-       int p;
-
-       wusbhc_generate_gtk(wusbhc);
-
-       for (p = 0; p < wusbhc->ports_max; p++) {
-               struct wusb_dev *wusb_dev;
-
-               wusb_dev = wusbhc->port[p].wusb_dev;
-               if (!wusb_dev || !wusb_dev->usb_dev || !wusb_dev->usb_dev->authenticated)
-                       continue;
-
-               usb_fill_control_urb(wusb_dev->set_gtk_urb, wusb_dev->usb_dev,
-                                    usb_sndctrlpipe(wusb_dev->usb_dev, 0),
-                                    (void *)wusb_dev->set_gtk_req,
-                                    &wusbhc->gtk.descr, wusbhc->gtk.descr.bLength,
-                                    wusbhc_set_gtk_callback, wusbhc);
-               if (usb_submit_urb(wusb_dev->set_gtk_urb, GFP_KERNEL) == 0)
-                       wusbhc->pending_set_gtks++;
-       }
-       if (wusbhc->pending_set_gtks == 0)
-               wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid, &wusbhc->gtk.descr.bKeyData, key_size);
+       /*
+        * We need to submit a URB to the downstream WUSB devices in order to
+        * change the group key.  This can't be done while holding the
+        * wusbhc->mutex since that is also taken in the urb_enqueue routine
+        * and will cause a deadlock.  Instead, queue a work item to do
+        * it when the lock is not held
+        */
+       queue_work(wusbd, &wusbhc->gtk_rekey_work);
 }
This page took 0.028629 seconds and 5 git commands to generate.