diff options
Diffstat (limited to 'hw/usb/hcd-ehci.c')
| -rw-r--r-- | hw/usb/hcd-ehci.c | 317 |
1 files changed, 176 insertions, 141 deletions
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index d9dc576e7c..7df8e21ecb 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -29,9 +29,6 @@ #include "hw/usb/hcd-ehci.h" -/* internal processing - reset HC to try and recover */ -#define USB_RET_PROCERR (-99) - /* Capability Registers Base Address - section 2.2 */ #define CAPLENGTH 0x0000 /* 1-byte, 0x0001 reserved */ #define HCIVERSION 0x0002 /* 2-bytes, i/f version # */ @@ -192,6 +189,7 @@ static const char *ehci_mmio_names[] = { static int ehci_state_executing(EHCIQueue *q); static int ehci_state_writeback(EHCIQueue *q); +static int ehci_state_advqueue(EHCIQueue *q); static int ehci_fill_queue(EHCIPacket *p); static const char *nr2str(const char **n, size_t len, uint32_t nr) @@ -456,12 +454,16 @@ static EHCIPacket *ehci_alloc_packet(EHCIQueue *q) static void ehci_free_packet(EHCIPacket *p) { if (p->async == EHCI_ASYNC_FINISHED) { - int state = ehci_get_state(p->queue->ehci, p->queue->async); + EHCIQueue *q = p->queue; + int state = ehci_get_state(q->ehci, q->async); /* This is a normal, but rare condition (cancel racing completion) */ fprintf(stderr, "EHCI: Warning packet completed but not processed\n"); - ehci_state_executing(p->queue); - ehci_state_writeback(p->queue); - ehci_set_state(p->queue->ehci, p->queue->async, state); + ehci_state_executing(q); + ehci_state_writeback(q); + if (!(q->qh.token & QTD_TOKEN_HALT)) { + ehci_state_advqueue(q); + } + ehci_set_state(q->ehci, q->async, state); /* state_writeback recurses into us with async == EHCI_ASYNC_NONE!! */ return; } @@ -962,6 +964,9 @@ static void ehci_opreg_write(void *ptr, hwaddr addr, case USBINTR: val &= USBINTR_MASK; + if (ehci_enabled(s) && (USBSTS_FLR & val)) { + qemu_bh_schedule(s->async_bh); + } break; case FRINDEX: @@ -998,21 +1003,25 @@ static void ehci_opreg_write(void *ptr, hwaddr addr, *mmio, old); } - -// TODO : Put in common header file, duplication from usb-ohci.c - /* Get an array of dwords from main memory */ static inline int get_dwords(EHCIState *ehci, uint32_t addr, uint32_t *buf, int num) { int i; + if (!ehci->dma) { + ehci_raise_irq(ehci, USBSTS_HSE); + ehci->usbcmd &= ~USBCMD_RUNSTOP; + trace_usb_ehci_dma_error(); + return -1; + } + for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { dma_memory_read(ehci->dma, addr, buf, sizeof(*buf)); *buf = le32_to_cpu(*buf); } - return 1; + return num; } /* Put an array of dwords in to main memory */ @@ -1021,12 +1030,19 @@ static inline int put_dwords(EHCIState *ehci, uint32_t addr, { int i; + if (!ehci->dma) { + ehci_raise_irq(ehci, USBSTS_HSE); + ehci->usbcmd &= ~USBCMD_RUNSTOP; + trace_usb_ehci_dma_error(); + return -1; + } + for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { uint32_t tmp = cpu_to_le32(*buf); dma_memory_write(ehci->dma, addr, &tmp, sizeof(tmp)); } - return 1; + return num; } /* @@ -1111,7 +1127,7 @@ static int ehci_init_transfer(EHCIPacket *p) while (bytes > 0) { if (cpage > 4) { fprintf(stderr, "cpage out of range (%d)\n", cpage); - return USB_RET_PROCERR; + return -1; } page = p->qtd.bufptr[cpage] & QTD_BUFPTR_MASK; @@ -1129,16 +1145,16 @@ static int ehci_init_transfer(EHCIPacket *p) return 0; } -static void ehci_finish_transfer(EHCIQueue *q, int status) +static void ehci_finish_transfer(EHCIQueue *q, int len) { uint32_t cpage, offset; - if (status > 0) { + if (len > 0) { /* update cpage & offset */ cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK; - offset += status; + offset += len; cpage += offset >> QTD_BUFPTR_SH; offset &= ~QTD_BUFPTR_MASK; @@ -1163,7 +1179,7 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) p = container_of(packet, EHCIPacket, packet); assert(p->async == EHCI_ASYNC_INFLIGHT); - if (packet->result == USB_RET_REMOVE_FROM_QUEUE) { + if (packet->status == USB_RET_REMOVE_FROM_QUEUE) { trace_usb_ehci_packet_action(p->queue, p, "remove"); ehci_free_packet(p); return; @@ -1171,7 +1187,6 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) trace_usb_ehci_packet_action(p->queue, p, "wakeup"); p->async = EHCI_ASYNC_FINISHED; - p->usb_status = packet->result; if (p->queue->async) { qemu_bh_schedule(p->queue->ehci->async_bh); @@ -1181,58 +1196,60 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) static void ehci_execute_complete(EHCIQueue *q) { EHCIPacket *p = QTAILQ_FIRST(&q->packets); + uint32_t tbytes; assert(p != NULL); assert(p->qtdaddr == q->qtdaddr); assert(p->async == EHCI_ASYNC_INITIALIZED || p->async == EHCI_ASYNC_FINISHED); - DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n", - q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status); + DPRINTF("execute_complete: qhaddr 0x%x, next 0x%x, qtdaddr 0x%x, " + "status %d, actual_length %d\n", + q->qhaddr, q->qh.next, q->qtdaddr, + p->packet.status, p->packet.actual_length); - if (p->usb_status < 0) { - switch (p->usb_status) { - case USB_RET_IOERROR: - case USB_RET_NODEV: - q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR); - set_field(&q->qh.token, 0, QTD_TOKEN_CERR); - ehci_raise_irq(q->ehci, USBSTS_ERRINT); - break; - case USB_RET_STALL: - q->qh.token |= QTD_TOKEN_HALT; - ehci_raise_irq(q->ehci, USBSTS_ERRINT); - break; - case USB_RET_NAK: - set_field(&q->qh.altnext_qtd, 0, QH_ALTNEXT_NAKCNT); - return; /* We're not done yet with this transaction */ - case USB_RET_BABBLE: - q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE); - ehci_raise_irq(q->ehci, USBSTS_ERRINT); - break; - default: - /* should not be triggerable */ - fprintf(stderr, "USB invalid response %d\n", p->usb_status); - assert(0); - break; + switch (p->packet.status) { + case USB_RET_SUCCESS: + break; + case USB_RET_IOERROR: + case USB_RET_NODEV: + q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR); + set_field(&q->qh.token, 0, QTD_TOKEN_CERR); + ehci_raise_irq(q->ehci, USBSTS_ERRINT); + break; + case USB_RET_STALL: + q->qh.token |= QTD_TOKEN_HALT; + ehci_raise_irq(q->ehci, USBSTS_ERRINT); + break; + case USB_RET_NAK: + set_field(&q->qh.altnext_qtd, 0, QH_ALTNEXT_NAKCNT); + return; /* We're not done yet with this transaction */ + case USB_RET_BABBLE: + q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE); + ehci_raise_irq(q->ehci, USBSTS_ERRINT); + break; + default: + /* should not be triggerable */ + fprintf(stderr, "USB invalid response %d\n", p->packet.status); + assert(0); + break; + } + + /* TODO check 4.12 for splits */ + tbytes = get_field(q->qh.token, QTD_TOKEN_TBYTES); + if (tbytes && p->pid == USB_TOKEN_IN) { + tbytes -= p->packet.actual_length; + if (tbytes) { + /* 4.15.1.2 must raise int on a short input packet */ + ehci_raise_irq(q->ehci, USBSTS_INT); } } else { - // TODO check 4.12 for splits - uint32_t tbytes = get_field(q->qh.token, QTD_TOKEN_TBYTES); - - if (tbytes && p->pid == USB_TOKEN_IN) { - tbytes -= p->usb_status; - if (tbytes) { - /* 4.15.1.2 must raise int on a short input packet */ - ehci_raise_irq(q->ehci, USBSTS_INT); - } - } else { - tbytes = 0; - } - - DPRINTF("updating tbytes to %d\n", tbytes); - set_field(&q->qh.token, tbytes, QTD_TOKEN_TBYTES); + tbytes = 0; } - ehci_finish_transfer(q, p->usb_status); + DPRINTF("updating tbytes to %d\n", tbytes); + set_field(&q->qh.token, tbytes, QTD_TOKEN_TBYTES); + + ehci_finish_transfer(q, p->packet.actual_length); usb_packet_unmap(&p->packet, &p->sgl); qemu_sglist_destroy(&p->sgl); p->async = EHCI_ASYNC_NONE; @@ -1248,12 +1265,10 @@ static void ehci_execute_complete(EHCIQueue *q) } } -// 4.10.3 - +/* 4.10.3 returns "again" */ static int ehci_execute(EHCIPacket *p, const char *action) { USBEndpoint *ep; - int ret; int endp; bool spd; @@ -1262,13 +1277,13 @@ static int ehci_execute(EHCIPacket *p, const char *action) if (!(p->qtd.token & QTD_TOKEN_ACTIVE)) { fprintf(stderr, "Attempting to execute inactive qtd\n"); - return USB_RET_PROCERR; + return -1; } if (get_field(p->qtd.token, QTD_TOKEN_TBYTES) > BUFF_SIZE) { ehci_trace_guest_bug(p->queue->ehci, "guest requested more bytes than allowed"); - return USB_RET_PROCERR; + return -1; } p->pid = (p->qtd.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH; @@ -1292,7 +1307,7 @@ static int ehci_execute(EHCIPacket *p, const char *action) if (p->async == EHCI_ASYNC_NONE) { if (ehci_init_transfer(p) != 0) { - return USB_RET_PROCERR; + return -1; } spd = (p->pid == USB_TOKEN_IN && NLPTR_TBIT(p->qtd.altnext) == 0); @@ -1303,17 +1318,18 @@ static int ehci_execute(EHCIPacket *p, const char *action) } trace_usb_ehci_packet_action(p->queue, p, action); - ret = usb_handle_packet(p->queue->dev, &p->packet); - DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd endp %x ret %d\n", - q->qhaddr, q->qh.next, q->qtdaddr, q->pid, - q->packet.iov.size, endp, ret); + usb_handle_packet(p->queue->dev, &p->packet); + DPRINTF("submit: qh 0x%x next 0x%x qtd 0x%x pid 0x%x len %zd endp 0x%x " + "status %d actual_length %d\n", p->queue->qhaddr, p->qtd.next, + p->qtdaddr, p->pid, p->packet.iov.size, endp, p->packet.status, + p->packet.actual_length); - if (ret > BUFF_SIZE) { + if (p->packet.actual_length > BUFF_SIZE) { fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n"); - return USB_RET_PROCERR; + return -1; } - return ret; + return 1; } /* 4.7.2 @@ -1325,7 +1341,6 @@ static int ehci_process_itd(EHCIState *ehci, { USBDevice *dev; USBEndpoint *ep; - int ret; uint32_t i, len, pid, dir, devaddr, endp; uint32_t pg, off, ptr1, ptr2, max, mult; @@ -1348,7 +1363,7 @@ static int ehci_process_itd(EHCIState *ehci, } if (len > BUFF_SIZE) { - return USB_RET_PROCERR; + return -1; } qemu_sglist_init(&ehci->isgl, 2, ehci->dma); @@ -1370,45 +1385,45 @@ static int ehci_process_itd(EHCIState *ehci, usb_packet_setup(&ehci->ipacket, pid, ep, addr, false, (itd->transact[i] & ITD_XACT_IOC) != 0); usb_packet_map(&ehci->ipacket, &ehci->isgl); - ret = usb_handle_packet(dev, &ehci->ipacket); + usb_handle_packet(dev, &ehci->ipacket); usb_packet_unmap(&ehci->ipacket, &ehci->isgl); } else { DPRINTF("ISOCH: attempt to addess non-iso endpoint\n"); - ret = USB_RET_NAK; + ehci->ipacket.status = USB_RET_NAK; + ehci->ipacket.actual_length = 0; } qemu_sglist_destroy(&ehci->isgl); - if (ret < 0) { - switch (ret) { - default: - fprintf(stderr, "Unexpected iso usb result: %d\n", ret); - /* Fall through */ - case USB_RET_IOERROR: - case USB_RET_NODEV: - /* 3.3.2: XACTERR is only allowed on IN transactions */ - if (dir) { - itd->transact[i] |= ITD_XACT_XACTERR; - ehci_raise_irq(ehci, USBSTS_ERRINT); - } - break; - case USB_RET_BABBLE: - itd->transact[i] |= ITD_XACT_BABBLE; + switch (ehci->ipacket.status) { + case USB_RET_SUCCESS: + break; + default: + fprintf(stderr, "Unexpected iso usb result: %d\n", + ehci->ipacket.status); + /* Fall through */ + case USB_RET_IOERROR: + case USB_RET_NODEV: + /* 3.3.2: XACTERR is only allowed on IN transactions */ + if (dir) { + itd->transact[i] |= ITD_XACT_XACTERR; ehci_raise_irq(ehci, USBSTS_ERRINT); - break; - case USB_RET_NAK: - /* no data for us, so do a zero-length transfer */ - ret = 0; - break; } + break; + case USB_RET_BABBLE: + itd->transact[i] |= ITD_XACT_BABBLE; + ehci_raise_irq(ehci, USBSTS_ERRINT); + break; + case USB_RET_NAK: + /* no data for us, so do a zero-length transfer */ + ehci->ipacket.actual_length = 0; + break; } - if (ret >= 0) { - if (!dir) { - /* OUT */ - set_field(&itd->transact[i], len - ret, ITD_XACT_LENGTH); - } else { - /* IN */ - set_field(&itd->transact[i], ret, ITD_XACT_LENGTH); - } + if (!dir) { + set_field(&itd->transact[i], len - ehci->ipacket.actual_length, + ITD_XACT_LENGTH); /* OUT */ + } else { + set_field(&itd->transact[i], ehci->ipacket.actual_length, + ITD_XACT_LENGTH); /* IN */ } if (itd->transact[i] & ITD_XACT_IOC) { ehci_raise_irq(ehci, USBSTS_INT); @@ -1439,8 +1454,10 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) /* Find the head of the list (4.9.1.1) */ for(i = 0; i < MAX_QH; i++) { - get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &qh, - sizeof(EHCIqh) >> 2); + if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &qh, + sizeof(EHCIqh) >> 2) < 0) { + return 0; + } ehci_trace_qh(NULL, NLPTR_GET(entry), &qh); if (qh.epchar & QH_EPCHAR_H) { @@ -1537,8 +1554,11 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) goto out; } - get_dwords(ehci, NLPTR_GET(q->qhaddr), - (uint32_t *) &qh, sizeof(EHCIqh) >> 2); + if (get_dwords(ehci, NLPTR_GET(q->qhaddr), + (uint32_t *) &qh, sizeof(EHCIqh) >> 2) < 0) { + q = NULL; + goto out; + } ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &qh); /* @@ -1549,8 +1569,10 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) endp = get_field(qh.epchar, QH_EPCHAR_EP); if ((devaddr != get_field(q->qh.epchar, QH_EPCHAR_DEVADDR)) || (endp != get_field(q->qh.epchar, QH_EPCHAR_EP)) || - (memcmp(&qh.current_qtd, &q->qh.current_qtd, - 9 * sizeof(uint32_t)) != 0) || + (qh.current_qtd != q->qh.current_qtd) || + (q->async && qh.next_qtd != q->qh.next_qtd) || + (memcmp(&qh.altnext_qtd, &q->qh.altnext_qtd, + 7 * sizeof(uint32_t)) != 0) || (q->dev != NULL && q->dev->addr != devaddr)) { if (ehci_reset_queue(q) > 0) { ehci_trace_guest_bug(ehci, "guest updated active QH"); @@ -1625,8 +1647,10 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async) assert(!async); entry = ehci_get_fetch_addr(ehci, async); - get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd, - sizeof(EHCIitd) >> 2); + if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd, + sizeof(EHCIitd) >> 2) < 0) { + return -1; + } ehci_trace_itd(ehci, entry, &itd); if (ehci_process_itd(ehci, &itd, entry) != 0) { @@ -1649,8 +1673,10 @@ static int ehci_state_fetchsitd(EHCIState *ehci, int async) assert(!async); entry = ehci_get_fetch_addr(ehci, async); - get_dwords(ehci, NLPTR_GET(entry), (uint32_t *)&sitd, - sizeof(EHCIsitd) >> 2); + if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *)&sitd, + sizeof(EHCIsitd) >> 2) < 0) { + return 0; + } ehci_trace_sitd(ehci, entry, &sitd); if (!(sitd.results & SITD_RESULTS_ACTIVE)) { @@ -1711,14 +1737,17 @@ static int ehci_state_fetchqtd(EHCIQueue *q) EHCIPacket *p; int again = 1; - get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd, - sizeof(EHCIqtd) >> 2); + if (get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd, + sizeof(EHCIqtd) >> 2) < 0) { + return 0; + } ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd); p = QTAILQ_FIRST(&q->packets); if (p != NULL) { if (p->qtdaddr != q->qtdaddr || - (!NLPTR_TBIT(p->qtd.next) && (p->qtd.next != qtd.next)) || + (q->async && !NLPTR_TBIT(p->qtd.next) && + (p->qtd.next != qtd.next)) || (!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd.altnext)) || p->qtd.bufptr[0] != qtd.bufptr[0]) { ehci_cancel_queue(q); @@ -1746,8 +1775,7 @@ static int ehci_state_fetchqtd(EHCIQueue *q) break; case EHCI_ASYNC_INFLIGHT: /* Check if the guest has added new tds to the queue */ - again = (ehci_fill_queue(QTAILQ_LAST(&q->packets, pkts_head)) == - USB_RET_PROCERR) ? -1 : 1; + again = ehci_fill_queue(QTAILQ_LAST(&q->packets, pkts_head)); /* Unfinished async handled packet, go horizontal */ ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); break; @@ -1784,12 +1812,13 @@ static int ehci_state_horizqh(EHCIQueue *q) return again; } +/* Returns "again" */ static int ehci_fill_queue(EHCIPacket *p) { USBEndpoint *ep = p->packet.ep; EHCIQueue *q = p->queue; EHCIqtd qtd = p->qtd; - uint32_t qtdaddr, start_addr = p->qtdaddr; + uint32_t qtdaddr; for (;;) { if (NLPTR_TBIT(qtd.next) != 0) { @@ -1800,11 +1829,15 @@ static int ehci_fill_queue(EHCIPacket *p) * Detect circular td lists, Windows creates these, counting on the * active bit going low after execution to make the queue stop. */ - if (qtdaddr == start_addr) { - break; + QTAILQ_FOREACH(p, &q->packets, next) { + if (p->qtdaddr == qtdaddr) { + goto leave; + } + } + if (get_dwords(q->ehci, NLPTR_GET(qtdaddr), + (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2) < 0) { + return -1; } - get_dwords(q->ehci, NLPTR_GET(qtdaddr), - (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2); ehci_trace_qtd(q, NLPTR_GET(qtdaddr), &qtd); if (!(qtd.token & QTD_TOKEN_ACTIVE)) { break; @@ -1812,17 +1845,15 @@ static int ehci_fill_queue(EHCIPacket *p) p = ehci_alloc_packet(q); p->qtdaddr = qtdaddr; p->qtd = qtd; - p->usb_status = ehci_execute(p, "queue"); - if (p->usb_status == USB_RET_PROCERR) { - break; + if (ehci_execute(p, "queue") == -1) { + return -1; } - assert(p->usb_status == USB_RET_ASYNC); + assert(p->packet.status == USB_RET_ASYNC); p->async = EHCI_ASYNC_INFLIGHT; } - if (p->usb_status != USB_RET_PROCERR) { - usb_device_flush_ep_queue(ep->dev, ep); - } - return p->usb_status; +leave: + usb_device_flush_ep_queue(ep->dev, ep); + return 1; } static int ehci_state_execute(EHCIQueue *q) @@ -1851,18 +1882,17 @@ static int ehci_state_execute(EHCIQueue *q) ehci_set_usbsts(q->ehci, USBSTS_REC); } - p->usb_status = ehci_execute(p, "process"); - if (p->usb_status == USB_RET_PROCERR) { - again = -1; + again = ehci_execute(p, "process"); + if (again == -1) { goto out; } - if (p->usb_status == USB_RET_ASYNC) { + if (p->packet.status == USB_RET_ASYNC) { ehci_flush_qh(q); trace_usb_ehci_packet_action(p->queue, p, "async"); p->async = EHCI_ASYNC_INFLIGHT; ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); if (q->async) { - again = (ehci_fill_queue(p) == USB_RET_PROCERR) ? -1 : 1; + again = ehci_fill_queue(p); } else { again = 1; } @@ -1891,7 +1921,7 @@ static int ehci_state_executing(EHCIQueue *q) } /* 4.10.5 */ - if (p->usb_status == USB_RET_NAK) { + if (p->packet.status == USB_RET_NAK) { ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); } else { ehci_set_state(q->ehci, q->async, EST_WRITEBACK); @@ -2106,8 +2136,9 @@ static void ehci_advance_periodic_state(EHCIState *ehci) } list |= ((ehci->frindex & 0x1ff8) >> 1); - dma_memory_read(ehci->dma, list, &entry, sizeof entry); - entry = le32_to_cpu(entry); + if (get_dwords(ehci, list, &entry, 1) < 0) { + break; + } DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n", ehci->frindex / 8, list, entry); @@ -2217,6 +2248,10 @@ static void ehci_frame_timer(void *opaque) ehci->async_stepdown = 0; } + if (ehci_enabled(ehci) && (ehci->usbintr & USBSTS_FLR)) { + need_timer++; + } + if (need_timer) { /* If we've raised int, we speed up the timer, so that we quickly * notice any new packets queued up in response */ |