| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: ohci.c,v 1.235 2013/03/22 13:28:11 skrll Exp $ */ | | 1 | /* $NetBSD: ohci.c,v 1.236 2013/04/03 19:02:12 skrll Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 1998, 2004, 2005, 2012 The NetBSD Foundation, Inc. | | 4 | * Copyright (c) 1998, 2004, 2005, 2012 The NetBSD Foundation, Inc. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation | | 7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Lennart Augustsson (lennart@augustsson.net) at | | 8 | * by Lennart Augustsson (lennart@augustsson.net) at |
9 | * Carlstedt Research & Technology, Jared D. McNeill (jmcneill@invisible.ca) | | 9 | * Carlstedt Research & Technology, Jared D. McNeill (jmcneill@invisible.ca) |
10 | * and Matthew R. Green (mrg@eterna.com.au). | | 10 | * and Matthew R. Green (mrg@eterna.com.au). |
11 | * This code is derived from software contributed to The NetBSD Foundation | | 11 | * This code is derived from software contributed to The NetBSD Foundation |
12 | * by Charles M. Hannum. | | 12 | * by Charles M. Hannum. |
13 | * | | 13 | * |
14 | * Redistribution and use in source and binary forms, with or without | | 14 | * Redistribution and use in source and binary forms, with or without |
| @@ -31,27 +31,27 @@ | | | @@ -31,27 +31,27 @@ |
31 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 31 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | | 32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
33 | * POSSIBILITY OF SUCH DAMAGE. | | 33 | * POSSIBILITY OF SUCH DAMAGE. |
34 | */ | | 34 | */ |
35 | | | 35 | |
36 | /* | | 36 | /* |
37 | * USB Open Host Controller driver. | | 37 | * USB Open Host Controller driver. |
38 | * | | 38 | * |
39 | * OHCI spec: http://www.compaq.com/productinfo/development/openhci.html | | 39 | * OHCI spec: http://www.compaq.com/productinfo/development/openhci.html |
40 | * USB spec: http://www.usb.org/developers/docs/ | | 40 | * USB spec: http://www.usb.org/developers/docs/ |
41 | */ | | 41 | */ |
42 | | | 42 | |
43 | #include <sys/cdefs.h> | | 43 | #include <sys/cdefs.h> |
44 | __KERNEL_RCSID(0, "$NetBSD: ohci.c,v 1.235 2013/03/22 13:28:11 skrll Exp $"); | | 44 | __KERNEL_RCSID(0, "$NetBSD: ohci.c,v 1.236 2013/04/03 19:02:12 skrll Exp $"); |
45 | | | 45 | |
46 | #include <sys/param.h> | | 46 | #include <sys/param.h> |
47 | #include <sys/systm.h> | | 47 | #include <sys/systm.h> |
48 | #include <sys/kmem.h> | | 48 | #include <sys/kmem.h> |
49 | #include <sys/kernel.h> | | 49 | #include <sys/kernel.h> |
50 | #include <sys/device.h> | | 50 | #include <sys/device.h> |
51 | #include <sys/select.h> | | 51 | #include <sys/select.h> |
52 | #include <sys/proc.h> | | 52 | #include <sys/proc.h> |
53 | #include <sys/queue.h> | | 53 | #include <sys/queue.h> |
54 | #include <sys/cpu.h> | | 54 | #include <sys/cpu.h> |
55 | | | 55 | |
56 | #include <machine/endian.h> | | 56 | #include <machine/endian.h> |
57 | | | 57 | |
| @@ -88,30 +88,28 @@ int ohcidebug = 0; | | | @@ -88,30 +88,28 @@ int ohcidebug = 0; |
88 | #define HTOO32(val) O32TOH(val) | | 88 | #define HTOO32(val) O32TOH(val) |
89 | | | 89 | |
90 | struct ohci_pipe; | | 90 | struct ohci_pipe; |
91 | | | 91 | |
92 | Static ohci_soft_ed_t *ohci_alloc_sed(ohci_softc_t *); | | 92 | Static ohci_soft_ed_t *ohci_alloc_sed(ohci_softc_t *); |
93 | Static void ohci_free_sed(ohci_softc_t *, ohci_soft_ed_t *); | | 93 | Static void ohci_free_sed(ohci_softc_t *, ohci_soft_ed_t *); |
94 | | | 94 | |
95 | Static ohci_soft_td_t *ohci_alloc_std(ohci_softc_t *); | | 95 | Static ohci_soft_td_t *ohci_alloc_std(ohci_softc_t *); |
96 | Static void ohci_free_std(ohci_softc_t *, ohci_soft_td_t *); | | 96 | Static void ohci_free_std(ohci_softc_t *, ohci_soft_td_t *); |
97 | | | 97 | |
98 | Static ohci_soft_itd_t *ohci_alloc_sitd(ohci_softc_t *); | | 98 | Static ohci_soft_itd_t *ohci_alloc_sitd(ohci_softc_t *); |
99 | Static void ohci_free_sitd(ohci_softc_t *,ohci_soft_itd_t *); | | 99 | Static void ohci_free_sitd(ohci_softc_t *,ohci_soft_itd_t *); |
100 | | | 100 | |
101 | #if 0 | | | |
102 | Static void ohci_free_std_chain(ohci_softc_t *, ohci_soft_td_t *, | | 101 | Static void ohci_free_std_chain(ohci_softc_t *, ohci_soft_td_t *, |
103 | ohci_soft_td_t *); | | 102 | ohci_soft_td_t *); |
104 | #endif | | | |
105 | Static usbd_status ohci_alloc_std_chain(struct ohci_pipe *, | | 103 | Static usbd_status ohci_alloc_std_chain(struct ohci_pipe *, |
106 | ohci_softc_t *, int, int, usbd_xfer_handle, | | 104 | ohci_softc_t *, int, int, usbd_xfer_handle, |
107 | ohci_soft_td_t *, ohci_soft_td_t **); | | 105 | ohci_soft_td_t *, ohci_soft_td_t **); |
108 | | | 106 | |
109 | Static usbd_status ohci_open(usbd_pipe_handle); | | 107 | Static usbd_status ohci_open(usbd_pipe_handle); |
110 | Static void ohci_poll(struct usbd_bus *); | | 108 | Static void ohci_poll(struct usbd_bus *); |
111 | Static void ohci_softintr(void *); | | 109 | Static void ohci_softintr(void *); |
112 | Static void ohci_waitintr(ohci_softc_t *, usbd_xfer_handle); | | 110 | Static void ohci_waitintr(ohci_softc_t *, usbd_xfer_handle); |
113 | Static void ohci_rhsc(ohci_softc_t *, usbd_xfer_handle); | | 111 | Static void ohci_rhsc(ohci_softc_t *, usbd_xfer_handle); |
114 | Static void ohci_rhsc_softint(void *arg); | | 112 | Static void ohci_rhsc_softint(void *arg); |
115 | | | 113 | |
116 | Static usbd_status ohci_device_request(usbd_xfer_handle xfer); | | 114 | Static usbd_status ohci_device_request(usbd_xfer_handle xfer); |
117 | Static void ohci_add_ed(ohci_softc_t *, ohci_soft_ed_t *, | | 115 | Static void ohci_add_ed(ohci_softc_t *, ohci_soft_ed_t *, |
| @@ -555,43 +553,44 @@ ohci_alloc_std_chain(struct ohci_pipe *o | | | @@ -555,43 +553,44 @@ ohci_alloc_std_chain(struct ohci_pipe *o |
555 | cur->td.td_be = ~0; | | 553 | cur->td.td_be = ~0; |
556 | cur->len = 0; | | 554 | cur->len = 0; |
557 | cur->flags = 0; | | 555 | cur->flags = 0; |
558 | cur->xfer = xfer; | | 556 | cur->xfer = xfer; |
559 | usb_syncmem(&cur->dma, cur->offs, sizeof(cur->td), | | 557 | usb_syncmem(&cur->dma, cur->offs, sizeof(cur->td), |
560 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 558 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
561 | DPRINTFN(2,("ohci_alloc_std_chain: add 0 xfer\n")); | | 559 | DPRINTFN(2,("ohci_alloc_std_chain: add 0 xfer\n")); |
562 | } | | 560 | } |
563 | *ep = cur; | | 561 | *ep = cur; |
564 | | | 562 | |
565 | return (USBD_NORMAL_COMPLETION); | | 563 | return (USBD_NORMAL_COMPLETION); |
566 | | | 564 | |
567 | nomem: | | 565 | nomem: |
568 | /* XXX free chain */ | | 566 | |
| | | 567 | /* Don't free sp - let the caller do that */ |
| | | 568 | ohci_free_std_chain(sc, sp->nexttd, NULL); |
| | | 569 | |
569 | return (USBD_NOMEM); | | 570 | return (USBD_NOMEM); |
570 | } | | 571 | } |
571 | | | 572 | |
572 | #if 0 | | | |
573 | Static void | | 573 | Static void |
574 | ohci_free_std_chain(ohci_softc_t *sc, ohci_soft_td_t *std, | | 574 | ohci_free_std_chain(ohci_softc_t *sc, ohci_soft_td_t *std, |
575 | ohci_soft_td_t *stdend) | | 575 | ohci_soft_td_t *stdend) |
576 | { | | 576 | { |
577 | ohci_soft_td_t *p; | | 577 | ohci_soft_td_t *p; |
578 | | | 578 | |
579 | for (; std != stdend; std = p) { | | 579 | for (; std != stdend; std = p) { |
580 | p = std->nexttd; | | 580 | p = std->nexttd; |
581 | ohci_free_std(sc, std); | | 581 | ohci_free_std(sc, std); |
582 | } | | 582 | } |
583 | } | | 583 | } |
584 | #endif | | | |
585 | | | 584 | |
586 | ohci_soft_itd_t * | | 585 | ohci_soft_itd_t * |
587 | ohci_alloc_sitd(ohci_softc_t *sc) | | 586 | ohci_alloc_sitd(ohci_softc_t *sc) |
588 | { | | 587 | { |
589 | ohci_soft_itd_t *sitd; | | 588 | ohci_soft_itd_t *sitd; |
590 | usbd_status err; | | 589 | usbd_status err; |
591 | int i, offs; | | 590 | int i, offs; |
592 | usb_dma_t dma; | | 591 | usb_dma_t dma; |
593 | | | 592 | |
594 | if (sc->sc_freeitds == NULL) { | | 593 | if (sc->sc_freeitds == NULL) { |
595 | DPRINTFN(2, ("ohci_alloc_sitd: allocating chunk\n")); | | 594 | DPRINTFN(2, ("ohci_alloc_sitd: allocating chunk\n")); |
596 | err = usb_allocmem(&sc->sc_bus, OHCI_SITD_SIZE * OHCI_SITD_CHUNK, | | 595 | err = usb_allocmem(&sc->sc_bus, OHCI_SITD_SIZE * OHCI_SITD_CHUNK, |
597 | OHCI_ITD_ALIGN, &dma); | | 596 | OHCI_ITD_ALIGN, &dma); |
| @@ -1747,29 +1746,32 @@ ohci_device_request(usbd_xfer_handle xfe | | | @@ -1747,29 +1746,32 @@ ohci_device_request(usbd_xfer_handle xfe |
1747 | OHCI_ED_SET_MAXP(UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize))); | | 1746 | OHCI_ED_SET_MAXP(UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize))); |
1748 | usb_syncmem(&sed->dma, sed->offs + offsetof(ohci_ed_t, ed_flags), | | 1747 | usb_syncmem(&sed->dma, sed->offs + offsetof(ohci_ed_t, ed_flags), |
1749 | sizeof(sed->ed.ed_flags), | | 1748 | sizeof(sed->ed.ed_flags), |
1750 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 1749 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
1751 | | | 1750 | |
1752 | next = stat; | | 1751 | next = stat; |
1753 | | | 1752 | |
1754 | /* Set up data transaction */ | | 1753 | /* Set up data transaction */ |
1755 | if (len != 0) { | | 1754 | if (len != 0) { |
1756 | ohci_soft_td_t *std = stat; | | 1755 | ohci_soft_td_t *std = stat; |
1757 | | | 1756 | |
1758 | err = ohci_alloc_std_chain(opipe, sc, len, isread, xfer, | | 1757 | err = ohci_alloc_std_chain(opipe, sc, len, isread, xfer, |
1759 | std, &stat); | | 1758 | std, &stat); |
1760 | stat = stat->nexttd; /* point at free TD */ | | 1759 | if (err) { |
1761 | if (err) | | 1760 | /* stat is unchanged if error */ |
1762 | goto bad3; | | 1761 | goto bad3; |
| | | 1762 | } |
| | | 1763 | stat = stat->nexttd; /* point at free TD */ |
| | | 1764 | |
1763 | /* Start toggle at 1 and then use the carried toggle. */ | | 1765 | /* Start toggle at 1 and then use the carried toggle. */ |
1764 | std->td.td_flags &= HTOO32(~OHCI_TD_TOGGLE_MASK); | | 1766 | std->td.td_flags &= HTOO32(~OHCI_TD_TOGGLE_MASK); |
1765 | std->td.td_flags |= HTOO32(OHCI_TD_TOGGLE_1); | | 1767 | std->td.td_flags |= HTOO32(OHCI_TD_TOGGLE_1); |
1766 | usb_syncmem(&std->dma, | | 1768 | usb_syncmem(&std->dma, |
1767 | std->offs + offsetof(ohci_td_t, td_flags), | | 1769 | std->offs + offsetof(ohci_td_t, td_flags), |
1768 | sizeof(std->td.td_flags), | | 1770 | sizeof(std->td.td_flags), |
1769 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 1771 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
1770 | } | | 1772 | } |
1771 | | | 1773 | |
1772 | memcpy(KERNADDR(&opipe->u.ctl.reqdma, 0), req, sizeof *req); | | 1774 | memcpy(KERNADDR(&opipe->u.ctl.reqdma, 0), req, sizeof *req); |
1773 | usb_syncmem(&opipe->u.ctl.reqdma, 0, sizeof *req, BUS_DMASYNC_PREWRITE); | | 1775 | usb_syncmem(&opipe->u.ctl.reqdma, 0, sizeof *req, BUS_DMASYNC_PREWRITE); |
1774 | | | 1776 | |
1775 | setup->td.td_flags = HTOO32(OHCI_TD_SETUP | OHCI_TD_NOCC | | | 1777 | setup->td.td_flags = HTOO32(OHCI_TD_SETUP | OHCI_TD_NOCC | |
| @@ -3034,26 +3036,29 @@ ohci_device_bulk_start(usbd_xfer_handle | | | @@ -3034,26 +3036,29 @@ ohci_device_bulk_start(usbd_xfer_handle |
3034 | opipe->u.bulk.length = len; | | 3036 | opipe->u.bulk.length = len; |
3035 | | | 3037 | |
3036 | usb_syncmem(&sed->dma, sed->offs, sizeof(sed->ed), | | 3038 | usb_syncmem(&sed->dma, sed->offs, sizeof(sed->ed), |
3037 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); | | 3039 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
3038 | /* Update device address */ | | 3040 | /* Update device address */ |
3039 | sed->ed.ed_flags = HTOO32( | | 3041 | sed->ed.ed_flags = HTOO32( |
3040 | (O32TOH(sed->ed.ed_flags) & ~OHCI_ED_ADDRMASK) | | | 3042 | (O32TOH(sed->ed.ed_flags) & ~OHCI_ED_ADDRMASK) | |
3041 | OHCI_ED_SET_FA(addr)); | | 3043 | OHCI_ED_SET_FA(addr)); |
3042 | | | 3044 | |
3043 | /* Allocate a chain of new TDs (including a new tail). */ | | 3045 | /* Allocate a chain of new TDs (including a new tail). */ |
3044 | data = opipe->tail.td; | | 3046 | data = opipe->tail.td; |
3045 | err = ohci_alloc_std_chain(opipe, sc, len, isread, xfer, | | 3047 | err = ohci_alloc_std_chain(opipe, sc, len, isread, xfer, |
3046 | data, &tail); | | 3048 | data, &tail); |
| | | 3049 | if (err) |
| | | 3050 | return err; |
| | | 3051 | |
3047 | /* We want interrupt at the end of the transfer. */ | | 3052 | /* We want interrupt at the end of the transfer. */ |
3048 | tail->td.td_flags &= HTOO32(~OHCI_TD_INTR_MASK); | | 3053 | tail->td.td_flags &= HTOO32(~OHCI_TD_INTR_MASK); |
3049 | tail->td.td_flags |= HTOO32(OHCI_TD_SET_DI(1)); | | 3054 | tail->td.td_flags |= HTOO32(OHCI_TD_SET_DI(1)); |
3050 | tail->flags |= OHCI_CALL_DONE; | | 3055 | tail->flags |= OHCI_CALL_DONE; |
3051 | tail = tail->nexttd; /* point at sentinel */ | | 3056 | tail = tail->nexttd; /* point at sentinel */ |
3052 | usb_syncmem(&tail->dma, tail->offs + offsetof(ohci_td_t, td_flags), | | 3057 | usb_syncmem(&tail->dma, tail->offs + offsetof(ohci_td_t, td_flags), |
3053 | sizeof(tail->td.td_flags), | | 3058 | sizeof(tail->td.td_flags), |
3054 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 3059 | BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
3055 | if (err) { | | 3060 | if (err) { |
3056 | mutex_exit(&sc->sc_lock); | | 3061 | mutex_exit(&sc->sc_lock); |
3057 | return (err); | | 3062 | return (err); |
3058 | } | | 3063 | } |
3059 | | | 3064 | |