[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[cdi-devel] [PATCH 3/5] usb: Implement interrupt transfers



Signed-off-by: Max Reitz <max@xxxxxxxxxx>
---
 usb/main.c |  2 ++
 usb/usb.c  | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 usb/usb.h  | 13 +++++++++
 3 files changed, 104 insertions(+)

diff --git a/usb/main.c b/usb/main.c
index 3add372..30d3716 100644
--- a/usb/main.c
+++ b/usb/main.c
@@ -56,6 +56,8 @@ static struct cdi_usb_driver usbd = {
     .get_endpoint_descriptor    = usb_get_endpoint_descriptor,
     .control_transfer           = usb_control_transfer,
     .bulk_transfer              = usb_bulk_transfer,
+    .interrupt_transfer         = usb_interrupt_transfer,
+    .destroy_async_transfer     = usb_destroy_async_transfer,
 };
 
 CDI_DRIVER(DRIVER_NAME, usbd)
diff --git a/usb/usb.c b/usb/usb.c
index 4e1dacc..87db9f9 100644
--- a/usb/usb.c
+++ b/usb/usb.c
@@ -769,6 +769,95 @@ cdi_usb_transmission_result_t usb_bulk_transfer(struct cdi_usb_device *dev,
 }
 
 
+static inline bool is_power_of_two(unsigned value)
+{
+    return !(value & (value - 1));
+}
+
+cdi_usb_async_transmission_t usb_interrupt_transfer(struct cdi_usb_device *dev,
+                                                    int ep, void *data,
+                                                    size_t size,
+                                                    cdi_usb_interrupt_cb_t cb,
+                                                    void *opaque)
+{
+    usb_logical_device_t *ldev = CDI_UPCAST(dev, usb_logical_device_t, cdi);
+
+    if (ep < 0 || ep >= dev->endpoint_count) {
+        return NULL;
+    }
+
+    int ep_idx = ldev->endpoints[ep];
+    usb_endpoint_t *ep_info = &ldev->dev->eps[ep_idx >> 4][ep_idx & 0xf];
+
+    if (ep_info->type != CDI_USB_INTERRUPT || !(ep_idx >> 4)) {
+        return NULL;
+    }
+
+    struct cdi_usb_hcd *hcd = CDI_UPCAST(ldev->dev->hc->dev.driver,
+                                         struct cdi_usb_hcd, drv);
+    cdi_usb_hc_transaction_t ta;
+
+    ta = hcd->create_transaction(ldev->dev->hc, &(struct cdi_usb_hc_ep_info){
+            .dev        = ldev->dev->id,
+            .ep         = ep_info->ep,
+            .ep_type    = ep_info->type,
+            .speed      = ldev->dev->speed,
+            .mps        = ep_info->mps,
+            .tt_addr    = ldev->dev->tt_addr,
+            .tt_port    = ldev->dev->tt_port
+        });
+
+    hcd->enqueue(ldev->dev->hc, &(struct cdi_usb_hc_transmission){
+            .ta     = ta,
+            .token  = CDI_USB_IN,
+            .buffer = data,
+            .size   = size,
+            .toggle = ep_info->toggle,
+        });
+
+    int interval = 0;
+    if (ldev->dev->speed == CDI_USB_LOW_SPEED ||
+        ldev->dev->speed == CDI_USB_FULL_SPEED)
+    {
+        interval = ep_info->desc->b_interval;
+        if (!interval) {
+            interval = 1;
+        } else if (!is_power_of_two(interval)) {
+            // Round up
+            interval = 1 << (sizeof(unsigned) * 8 - __builtin_clz(interval));
+        }
+    } else if (ldev->dev->speed == CDI_USB_HIGH_SPEED) {
+        if (ep_info->desc->b_interval < 16) {
+            interval = 1 << ep_info->desc->b_interval;
+        } else {
+            interval = 1 << 16;
+        }
+    } else {
+        // TODO
+        abort();
+    }
+
+    struct usb_async_transmission *at = malloc(sizeof(*at));
+    at->ta = ta;
+    at->hc = ldev->dev->hc;
+
+    hcd->start_interrupt_transaction(at->hc, ta, interval, cb, opaque);
+
+    return at;
+}
+
+
+void usb_destroy_async_transfer(cdi_usb_async_transmission_t cdi_at)
+{
+    struct usb_async_transmission *at = cdi_at;
+    struct cdi_usb_hcd *hcd = CDI_UPCAST(at->hc->dev.driver,
+                                         struct cdi_usb_hcd, drv);
+
+    hcd->destroy_transaction(at->hc, at->ta);
+    free(at);
+}
+
+
 static struct cdi_usb_hub_driver hub_drv;
 
 
diff --git a/usb/usb.h b/usb/usb.h
index 9f1f4ac..687fec8 100644
--- a/usb/usb.h
+++ b/usb/usb.h
@@ -71,6 +71,11 @@ struct usb_logical_device {
     int *endpoints;
 };
 
+struct usb_async_transmission {
+    cdi_usb_hc_transaction_t ta;
+    struct cdi_usb_hc *hc;
+};
+
 
 struct cdi_device *usb_init_hc(struct cdi_bus_data *bus_data);
 
@@ -85,4 +90,12 @@ cdi_usb_transmission_result_t usb_bulk_transfer(struct cdi_usb_device *dev,
                                                 int ep, void *data,
                                                 size_t size);
 
+cdi_usb_async_transmission_t usb_interrupt_transfer(struct cdi_usb_device *dev,
+                                                    int ep, void *data,
+                                                    size_t size,
+                                                    cdi_usb_interrupt_cb_t cb,
+                                                    void *opaque);
+
+void usb_destroy_async_transfer(cdi_usb_async_transmission_t at);
+
 #endif
-- 
2.6.4