]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Use background transfers for usb_keyboard
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sat, 21 Aug 2010 15:12:51 +0000 (17:12 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sat, 21 Aug 2010 15:12:51 +0000 (17:12 +0200)
bus/usb/usbtrans.c
include/grub/usb.h
include/grub/usbtrans.h
term/usb_keyboard.c

index 27377a3a5b1dc761019f955d90286f27fbd828cf..5d6966ecc59ed7df4585fcd467f6bf9a75dd62e0 100644 (file)
@@ -195,29 +195,27 @@ grub_usb_control_msg (grub_usb_device_t dev,
   return err;
 }
 
-static grub_usb_err_t
-grub_usb_bulk_readwrite (grub_usb_device_t dev,
-                        int endpoint, grub_size_t size0, char *data_in,
-                        grub_transfer_type_t type, int timeout,
-                        grub_size_t *actual)
+static grub_usb_transfer_t
+grub_usb_bulk_setup_readwrite (grub_usb_device_t dev,
+                              int endpoint, grub_size_t size0, char *data_in,
+                              grub_transfer_type_t type)
 {
   int i;
   grub_usb_transfer_t transfer;
   int datablocks;
   unsigned int max;
-  grub_usb_err_t err;
-  int toggle = dev->toggle[endpoint];
   volatile char *data;
   grub_uint32_t data_addr;
   struct grub_pci_dma_chunk *data_chunk;
   grub_size_t size = size0;
+  int toggle = dev->toggle[endpoint];
 
   grub_dprintf ("usb", "bulk: size=0x%02x type=%d\n", size, type);
 
   /* FIXME: avoid allocation any kind of buffer in a first place.  */
   data_chunk = grub_memalign_dma32 (128, size);
   if (!data_chunk)
-    return GRUB_USB_ERR_INTERNAL;
+    return NULL;
   data = grub_dma_get_virt (data_chunk);
   data_addr = grub_dma_get_phys (data_chunk);
   if (type == GRUB_USB_TRANSFER_TYPE_OUT)
@@ -242,7 +240,7 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev,
   if (! transfer)
     {
       grub_dma_free (data_chunk);
-      return GRUB_USB_ERR_INTERNAL;
+      return NULL;
     }
 
   datablocks = ((size + max - 1) / max);
@@ -251,9 +249,12 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev,
   transfer->endpoint = endpoint & 15;
   transfer->devaddr = dev->addr;
   transfer->type = GRUB_USB_TRANSACTION_TYPE_BULK;
+  transfer->dir = type;
   transfer->max = max;
   transfer->dev = dev;
   transfer->last_trans = -1; /* Reset index of last processed transaction (TD) */
+  transfer->data_chunk = data_chunk;
+  transfer->data = data_in;
 
   /* Allocate an array of transfer data structures.  */
   transfer->transactions = grub_malloc (transfer->transcnt
@@ -262,7 +263,7 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev,
     {
       grub_free (transfer);
       grub_dma_free (data_chunk);
-      return GRUB_USB_ERR_INTERNAL;
+      return NULL;
     }
 
   /* Set up all transfers.  */
@@ -280,24 +281,51 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev,
       tr->preceding = i * max;
       size -= tr->size;
     }
+  return transfer;
+}
+
+static void
+grub_usb_bulk_finish_readwrite (grub_usb_transfer_t transfer)
+{
+  grub_usb_device_t dev = transfer->dev;
+  int toggle = dev->toggle[transfer->endpoint];
 
-  err = grub_usb_execute_and_wait_transfer (dev, transfer, timeout, actual);
   /* We must remember proper toggle value even if some transactions
    * were not processed - correct value should be inversion of last
    * processed transaction (TD). */
   if (transfer->last_trans >= 0)
     toggle = transfer->transactions[transfer->last_trans].toggle ? 0 : 1;
   else
-    toggle = dev->toggle[endpoint]; /* Nothing done, take original */
-  grub_dprintf ("usb", "bulk: err=%d, toggle=%d\n", err, toggle);
-  dev->toggle[endpoint] = toggle;
+    toggle = dev->toggle[transfer->endpoint]; /* Nothing done, take original */
+  grub_dprintf ("usb", "bulk: toggle=%d\n", toggle);
+  dev->toggle[transfer->endpoint] = toggle;
+
+  if (transfer->dir == GRUB_USB_TRANSFER_TYPE_IN)
+    grub_memcpy (transfer->data, (void *)
+                grub_dma_get_virt (transfer->data_chunk),
+                transfer->size + 1);
 
   grub_free (transfer->transactions);
   grub_free (transfer);
-  grub_dma_free (data_chunk);
+  grub_dma_free (transfer->data_chunk);
+}
+
+static grub_usb_err_t
+grub_usb_bulk_readwrite (grub_usb_device_t dev,
+                        int endpoint, grub_size_t size0, char *data_in,
+                        grub_transfer_type_t type, int timeout,
+                        grub_size_t *actual)
+{
+  grub_usb_err_t err;
+  grub_usb_transfer_t transfer;
+
+  transfer = grub_usb_bulk_setup_readwrite (dev, endpoint, size0,
+                                           data_in, type);
+  if (!transfer)
+    return GRUB_USB_ERR_INTERNAL;
+  err = grub_usb_execute_and_wait_transfer (dev, transfer, timeout, actual);
 
-  if (type == GRUB_USB_TRANSFER_TYPE_IN)
-    grub_memcpy (data_in, (char *) data, size0);
+  grub_usb_bulk_finish_readwrite (transfer);
 
   return err;
 }
@@ -329,6 +357,41 @@ grub_usb_bulk_read (grub_usb_device_t dev,
   return err;
 }
 
+grub_usb_err_t
+grub_usb_check_transfer (grub_usb_transfer_t transfer, grub_size_t *actual)
+{
+  grub_usb_err_t err;
+  grub_usb_device_t dev = transfer->dev;
+
+  err = dev->controller.dev->check_transfer (&dev->controller, transfer,
+                                            actual);
+  if (err == GRUB_USB_ERR_WAIT)
+    return err;
+
+  grub_usb_bulk_finish_readwrite (transfer);
+
+  return err;
+}
+
+grub_usb_transfer_t
+grub_usb_bulk_read_background (grub_usb_device_t dev,
+                              int endpoint, grub_size_t size, void *data)
+{
+  grub_usb_err_t err;
+  grub_usb_transfer_t transfer;
+
+  transfer = grub_usb_bulk_setup_readwrite (dev, endpoint, size,
+                                           data, GRUB_USB_TRANSFER_TYPE_IN);
+  if (!transfer)
+    return NULL;
+
+  err = dev->controller.dev->setup_transfer (&dev->controller, transfer);
+  if (err)
+    return NULL;
+
+  return transfer;
+}
+
 grub_usb_err_t
 grub_usb_bulk_read_extended (grub_usb_device_t dev,
                             int endpoint, grub_size_t size, char *data,
index e7d119646719d3ec43131f8ea354cc4c3a100c9e..768ec2f66ea28c0b8bd861fda6f2664b7fcdfae5 100644 (file)
@@ -274,5 +274,10 @@ grub_usb_err_t
 grub_usb_bulk_read_extended (grub_usb_device_t dev,
                             int endpoint, grub_size_t size, char *data,
                             int timeout, grub_size_t *actual);
+grub_usb_transfer_t
+grub_usb_bulk_read_background (grub_usb_device_t dev,
+                             int endpoint, grub_size_t size, void *data);
+grub_usb_err_t
+grub_usb_check_transfer (grub_usb_transfer_t trans, grub_size_t *actual);
 
 #endif /* GRUB_USB_H */
index 486e83f4098fba78541b64c5bda4a73a3c45211b..a8aca3119550c58f41442d364cd12d717be67446 100644 (file)
@@ -56,6 +56,8 @@ struct grub_usb_transfer
 
   grub_transaction_type_t type;
 
+  grub_transfer_type_t dir;
+
   struct grub_usb_device *dev;
 
   struct grub_usb_transaction *transactions;
@@ -64,6 +66,10 @@ struct grub_usb_transfer
   /* Index of last processed transaction in OHCI/UHCI driver. */
 
   void *controller_data;
+
+  /* Used when finishing transfer to copy data back.  */
+  struct grub_pci_dma_chunk *data_chunk;
+  void *data;
 };
 typedef struct grub_usb_transfer *grub_usb_transfer_t;
 
index adb84fa9477c45a860da8e2eb5a3d6bf74272743..1a486b53d500c623f5339d2103d604b05df19a16 100644 (file)
@@ -84,6 +84,9 @@ struct grub_usb_keyboard_data
   grub_uint8_t status;
   int key;
   struct grub_usb_desc_endp *endp;
+  grub_usb_transfer_t transfer;
+  grub_uint8_t report[8];
+  int dead;
 };
 
 static struct grub_term_input grub_usb_keyboards[16];
@@ -214,8 +217,20 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno)
   data->key = -1;
 #endif
 
+  data->transfer = grub_usb_bulk_read_background (usbdev,
+                                                 data->endp->endp_addr,
+                                                 sizeof (data->report),
+                                                 (char *) data->report);
+  if (!data->transfer)
+    {
+      grub_print_error ();
+      return 0;
+    }
+
   grub_term_register_input_active ("usb_keyboard", &grub_usb_keyboards[curnum]);
 
+  data->dead = 0;
+
   return 1;
 }
 
@@ -224,19 +239,35 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno)
 static int
 grub_usb_keyboard_checkkey (struct grub_term_input *term)
 {
-  grub_uint8_t data[8];
   grub_usb_err_t err;
   struct grub_usb_keyboard_data *termdata = term->data;
+  grub_uint8_t data[sizeof (termdata->report)];
   grub_size_t actual;
 
   if (termdata->key != -1)
     return termdata->key;
 
-  data[2] = 0;
+  if (termdata->dead)
+    return -1;
+
   /* Poll interrupt pipe.  */
-  err = grub_usb_bulk_read_extended (termdata->usbdev,
-                                    termdata->endp->endp_addr, sizeof (data),
-                                    (char *) data, 10, &actual);
+  err = grub_usb_check_transfer (termdata->transfer, &actual);
+
+  if (err == GRUB_USB_ERR_WAIT)
+    return -1;
+
+  grub_memcpy (data, termdata->report, sizeof (data));
+
+  termdata->transfer = grub_usb_bulk_read_background (termdata->usbdev,
+                                                     termdata->endp->endp_addr,
+                                                     sizeof (termdata->report),
+                                                     (char *) termdata->report);
+  if (!termdata->transfer)
+    {
+      grub_printf ("%s failed. Stopped\n", term->name);
+      termdata->dead = 1;
+    }
+
   if (err || actual < 1)
     return -1;