[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