aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorArvid Brodin <arvid.brodin@enea.com>2011-05-19 18:17:45 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-05-19 19:34:04 -0400
commitd05b6ec01b8186f847ac9e41098e40858926db40 (patch)
tree12ac448547d00704cd15b293732c17d036ddae10 /drivers/usb/host
parent079cdb0947ce6ae7df0c73a1c82c14920a9b6b6d (diff)
usb/isp1760: Fix possible unlink problems
Use skip map to avoid spurious interrupts from unlinked transfers. Also changes to urb_dequeue() and endpoint_disable() to avoid release of spinlock in uncertain state. Signed-off-by: Arvid Brodin <arvid.brodin@enea.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/isp1760-hcd.c147
1 files changed, 95 insertions, 52 deletions
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c
index 485fc70625a1..c9e6e454c625 100644
--- a/drivers/usb/host/isp1760-hcd.c
+++ b/drivers/usb/host/isp1760-hcd.c
@@ -34,7 +34,9 @@ struct isp1760_hcd {
34 u32 hcs_params; 34 u32 hcs_params;
35 spinlock_t lock; 35 spinlock_t lock;
36 struct slotinfo atl_slots[32]; 36 struct slotinfo atl_slots[32];
37 int atl_done_map;
37 struct slotinfo int_slots[32]; 38 struct slotinfo int_slots[32];
39 int int_done_map;
38 struct memory_chunk memory_pool[BLOCKS]; 40 struct memory_chunk memory_pool[BLOCKS];
39 struct list_head controlqhs, bulkqhs, interruptqhs; 41 struct list_head controlqhs, bulkqhs, interruptqhs;
40 int active_ptds; 42 int active_ptds;
@@ -519,9 +521,9 @@ static void isp1760_init_maps(struct usb_hcd *hcd)
519 reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000); 521 reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000);
520 reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001); 522 reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001);
521 523
522 reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0); 524 reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0xffffffff);
523 reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0); 525 reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0xffffffff);
524 reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0); 526 reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0xffffffff);
525 527
526 reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, 528 reg_write32(hcd->regs, HC_BUFFER_STATUS_REG,
527 ATL_BUF_FILL | INT_BUF_FILL); 529 ATL_BUF_FILL | INT_BUF_FILL);
@@ -803,6 +805,8 @@ static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot,
803 struct isp1760_qh *qh, struct ptd *ptd) 805 struct isp1760_qh *qh, struct ptd *ptd)
804{ 806{
805 struct isp1760_hcd *priv = hcd_to_priv(hcd); 807 struct isp1760_hcd *priv = hcd_to_priv(hcd);
808 int skip_map;
809
806 WARN_ON((slot < 0) || (slot > 31)); 810 WARN_ON((slot < 0) || (slot > 31));
807 WARN_ON(qtd->length && !qtd->payload_addr); 811 WARN_ON(qtd->length && !qtd->payload_addr);
808 WARN_ON(slots[slot].qtd); 812 WARN_ON(slots[slot].qtd);
@@ -816,6 +820,25 @@ static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot,
816 interrupt routine may preempt and expects this value. */ 820 interrupt routine may preempt and expects this value. */
817 ptd_write(hcd->regs, ptd_offset, slot, ptd); 821 ptd_write(hcd->regs, ptd_offset, slot, ptd);
818 priv->active_ptds++; 822 priv->active_ptds++;
823
824 /* Make sure done map has not triggered from some unlinked transfer */
825 if (ptd_offset == ATL_PTD_OFFSET) {
826 priv->atl_done_map |= reg_read32(hcd->regs,
827 HC_ATL_PTD_DONEMAP_REG);
828 priv->atl_done_map &= ~(1 << qh->slot);
829
830 skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
831 skip_map &= ~(1 << qh->slot);
832 reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map);
833 } else {
834 priv->int_done_map |= reg_read32(hcd->regs,
835 HC_INT_PTD_DONEMAP_REG);
836 priv->int_done_map &= ~(1 << qh->slot);
837
838 skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
839 skip_map &= ~(1 << qh->slot);
840 reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map);
841 }
819} 842}
820 843
821static int is_short_bulk(struct isp1760_qtd *qtd) 844static int is_short_bulk(struct isp1760_qtd *qtd)
@@ -1152,7 +1175,6 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
1152 irqreturn_t irqret = IRQ_NONE; 1175 irqreturn_t irqret = IRQ_NONE;
1153 struct ptd ptd; 1176 struct ptd ptd;
1154 struct isp1760_qh *qh; 1177 struct isp1760_qh *qh;
1155 int int_done_map, atl_done_map;
1156 int slot; 1178 int slot;
1157 int state; 1179 int state;
1158 struct slotinfo *slots; 1180 struct slotinfo *slots;
@@ -1160,6 +1182,7 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
1160 struct isp1760_qtd *qtd; 1182 struct isp1760_qtd *qtd;
1161 int modified; 1183 int modified;
1162 static int last_active_ptds; 1184 static int last_active_ptds;
1185 int int_skip_map, atl_skip_map;
1163 1186
1164 spin_lock(&priv->lock); 1187 spin_lock(&priv->lock);
1165 1188
@@ -1171,29 +1194,42 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
1171 goto leave; 1194 goto leave;
1172 reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */ 1195 reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */
1173 1196
1174 int_done_map = reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG); 1197 int_skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
1175 atl_done_map = reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); 1198 atl_skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
1176 modified = int_done_map | atl_done_map; 1199 priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG);
1200 priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG);
1201 priv->int_done_map &= ~int_skip_map;
1202 priv->atl_done_map &= ~atl_skip_map;
1177 1203
1178 while (int_done_map || atl_done_map) { 1204 modified = priv->int_done_map | priv->atl_done_map;
1179 if (int_done_map) { 1205
1206 while (priv->int_done_map || priv->atl_done_map) {
1207 if (priv->int_done_map) {
1180 /* INT ptd */ 1208 /* INT ptd */
1181 slot = __ffs(int_done_map); 1209 slot = __ffs(priv->int_done_map);
1182 int_done_map &= ~(1 << slot); 1210 priv->int_done_map &= ~(1 << slot);
1183 slots = priv->int_slots; 1211 slots = priv->int_slots;
1184 if (!slots[slot].qh) 1212 /* This should not trigger, and could be removed if
1213 noone have any problems with it triggering: */
1214 if (!slots[slot].qh) {
1215 WARN_ON(1);
1185 continue; 1216 continue;
1217 }
1186 ptd_offset = INT_PTD_OFFSET; 1218 ptd_offset = INT_PTD_OFFSET;
1187 ptd_read(hcd->regs, INT_PTD_OFFSET, slot, &ptd); 1219 ptd_read(hcd->regs, INT_PTD_OFFSET, slot, &ptd);
1188 state = check_int_transfer(hcd, &ptd, 1220 state = check_int_transfer(hcd, &ptd,
1189 slots[slot].qtd->urb); 1221 slots[slot].qtd->urb);
1190 } else { 1222 } else {
1191 /* ATL ptd */ 1223 /* ATL ptd */
1192 slot = __ffs(atl_done_map); 1224 slot = __ffs(priv->atl_done_map);
1193 atl_done_map &= ~(1 << slot); 1225 priv->atl_done_map &= ~(1 << slot);
1194 slots = priv->atl_slots; 1226 slots = priv->atl_slots;
1195 if (!slots[slot].qh) 1227 /* This should not trigger, and could be removed if
1228 noone have any problems with it triggering: */
1229 if (!slots[slot].qh) {
1230 WARN_ON(1);
1196 continue; 1231 continue;
1232 }
1197 ptd_offset = ATL_PTD_OFFSET; 1233 ptd_offset = ATL_PTD_OFFSET;
1198 ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); 1234 ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd);
1199 state = check_atl_transfer(hcd, &ptd, 1235 state = check_atl_transfer(hcd, &ptd,
@@ -1509,14 +1545,41 @@ out:
1509 return retval; 1545 return retval;
1510} 1546}
1511 1547
1548static void kill_transfer(struct usb_hcd *hcd, struct urb *urb,
1549 struct isp1760_qh *qh)
1550{
1551 struct isp1760_hcd *priv = hcd_to_priv(hcd);
1552 int skip_map;
1553
1554 WARN_ON(qh->slot == -1);
1555
1556 /* We need to forcefully reclaim the slot since some transfers never
1557 return, e.g. interrupt transfers and NAKed bulk transfers. */
1558 if (usb_pipebulk(urb->pipe)) {
1559 skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
1560 skip_map |= (1 << qh->slot);
1561 reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map);
1562 priv->atl_slots[qh->slot].qh = NULL;
1563 priv->atl_slots[qh->slot].qtd = NULL;
1564 } else {
1565 skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
1566 skip_map |= (1 << qh->slot);
1567 reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map);
1568 priv->int_slots[qh->slot].qh = NULL;
1569 priv->int_slots[qh->slot].qtd = NULL;
1570 }
1571
1572 qh->slot = -1;
1573 priv->active_ptds--;
1574}
1575
1512static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, 1576static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
1513 int status) 1577 int status)
1514{ 1578{
1515 struct isp1760_hcd *priv = hcd_to_priv(hcd); 1579 struct isp1760_hcd *priv = hcd_to_priv(hcd);
1580 unsigned long spinflags;
1516 struct isp1760_qh *qh; 1581 struct isp1760_qh *qh;
1517 struct isp1760_qtd *qtd; 1582 struct isp1760_qtd *qtd;
1518 struct ptd ptd;
1519 unsigned long spinflags;
1520 int retval = 0; 1583 int retval = 0;
1521 1584
1522 spin_lock_irqsave(&priv->lock, spinflags); 1585 spin_lock_irqsave(&priv->lock, spinflags);
@@ -1527,34 +1590,18 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
1527 goto out; 1590 goto out;
1528 } 1591 }
1529 1592
1530 /* We need to forcefully reclaim the slot since some transfers never 1593 list_for_each_entry(qtd, &qh->qtd_list, qtd_list)
1531 return, e.g. interrupt transfers and NAKed bulk transfers. */ 1594 if (qtd->urb == urb) {
1532 if (qh->slot > -1) { 1595 if (qtd->status == QTD_XFER_STARTED)
1533 memset(&ptd, 0, sizeof(ptd)); 1596 kill_transfer(hcd, urb, qh);
1534 if (usb_pipebulk(urb->pipe)) {
1535 priv->atl_slots[qh->slot].qh = NULL;
1536 priv->atl_slots[qh->slot].qtd = NULL;
1537 ptd_write(hcd->regs, ATL_PTD_OFFSET, qh->slot, &ptd);
1538 } else {
1539 priv->int_slots[qh->slot].qh = NULL;
1540 priv->int_slots[qh->slot].qtd = NULL;
1541 ptd_write(hcd->regs, INT_PTD_OFFSET, qh->slot, &ptd);
1542 }
1543 priv->active_ptds--;
1544 qh->slot = -1;
1545 }
1546
1547 list_for_each_entry(qtd, &qh->qtd_list, qtd_list) {
1548 if (qtd->urb == urb)
1549 qtd->status = QTD_RETIRE; 1597 qtd->status = QTD_RETIRE;
1550 } 1598 }
1551 1599
1552 urb->status = status; 1600 urb->status = status;
1553 schedule_ptds(hcd); 1601 schedule_ptds(hcd);
1554 1602
1555out: 1603out:
1556 spin_unlock_irqrestore(&priv->lock, spinflags); 1604 spin_unlock_irqrestore(&priv->lock, spinflags);
1557
1558 return retval; 1605 return retval;
1559} 1606}
1560 1607
@@ -1562,32 +1609,28 @@ static void isp1760_endpoint_disable(struct usb_hcd *hcd,
1562 struct usb_host_endpoint *ep) 1609 struct usb_host_endpoint *ep)
1563{ 1610{
1564 struct isp1760_hcd *priv = hcd_to_priv(hcd); 1611 struct isp1760_hcd *priv = hcd_to_priv(hcd);
1612 unsigned long spinflags;
1565 struct isp1760_qh *qh; 1613 struct isp1760_qh *qh;
1566 struct isp1760_qtd *qtd; 1614 struct isp1760_qtd *qtd;
1567 unsigned long spinflags;
1568 int do_iter;
1569 1615
1570 spin_lock_irqsave(&priv->lock, spinflags); 1616 spin_lock_irqsave(&priv->lock, spinflags);
1617
1571 qh = ep->hcpriv; 1618 qh = ep->hcpriv;
1572 if (!qh) 1619 if (!qh)
1573 goto out; 1620 goto out;
1574 1621
1575 do_iter = !list_empty(&qh->qtd_list); 1622 list_for_each_entry(qtd, &qh->qtd_list, qtd_list) {
1576 while (do_iter) { 1623 if (qtd->status == QTD_XFER_STARTED)
1577 do_iter = 0; 1624 kill_transfer(hcd, qtd->urb, qh);
1578 list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { 1625 qtd->status = QTD_RETIRE;
1579 if (qtd->urb->ep == ep) { 1626 qtd->urb->status = -ECONNRESET;
1580 spin_unlock_irqrestore(&priv->lock, spinflags);
1581 isp1760_urb_dequeue(hcd, qtd->urb, -ECONNRESET);
1582 spin_lock_irqsave(&priv->lock, spinflags);
1583 do_iter = 1;
1584 break; /* Restart iteration */
1585 }
1586 }
1587 } 1627 }
1628
1588 ep->hcpriv = NULL; 1629 ep->hcpriv = NULL;
1589 /* Cannot free qh here since it will be parsed by schedule_ptds() */ 1630 /* Cannot free qh here since it will be parsed by schedule_ptds() */
1590 1631
1632 schedule_ptds(hcd);
1633
1591out: 1634out:
1592 spin_unlock_irqrestore(&priv->lock, spinflags); 1635 spin_unlock_irqrestore(&priv->lock, spinflags);
1593} 1636}