usb: ehci: Properly deal with data toggle for interrupt endpoints
authorHans de Goede <hdegoede@redhat.com>
Thu, 18 Jun 2015 20:34:33 +0000 (22:34 +0200)
committerMarek Vasut <marex@denx.de>
Fri, 19 Jun 2015 12:33:28 +0000 (14:33 +0200)
Without this we loose every other interrupt packet. We never noticed this
because with keyboards the packets which we were loosing would normally
be key release packets.

But now that we do keyrepeat in software instead of relying on the hid
idle functionality, missing a release will result in key repeat triggering.

This commit fixes this.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
drivers/usb/host/ehci-hcd.c

index 1e5a6e2b20c6cdced927c3dc0e2f5fc5d2da0610..bf02221c9f0f5308399acacd1385c60a102ce825 100644 (file)
@@ -1214,6 +1214,7 @@ static int _ehci_submit_control_msg(struct usb_device *dev, unsigned long pipe,
 
 struct int_queue {
        int elementsize;
+       unsigned long pipe;
        struct QH *first;
        struct QH *current;
        struct QH *last;
@@ -1269,7 +1270,7 @@ static struct int_queue *_ehci_create_int_queue(struct usb_device *dev,
 {
        struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
        struct int_queue *result = NULL;
-       int i;
+       uint32_t i, toggle;
 
        /*
         * Interrupt transfers requiring several transactions are not supported
@@ -1309,6 +1310,7 @@ static struct int_queue *_ehci_create_int_queue(struct usb_device *dev,
                goto fail1;
        }
        result->elementsize = elementsize;
+       result->pipe = pipe;
        result->first = memalign(USB_DMA_MINALIGN,
                                 sizeof(struct QH) * queuesize);
        if (!result->first) {
@@ -1326,6 +1328,8 @@ static struct int_queue *_ehci_create_int_queue(struct usb_device *dev,
        memset(result->first, 0, sizeof(struct QH) * queuesize);
        memset(result->tds, 0, sizeof(struct qTD) * queuesize);
 
+       toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
+
        for (i = 0; i < queuesize; i++) {
                struct QH *qh = result->first + i;
                struct qTD *td = result->tds + i;
@@ -1357,7 +1361,9 @@ static struct int_queue *_ehci_create_int_queue(struct usb_device *dev,
                td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
                debug("communication direction is '%s'\n",
                      usb_pipein(pipe) ? "in" : "out");
-               td->qt_token = cpu_to_hc32((elementsize << 16) |
+               td->qt_token = cpu_to_hc32(
+                       QT_TOKEN_DT(toggle) |
+                       (elementsize << 16) |
                        ((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */
                        0x80); /* active */
                td->qt_buffer[0] =
@@ -1372,6 +1378,7 @@ static struct int_queue *_ehci_create_int_queue(struct usb_device *dev,
                    cpu_to_hc32((td->qt_buffer[0] + 0x4000) & ~0xfff);
 
                *buf = buffer + i * elementsize;
+               toggle ^= 1;
        }
 
        flush_dcache_range((unsigned long)buffer,
@@ -1426,6 +1433,8 @@ static void *_ehci_poll_int_queue(struct usb_device *dev,
 {
        struct QH *cur = queue->current;
        struct qTD *cur_td;
+       uint32_t token, toggle;
+       unsigned long pipe = queue->pipe;
 
        /* depleted queue */
        if (cur == NULL) {
@@ -1436,12 +1445,15 @@ static void *_ehci_poll_int_queue(struct usb_device *dev,
        cur_td = &queue->tds[queue->current - queue->first];
        invalidate_dcache_range((unsigned long)cur_td,
                                ALIGN_END_ADDR(struct qTD, cur_td, 1));
-       if (QT_TOKEN_GET_STATUS(hc32_to_cpu(cur_td->qt_token)) &
-                       QT_TOKEN_STATUS_ACTIVE) {
-               debug("Exit poll_int_queue with no completed intr transfer. token is %x\n",
-                     hc32_to_cpu(cur_td->qt_token));
+       token = hc32_to_cpu(cur_td->qt_token);
+       if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE) {
+               debug("Exit poll_int_queue with no completed intr transfer. token is %x\n", token);
                return NULL;
        }
+
+       toggle = QT_TOKEN_GET_DT(token);
+       usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), toggle);
+
        if (!(cur->qh_link & QH_LINK_TERMINATE))
                queue->current++;
        else
@@ -1452,7 +1464,7 @@ static void *_ehci_poll_int_queue(struct usb_device *dev,
                                               queue->elementsize));
 
        debug("Exit poll_int_queue with completed intr transfer. token is %x at %p (first at %p)\n",
-             hc32_to_cpu(cur_td->qt_token), cur, queue->first);
+             token, cur, queue->first);
        return cur->buffer;
 }