Index: head/sys/dev/usb/if_cue.c =================================================================== --- head/sys/dev/usb/if_cue.c (revision 169488) +++ head/sys/dev/usb/if_cue.c (revision 169489) @@ -1,1104 +1,1079 @@ /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * CATC USB-EL1210A USB to ethernet driver. Used in the CATC Netmate * adapters and others. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The CATC USB-EL1210A provides USB ethernet support at 10Mbps. The * RX filter uses a 512-bit multicast hash table, single perfect entry * for the station address, and promiscuous mode. Unlike the ADMtek * and KLSI chips, the CATC ASIC supports read and write combining * mode where multiple packets can be transfered using a single bulk * transaction, which helps performance a great deal. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#if __FreeBSD_version < 500000 -#include -#endif #include #include #include #include #include "usbdevs.h" #include #include /* * Various supported device vendors/products. */ static struct cue_type cue_devs[] = { { USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE }, { USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE2 }, { USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTLINK }, { 0, 0 } }; static int cue_match(device_t); static int cue_attach(device_t); static int cue_detach(device_t); static int cue_encap(struct cue_softc *, struct mbuf *, int); static void cue_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status); static void cue_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status); static void cue_tick(void *); static void cue_rxstart(struct ifnet *); static void cue_start(struct ifnet *); static int cue_ioctl(struct ifnet *, u_long, caddr_t); static void cue_init(void *); static void cue_stop(struct cue_softc *); static void cue_watchdog(struct ifnet *); static void cue_shutdown(device_t); static void cue_setmulti(struct cue_softc *); static uint32_t cue_mchash(const uint8_t *); static void cue_reset(struct cue_softc *); static int cue_csr_read_1(struct cue_softc *, int); static int cue_csr_write_1(struct cue_softc *, int, int); static int cue_csr_read_2(struct cue_softc *, int); #ifdef notdef static int cue_csr_write_2(struct cue_softc *, int, int); #endif static int cue_mem(struct cue_softc *, int, int, void *, int); static int cue_getmac(struct cue_softc *, void *); static device_method_t cue_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cue_match), DEVMETHOD(device_attach, cue_attach), DEVMETHOD(device_detach, cue_detach), DEVMETHOD(device_shutdown, cue_shutdown), { 0, 0 } }; static driver_t cue_driver = { "cue", cue_methods, sizeof(struct cue_softc) }; static devclass_t cue_devclass; DRIVER_MODULE(cue, uhub, cue_driver, cue_devclass, usbd_driver_load, 0); MODULE_DEPEND(cue, usb, 1, 1, 1); MODULE_DEPEND(cue, ether, 1, 1, 1); #define CUE_SETBIT(sc, reg, x) \ cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) | (x)) #define CUE_CLRBIT(sc, reg, x) \ cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) & ~(x)) static int cue_csr_read_1(struct cue_softc *sc, int reg) { usb_device_request_t req; usbd_status err; u_int8_t val = 0; if (sc->cue_dying) return(0); CUE_LOCK(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = CUE_CMD_READREG; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, 1); err = usbd_do_request(sc->cue_udev, &req, &val); CUE_UNLOCK(sc); if (err) return(0); return(val); } static int cue_csr_read_2(struct cue_softc *sc, int reg) { usb_device_request_t req; usbd_status err; u_int16_t val = 0; if (sc->cue_dying) return(0); CUE_LOCK(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = CUE_CMD_READREG; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, 2); err = usbd_do_request(sc->cue_udev, &req, &val); CUE_UNLOCK(sc); if (err) return(0); return(val); } static int cue_csr_write_1(struct cue_softc *sc, int reg, int val) { usb_device_request_t req; usbd_status err; if (sc->cue_dying) return(0); CUE_LOCK(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = CUE_CMD_WRITEREG; USETW(req.wValue, val); USETW(req.wIndex, reg); USETW(req.wLength, 0); err = usbd_do_request(sc->cue_udev, &req, NULL); CUE_UNLOCK(sc); if (err) return(-1); return(0); } #ifdef notdef static int cue_csr_write_2(struct cue_softc *sc, int reg, int val) { usb_device_request_t req; usbd_status err; if (sc->cue_dying) return(0); CUE_LOCK(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = CUE_CMD_WRITEREG; USETW(req.wValue, val); USETW(req.wIndex, reg); USETW(req.wLength, 0); err = usbd_do_request(sc->cue_udev, &req, NULL); CUE_UNLOCK(sc); if (err) return(-1); return(0); } #endif static int cue_mem(struct cue_softc *sc, int cmd, int addr, void *buf, int len) { usb_device_request_t req; usbd_status err; if (sc->cue_dying) return(0); CUE_LOCK(sc); if (cmd == CUE_CMD_READSRAM) req.bmRequestType = UT_READ_VENDOR_DEVICE; else req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = cmd; USETW(req.wValue, 0); USETW(req.wIndex, addr); USETW(req.wLength, len); err = usbd_do_request(sc->cue_udev, &req, buf); CUE_UNLOCK(sc); if (err) return(-1); return(0); } static int cue_getmac(struct cue_softc *sc, void *buf) { usb_device_request_t req; usbd_status err; if (sc->cue_dying) return(0); CUE_LOCK(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = CUE_CMD_GET_MACADDR; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, ETHER_ADDR_LEN); err = usbd_do_request(sc->cue_udev, &req, buf); CUE_UNLOCK(sc); if (err) { printf("cue%d: read MAC address failed\n", sc->cue_unit); return(-1); } return(0); } #define CUE_BITS 9 static uint32_t cue_mchash(const uint8_t *addr) { uint32_t crc; /* Compute CRC for the address value. */ crc = ether_crc32_le(addr, ETHER_ADDR_LEN); return (crc & ((1 << CUE_BITS) - 1)); } static void cue_setmulti(struct cue_softc *sc) { struct ifnet *ifp; struct ifmultiaddr *ifma; u_int32_t h = 0, i; ifp = sc->cue_ifp; if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { for (i = 0; i < CUE_MCAST_TABLE_LEN; i++) sc->cue_mctab[i] = 0xFF; cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, &sc->cue_mctab, CUE_MCAST_TABLE_LEN); return; } /* first, zot all the existing hash bits */ for (i = 0; i < CUE_MCAST_TABLE_LEN; i++) sc->cue_mctab[i] = 0; /* now program new ones */ IF_ADDR_LOCK(ifp); -#if __FreeBSD_version >= 500000 TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) -#else - LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) -#endif { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = cue_mchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); sc->cue_mctab[h >> 3] |= 1 << (h & 0x7); } IF_ADDR_UNLOCK(ifp); /* * Also include the broadcast address in the filter * so we can receive broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) { -#if __FreeBSD_version >= 500000 h = cue_mchash(ifp->if_broadcastaddr); -#else - h = cue_mchash(etherbroadcastaddr); -#endif sc->cue_mctab[h >> 3] |= 1 << (h & 0x7); } cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, &sc->cue_mctab, CUE_MCAST_TABLE_LEN); return; } static void cue_reset(struct cue_softc *sc) { usb_device_request_t req; usbd_status err; if (sc->cue_dying) return; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = CUE_CMD_RESET; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, 0); err = usbd_do_request(sc->cue_udev, &req, NULL); if (err) printf("cue%d: reset failed\n", sc->cue_unit); /* Wait a little while for the chip to get its brains in order. */ DELAY(1000); return; } /* * Probe for a Pegasus chip. */ USB_MATCH(cue) { USB_MATCH_START(cue, uaa); struct cue_type *t; if (!uaa->iface) return(UMATCH_NONE); t = cue_devs; while(t->cue_vid) { if (uaa->vendor == t->cue_vid && uaa->product == t->cue_did) { return(UMATCH_VENDOR_PRODUCT); } t++; } return(UMATCH_NONE); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ USB_ATTACH(cue) { USB_ATTACH_START(cue, sc, uaa); char devinfo[1024]; u_char eaddr[ETHER_ADDR_LEN]; struct ifnet *ifp; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; int i; bzero(sc, sizeof(struct cue_softc)); sc->cue_dev = self; sc->cue_iface = uaa->iface; sc->cue_udev = uaa->device; sc->cue_unit = device_get_unit(self); if (usbd_set_config_no(sc->cue_udev, CUE_CONFIG_NO, 0)) { printf("cue%d: getting interface handle failed\n", sc->cue_unit); USB_ATTACH_ERROR_RETURN; } id = usbd_get_interface_descriptor(uaa->iface); usbd_devinfo(uaa->device, 0, devinfo); device_set_desc_copy(self, devinfo); printf("%s: %s\n", device_get_nameunit(self), devinfo); /* Find endpoints. */ for (i = 0; i < id->bNumEndpoints; i++) { ed = usbd_interface2endpoint_descriptor(uaa->iface, i); if (!ed) { printf("cue%d: couldn't get ep %d\n", sc->cue_unit, i); USB_ATTACH_ERROR_RETURN; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { sc->cue_ed[CUE_ENDPT_RX] = ed->bEndpointAddress; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { sc->cue_ed[CUE_ENDPT_TX] = ed->bEndpointAddress; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { sc->cue_ed[CUE_ENDPT_INTR] = ed->bEndpointAddress; } } -#if __FreeBSD_version >= 500000 mtx_init(&sc->cue_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); -#endif CUE_LOCK(sc); #ifdef notdef /* Reset the adapter. */ cue_reset(sc); #endif /* * Get station address. */ cue_getmac(sc, &eaddr); ifp = sc->cue_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { printf("cue%d: can not if_alloc()\n", sc->cue_unit); CUE_UNLOCK(sc); -#if __FreeBSD_version >= 500000 mtx_destroy(&sc->cue_mtx); -#endif USB_ATTACH_ERROR_RETURN; } ifp->if_softc = sc; if_initname(ifp, "cue", sc->cue_unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_NEEDSGIANT; ifp->if_ioctl = cue_ioctl; ifp->if_start = cue_start; ifp->if_watchdog = cue_watchdog; ifp->if_init = cue_init; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; sc->cue_qdat.ifp = ifp; sc->cue_qdat.if_rxstart = cue_rxstart; /* * Call MI attach routine. */ -#if __FreeBSD_version >= 500000 ether_ifattach(ifp, eaddr); -#else - ether_ifattach(ifp, ETHER_BPF_SUPPORTED); -#endif callout_handle_init(&sc->cue_stat_ch); usb_register_netisr(); sc->cue_dying = 0; CUE_UNLOCK(sc); USB_ATTACH_SUCCESS_RETURN; } static int cue_detach(device_t dev) { struct cue_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); CUE_LOCK(sc); ifp = sc->cue_ifp; sc->cue_dying = 1; untimeout(cue_tick, sc, sc->cue_stat_ch); -#if __FreeBSD_version >= 500000 ether_ifdetach(ifp); if_free(ifp); -#else - ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); -#endif if (sc->cue_ep[CUE_ENDPT_TX] != NULL) usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_TX]); if (sc->cue_ep[CUE_ENDPT_RX] != NULL) usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_RX]); if (sc->cue_ep[CUE_ENDPT_INTR] != NULL) usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_INTR]); CUE_UNLOCK(sc); -#if __FreeBSD_version >= 500000 mtx_destroy(&sc->cue_mtx); -#endif return(0); } static void cue_rxstart(struct ifnet *ifp) { struct cue_softc *sc; struct ue_chain *c; sc = ifp->if_softc; CUE_LOCK(sc); c = &sc->cue_cdata.ue_rx_chain[sc->cue_cdata.ue_rx_prod]; c->ue_mbuf = usb_ether_newbuf(); if (c->ue_mbuf == NULL) { printf("%s: no memory for rx list " "-- packet dropped!\n", device_get_nameunit(sc->cue_dev)); ifp->if_ierrors++; CUE_UNLOCK(sc); return; } /* Setup new transfer. */ usbd_setup_xfer(c->ue_xfer, sc->cue_ep[CUE_ENDPT_RX], c, mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, cue_rxeof); usbd_transfer(c->ue_xfer); CUE_UNLOCK(sc); return; } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */ static void cue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct cue_softc *sc; struct ue_chain *c; struct mbuf *m; struct ifnet *ifp; int total_len = 0; u_int16_t len; c = priv; sc = c->ue_sc; CUE_LOCK(sc); ifp = sc->cue_ifp; if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { CUE_UNLOCK(sc); return; } if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { CUE_UNLOCK(sc); return; } if (usbd_ratecheck(&sc->cue_rx_notice)) printf("cue%d: usb error on rx: %s\n", sc->cue_unit, usbd_errstr(status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall(sc->cue_ep[CUE_ENDPT_RX]); goto done; } usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); m = c->ue_mbuf; len = *mtod(m, u_int16_t *); /* No errors; receive the packet. */ total_len = len; if (len < sizeof(struct ether_header)) { ifp->if_ierrors++; goto done; } ifp->if_ipackets++; m_adj(m, sizeof(u_int16_t)); m->m_pkthdr.rcvif = (void *)&sc->cue_qdat; m->m_pkthdr.len = m->m_len = total_len; /* Put the packet on the special USB input queue. */ usb_ether_input(m); CUE_UNLOCK(sc); return; done: /* Setup new transfer. */ usbd_setup_xfer(c->ue_xfer, sc->cue_ep[CUE_ENDPT_RX], c, mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, cue_rxeof); usbd_transfer(c->ue_xfer); CUE_UNLOCK(sc); return; } /* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void cue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct cue_softc *sc; struct ue_chain *c; struct ifnet *ifp; usbd_status err; c = priv; sc = c->ue_sc; CUE_LOCK(sc); ifp = sc->cue_ifp; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { CUE_UNLOCK(sc); return; } printf("cue%d: usb error on tx: %s\n", sc->cue_unit, usbd_errstr(status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall(sc->cue_ep[CUE_ENDPT_TX]); CUE_UNLOCK(sc); return; } ifp->if_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &err); if (c->ue_mbuf != NULL) { c->ue_mbuf->m_pkthdr.rcvif = ifp; usb_tx_done(c->ue_mbuf); c->ue_mbuf = NULL; } if (err) ifp->if_oerrors++; else ifp->if_opackets++; CUE_UNLOCK(sc); return; } static void cue_tick(void *xsc) { struct cue_softc *sc; struct ifnet *ifp; sc = xsc; if (sc == NULL) return; CUE_LOCK(sc); ifp = sc->cue_ifp; ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_SINGLECOLL); ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_MULTICOLL); ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_EXCESSCOLL); if (cue_csr_read_2(sc, CUE_RX_FRAMEERR)) ifp->if_ierrors++; sc->cue_stat_ch = timeout(cue_tick, sc, hz); CUE_UNLOCK(sc); return; } static int cue_encap(struct cue_softc *sc, struct mbuf *m, int idx) { int total_len; struct ue_chain *c; usbd_status err; c = &sc->cue_cdata.ue_tx_chain[idx]; /* * Copy the mbuf data into a contiguous buffer, leaving two * bytes at the beginning to hold the frame length. */ m_copydata(m, 0, m->m_pkthdr.len, c->ue_buf + 2); c->ue_mbuf = m; total_len = m->m_pkthdr.len + 2; /* The first two bytes are the frame length */ c->ue_buf[0] = (u_int8_t)m->m_pkthdr.len; c->ue_buf[1] = (u_int8_t)(m->m_pkthdr.len >> 8); usbd_setup_xfer(c->ue_xfer, sc->cue_ep[CUE_ENDPT_TX], c, c->ue_buf, total_len, 0, 10000, cue_txeof); /* Transmit */ err = usbd_transfer(c->ue_xfer); if (err != USBD_IN_PROGRESS) { cue_stop(sc); return(EIO); } sc->cue_cdata.ue_tx_cnt++; return(0); } static void cue_start(struct ifnet *ifp) { struct cue_softc *sc; struct mbuf *m_head = NULL; sc = ifp->if_softc; CUE_LOCK(sc); if (ifp->if_drv_flags & IFF_DRV_OACTIVE) { CUE_UNLOCK(sc); return; } IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) { CUE_UNLOCK(sc); return; } if (cue_encap(sc, m_head, 0)) { IF_PREPEND(&ifp->if_snd, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; CUE_UNLOCK(sc); return; } /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; /* * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; CUE_UNLOCK(sc); return; } static void cue_init(void *xsc) { struct cue_softc *sc = xsc; struct ifnet *ifp = sc->cue_ifp; struct ue_chain *c; usbd_status err; int i; if (ifp->if_drv_flags & IFF_DRV_RUNNING) return; CUE_LOCK(sc); /* * Cancel pending I/O and free all RX/TX buffers. */ #ifdef foo cue_reset(sc); #endif /* Set MAC address */ for (i = 0; i < ETHER_ADDR_LEN; i++) cue_csr_write_1(sc, CUE_PAR0 - i, IF_LLADDR(sc->cue_ifp)[i]); /* Enable RX logic. */ cue_csr_write_1(sc, CUE_ETHCTL, CUE_ETHCTL_RX_ON|CUE_ETHCTL_MCAST_ON); /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) { CUE_SETBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); } else { CUE_CLRBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); } /* Init TX ring. */ if (usb_ether_tx_list_init(sc, &sc->cue_cdata, sc->cue_udev) == ENOBUFS) { printf("cue%d: tx list init failed\n", sc->cue_unit); CUE_UNLOCK(sc); return; } /* Init RX ring. */ if (usb_ether_rx_list_init(sc, &sc->cue_cdata, sc->cue_udev) == ENOBUFS) { printf("cue%d: rx list init failed\n", sc->cue_unit); CUE_UNLOCK(sc); return; } /* Load the multicast filter. */ cue_setmulti(sc); /* * Set the number of RX and TX buffers that we want * to reserve inside the ASIC. */ cue_csr_write_1(sc, CUE_RX_BUFPKTS, CUE_RX_FRAMES); cue_csr_write_1(sc, CUE_TX_BUFPKTS, CUE_TX_FRAMES); /* Set advanced operation modes. */ cue_csr_write_1(sc, CUE_ADVANCED_OPMODES, CUE_AOP_EMBED_RXLEN|0x01); /* 1 wait state */ /* Program the LED operation. */ cue_csr_write_1(sc, CUE_LEDCTL, CUE_LEDCTL_FOLLOW_LINK); /* Open RX and TX pipes. */ err = usbd_open_pipe(sc->cue_iface, sc->cue_ed[CUE_ENDPT_RX], USBD_EXCLUSIVE_USE, &sc->cue_ep[CUE_ENDPT_RX]); if (err) { printf("cue%d: open rx pipe failed: %s\n", sc->cue_unit, usbd_errstr(err)); CUE_UNLOCK(sc); return; } err = usbd_open_pipe(sc->cue_iface, sc->cue_ed[CUE_ENDPT_TX], USBD_EXCLUSIVE_USE, &sc->cue_ep[CUE_ENDPT_TX]); if (err) { printf("cue%d: open tx pipe failed: %s\n", sc->cue_unit, usbd_errstr(err)); CUE_UNLOCK(sc); return; } /* Start up the receive pipe. */ for (i = 0; i < UE_RX_LIST_CNT; i++) { c = &sc->cue_cdata.ue_rx_chain[i]; usbd_setup_xfer(c->ue_xfer, sc->cue_ep[CUE_ENDPT_RX], c, mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, cue_rxeof); usbd_transfer(c->ue_xfer); } ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; CUE_UNLOCK(sc); sc->cue_stat_ch = timeout(cue_tick, sc, hz); return; } static int cue_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct cue_softc *sc = ifp->if_softc; int error = 0; CUE_LOCK(sc); switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING && ifp->if_flags & IFF_PROMISC && !(sc->cue_if_flags & IFF_PROMISC)) { CUE_SETBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); cue_setmulti(sc); } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && !(ifp->if_flags & IFF_PROMISC) && sc->cue_if_flags & IFF_PROMISC) { CUE_CLRBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); cue_setmulti(sc); } else if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) cue_init(sc); } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) cue_stop(sc); } sc->cue_if_flags = ifp->if_flags; error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: cue_setmulti(sc); error = 0; break; default: error = ether_ioctl(ifp, command, data); break; } CUE_UNLOCK(sc); return(error); } static void cue_watchdog(struct ifnet *ifp) { struct cue_softc *sc; struct ue_chain *c; usbd_status stat; sc = ifp->if_softc; CUE_LOCK(sc); ifp->if_oerrors++; printf("cue%d: watchdog timeout\n", sc->cue_unit); c = &sc->cue_cdata.ue_tx_chain[0]; usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &stat); cue_txeof(c->ue_xfer, c, stat); if (ifp->if_snd.ifq_head != NULL) cue_start(ifp); CUE_UNLOCK(sc); return; } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void cue_stop(struct cue_softc *sc) { usbd_status err; struct ifnet *ifp; CUE_LOCK(sc); ifp = sc->cue_ifp; ifp->if_timer = 0; cue_csr_write_1(sc, CUE_ETHCTL, 0); cue_reset(sc); untimeout(cue_tick, sc, sc->cue_stat_ch); /* Stop transfers. */ if (sc->cue_ep[CUE_ENDPT_RX] != NULL) { err = usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_RX]); if (err) { printf("cue%d: abort rx pipe failed: %s\n", sc->cue_unit, usbd_errstr(err)); } err = usbd_close_pipe(sc->cue_ep[CUE_ENDPT_RX]); if (err) { printf("cue%d: close rx pipe failed: %s\n", sc->cue_unit, usbd_errstr(err)); } sc->cue_ep[CUE_ENDPT_RX] = NULL; } if (sc->cue_ep[CUE_ENDPT_TX] != NULL) { err = usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_TX]); if (err) { printf("cue%d: abort tx pipe failed: %s\n", sc->cue_unit, usbd_errstr(err)); } err = usbd_close_pipe(sc->cue_ep[CUE_ENDPT_TX]); if (err) { printf("cue%d: close tx pipe failed: %s\n", sc->cue_unit, usbd_errstr(err)); } sc->cue_ep[CUE_ENDPT_TX] = NULL; } if (sc->cue_ep[CUE_ENDPT_INTR] != NULL) { err = usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_INTR]); if (err) { printf("cue%d: abort intr pipe failed: %s\n", sc->cue_unit, usbd_errstr(err)); } err = usbd_close_pipe(sc->cue_ep[CUE_ENDPT_INTR]); if (err) { printf("cue%d: close intr pipe failed: %s\n", sc->cue_unit, usbd_errstr(err)); } sc->cue_ep[CUE_ENDPT_INTR] = NULL; } /* Free RX resources. */ usb_ether_rx_list_free(&sc->cue_cdata); /* Free TX resources. */ usb_ether_tx_list_free(&sc->cue_cdata); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); CUE_UNLOCK(sc); return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static void cue_shutdown(device_t dev) { struct cue_softc *sc; sc = device_get_softc(dev); CUE_LOCK(sc); cue_reset(sc); cue_stop(sc); CUE_UNLOCK(sc); return; } Index: head/sys/dev/usb/if_cuereg.h =================================================================== --- head/sys/dev/usb/if_cuereg.h (revision 169488) +++ head/sys/dev/usb/if_cuereg.h (revision 169489) @@ -1,171 +1,169 @@ /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* * Definitions for the CATC Netmate II USB to ethernet controller. */ /* * Vendor specific control commands. */ #define CUE_CMD_RESET 0xF4 #define CUE_CMD_GET_MACADDR 0xF2 #define CUE_CMD_WRITEREG 0xFA #define CUE_CMD_READREG 0xFB #define CUE_CMD_READSRAM 0xF1 #define CUE_CMD_WRITESRAM 0xFC /* * Internal registers */ #define CUE_TX_BUFCNT 0x20 #define CUE_RX_BUFCNT 0x21 #define CUE_ADVANCED_OPMODES 0x22 #define CUE_TX_BUFPKTS 0x23 #define CUE_RX_BUFPKTS 0x24 #define CUE_RX_MAXCHAIN 0x25 #define CUE_ETHCTL 0x60 #define CUE_ETHSTS 0x61 #define CUE_PAR5 0x62 #define CUE_PAR4 0x63 #define CUE_PAR3 0x64 #define CUE_PAR2 0x65 #define CUE_PAR1 0x66 #define CUE_PAR0 0x67 /* Error counters, all 16 bits wide. */ #define CUE_TX_SINGLECOLL 0x69 #define CUE_TX_MULTICOLL 0x6B #define CUE_TX_EXCESSCOLL 0x6D #define CUE_RX_FRAMEERR 0x6F #define CUE_LEDCTL 0x81 /* Advenced operating mode register */ #define CUE_AOP_SRAMWAITS 0x03 #define CUE_AOP_EMBED_RXLEN 0x08 #define CUE_AOP_RXCOMBINE 0x10 #define CUE_AOP_TXCOMBINE 0x20 #define CUE_AOP_EVEN_PKT_READS 0x40 #define CUE_AOP_LOOPBK 0x80 /* Ethernet control register */ #define CUE_ETHCTL_RX_ON 0x01 #define CUE_ETHCTL_LINK_POLARITY 0x02 #define CUE_ETHCTL_LINK_FORCE_OK 0x04 #define CUE_ETHCTL_MCAST_ON 0x08 #define CUE_ETHCTL_PROMISC 0x10 /* Ethernet status register */ #define CUE_ETHSTS_NO_CARRIER 0x01 #define CUE_ETHSTS_LATECOLL 0x02 #define CUE_ETHSTS_EXCESSCOLL 0x04 #define CUE_ETHSTS_TXBUF_AVAIL 0x08 #define CUE_ETHSTS_BAD_POLARITY 0x10 #define CUE_ETHSTS_LINK_OK 0x20 /* LED control register */ #define CUE_LEDCTL_BLINK_1X 0x00 #define CUE_LEDCTL_BLINK_2X 0x01 #define CUE_LEDCTL_BLINK_QUARTER_ON 0x02 #define CUE_LEDCTL_BLINK_QUARTER_OFF 0x03 #define CUE_LEDCTL_OFF 0x04 #define CUE_LEDCTL_FOLLOW_LINK 0x08 /* * Address in ASIC's internal SRAM where the * multicast hash table lives. The table is 64 bytes long, * giving us a 512-bit table. We have to set the bit that * corresponds to the broadcast address in order to enable * reception of broadcast frames. */ #define CUE_MCAST_TABLE_ADDR 0xFA80 #define CUE_MCAST_TABLE_LEN 64 #define CUE_TIMEOUT 1000 #define CUE_MIN_FRAMELEN 60 #define CUE_RX_FRAMES 1 #define CUE_TX_FRAMES 1 #define CUE_CTL_READ 0x01 #define CUE_CTL_WRITE 0x02 #define CUE_CONFIG_NO 1 /* * The interrupt endpoint is currently unused * by the KLSI part. */ #define CUE_ENDPT_RX 0x0 #define CUE_ENDPT_TX 0x1 #define CUE_ENDPT_INTR 0x2 #define CUE_ENDPT_MAX 0x3 struct cue_type { u_int16_t cue_vid; u_int16_t cue_did; }; #define CUE_INC(x, y) (x) = (x + 1) % y struct cue_softc { struct ifnet *cue_ifp; device_t cue_dev; usbd_device_handle cue_udev; usbd_interface_handle cue_iface; int cue_ed[CUE_ENDPT_MAX]; usbd_pipe_handle cue_ep[CUE_ENDPT_MAX]; int cue_unit; u_int8_t cue_mctab[CUE_MCAST_TABLE_LEN]; int cue_if_flags; u_int16_t cue_rxfilt; struct ue_cdata cue_cdata; struct callout_handle cue_stat_ch; -#if __FreeBSD_version >= 500000 struct mtx cue_mtx; -#endif char cue_dying; struct timeval cue_rx_notice; struct usb_qdat cue_qdat; }; #if 0 #define CUE_LOCK(_sc) mtx_lock(&(_sc)->cue_mtx) #define CUE_UNLOCK(_sc) mtx_unlock(&(_sc)->cue_mtx) #else #define CUE_LOCK(_sc) #define CUE_UNLOCK(_sc) #endif Index: head/sys/dev/usb/if_kue.c =================================================================== --- head/sys/dev/usb/if_kue.c (revision 169488) +++ head/sys/dev/usb/if_kue.c (revision 169489) @@ -1,1040 +1,1017 @@ /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Kawasaki LSI KL5KUSB101B USB to ethernet adapter driver. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The KLSI USB to ethernet adapter chip contains an USB serial interface, * ethernet MAC and embedded microcontroller (called the QT Engine). * The chip must have firmware loaded into it before it will operate. * Packets are passed between the chip and host via bulk transfers. * There is an interrupt endpoint mentioned in the software spec, however * it's currently unused. This device is 10Mbps half-duplex only, hence * there is no media selection logic. The MAC supports a 128 entry * multicast filter, though the exact size of the filter can depend * on the firmware. Curiously, while the software spec describes various * ethernet statistics counters, my sample adapter and firmware combination * claims not to support any statistics counters at all. * * Note that once we load the firmware in the device, we have to be * careful not to load it again: if you restart your computer but * leave the adapter attached to the USB controller, it may remain * powered on and retain its firmware. In this case, we don't need * to load the firmware a second time. * * Special thanks to Rob Furr for providing an ADS Technologies * adapter for development and testing. No monkeys were harmed during * the development of this driver. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#if __FreeBSD_version < 500000 -#include -#endif #include #include #include #include #include "usbdevs.h" #include #include #include MODULE_DEPEND(kue, usb, 1, 1, 1); MODULE_DEPEND(kue, ether, 1, 1, 1); /* * Various supported device vendors/products. */ static struct kue_type kue_devs[] = { { USB_VENDOR_AOX, USB_PRODUCT_AOX_USB101 }, { USB_VENDOR_KLSI, USB_PRODUCT_AOX_USB101 }, { USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BT }, { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC10T }, { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_EA101 }, { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET }, { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET2 }, { USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_E45 }, { USB_VENDOR_3COM, USB_PRODUCT_3COM_3C19250 }, { USB_VENDOR_COREGA, USB_PRODUCT_COREGA_ETHER_USB_T }, { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650C }, { USB_VENDOR_SMC, USB_PRODUCT_SMC_2102USB }, { USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T }, { USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BT }, { USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BTN }, { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET3 }, { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETT }, { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_URE450 }, { USB_VENDOR_SILICOM, USB_PRODUCT_SILICOM_GPE }, { 0, 0 } }; static int kue_match(device_t); static int kue_attach(device_t); static int kue_detach(device_t); static void kue_shutdown(device_t); static int kue_encap(struct kue_softc *, struct mbuf *, int); static void kue_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status); static void kue_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status); static void kue_start(struct ifnet *); static void kue_rxstart(struct ifnet *); static int kue_ioctl(struct ifnet *, u_long, caddr_t); static void kue_init(void *); static void kue_stop(struct kue_softc *); static void kue_watchdog(struct ifnet *); static void kue_setmulti(struct kue_softc *); static void kue_reset(struct kue_softc *); static usbd_status kue_do_request(usbd_device_handle, usb_device_request_t *, void *); static usbd_status kue_ctl(struct kue_softc *, int, u_int8_t, u_int16_t, char *, int); static usbd_status kue_setword(struct kue_softc *, u_int8_t, u_int16_t); static int kue_load_fw(struct kue_softc *); static device_method_t kue_methods[] = { /* Device interface */ DEVMETHOD(device_probe, kue_match), DEVMETHOD(device_attach, kue_attach), DEVMETHOD(device_detach, kue_detach), DEVMETHOD(device_shutdown, kue_shutdown), { 0, 0 } }; static driver_t kue_driver = { "kue", kue_methods, sizeof(struct kue_softc) }; static devclass_t kue_devclass; DRIVER_MODULE(kue, uhub, kue_driver, kue_devclass, usbd_driver_load, 0); /* * We have a custom do_request function which is almost like the * regular do_request function, except it has a much longer timeout. * Why? Because we need to make requests over the control endpoint * to download the firmware to the device, which can take longer * than the default timeout. */ static usbd_status kue_do_request(usbd_device_handle dev, usb_device_request_t *req, void *data) { usbd_xfer_handle xfer; usbd_status err; xfer = usbd_alloc_xfer(dev); usbd_setup_default_xfer(xfer, dev, 0, 500000, req, data, UGETW(req->wLength), USBD_SHORT_XFER_OK, 0); err = usbd_sync_transfer(xfer); usbd_free_xfer(xfer); return(err); } static usbd_status kue_setword(struct kue_softc *sc, u_int8_t breq, u_int16_t word) { usbd_device_handle dev; usb_device_request_t req; usbd_status err; if (sc->kue_dying) return(USBD_NORMAL_COMPLETION); dev = sc->kue_udev; KUE_LOCK(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = breq; USETW(req.wValue, word); USETW(req.wIndex, 0); USETW(req.wLength, 0); err = kue_do_request(dev, &req, NULL); KUE_UNLOCK(sc); return(err); } static usbd_status kue_ctl(struct kue_softc *sc, int rw, u_int8_t breq, u_int16_t val, char *data, int len) { usbd_device_handle dev; usb_device_request_t req; usbd_status err; dev = sc->kue_udev; if (sc->kue_dying) return(USBD_NORMAL_COMPLETION); KUE_LOCK(sc); if (rw == KUE_CTL_WRITE) req.bmRequestType = UT_WRITE_VENDOR_DEVICE; else req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = breq; USETW(req.wValue, val); USETW(req.wIndex, 0); USETW(req.wLength, len); err = kue_do_request(dev, &req, data); KUE_UNLOCK(sc); return(err); } static int kue_load_fw(struct kue_softc *sc) { usbd_status err; usb_device_descriptor_t *dd; int hwrev; dd = &sc->kue_udev->ddesc; hwrev = UGETW(dd->bcdDevice); /* * First, check if we even need to load the firmware. * If the device was still attached when the system was * rebooted, it may already have firmware loaded in it. * If this is the case, we don't need to do it again. * And in fact, if we try to load it again, we'll hang, * so we have to avoid this condition if we don't want * to look stupid. * * We can test this quickly by checking the bcdRevision * code. The NIC will return a different revision code if * it's probed while the firmware is still loaded and * running. */ if (hwrev == 0x0202) return(0); /* Load code segment */ err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, 0, kue_code_seg, sizeof(kue_code_seg)); if (err) { printf("kue%d: failed to load code segment: %s\n", sc->kue_unit, usbd_errstr(err)); return(ENXIO); } /* Load fixup segment */ err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, 0, kue_fix_seg, sizeof(kue_fix_seg)); if (err) { printf("kue%d: failed to load fixup segment: %s\n", sc->kue_unit, usbd_errstr(err)); return(ENXIO); } /* Send trigger command. */ err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, 0, kue_trig_seg, sizeof(kue_trig_seg)); if (err) { printf("kue%d: failed to load trigger segment: %s\n", sc->kue_unit, usbd_errstr(err)); return(ENXIO); } return(0); } static void kue_setmulti(struct kue_softc *sc) { struct ifnet *ifp; struct ifmultiaddr *ifma; int i = 0; ifp = sc->kue_ifp; if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { sc->kue_rxfilt |= KUE_RXFILT_ALLMULTI; sc->kue_rxfilt &= ~KUE_RXFILT_MULTICAST; kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->kue_rxfilt); return; } sc->kue_rxfilt &= ~KUE_RXFILT_ALLMULTI; IF_ADDR_LOCK(ifp); -#if __FreeBSD_version >= 500000 TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) -#else - LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) -#endif { if (ifma->ifma_addr->sa_family != AF_LINK) continue; /* * If there are too many addresses for the * internal filter, switch over to allmulti mode. */ if (i == KUE_MCFILTCNT(sc)) break; bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), KUE_MCFILT(sc, i), ETHER_ADDR_LEN); i++; } IF_ADDR_UNLOCK(ifp); if (i == KUE_MCFILTCNT(sc)) sc->kue_rxfilt |= KUE_RXFILT_ALLMULTI; else { sc->kue_rxfilt |= KUE_RXFILT_MULTICAST; kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MCAST_FILTERS, i, sc->kue_mcfilters, i * ETHER_ADDR_LEN); } kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->kue_rxfilt); return; } /* * Issue a SET_CONFIGURATION command to reset the MAC. This should be * done after the firmware is loaded into the adapter in order to * bring it into proper operation. */ static void kue_reset(struct kue_softc *sc) { if (usbd_set_config_no(sc->kue_udev, KUE_CONFIG_NO, 0) || usbd_device2interface_handle(sc->kue_udev, KUE_IFACE_IDX, &sc->kue_iface)) { printf("kue%d: getting interface handle failed\n", sc->kue_unit); } /* Wait a little while for the chip to get its brains in order. */ DELAY(1000); return; } /* * Probe for a KLSI chip. */ USB_MATCH(kue) { USB_MATCH_START(kue, uaa); struct kue_type *t; if (!uaa->iface) return(UMATCH_NONE); t = kue_devs; while(t->kue_vid) { if (uaa->vendor == t->kue_vid && uaa->product == t->kue_did) { return(UMATCH_VENDOR_PRODUCT); } t++; } return(UMATCH_NONE); } /* * Attach the interface. Allocate softc structures, do * setup and ethernet/BPF attach. */ USB_ATTACH(kue) { USB_ATTACH_START(kue, sc, uaa); char devinfo[1024]; struct ifnet *ifp; usbd_status err; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; int i; bzero(sc, sizeof(struct kue_softc)); sc->kue_dev = self; sc->kue_iface = uaa->iface; sc->kue_udev = uaa->device; sc->kue_unit = device_get_unit(self); id = usbd_get_interface_descriptor(uaa->iface); usbd_devinfo(uaa->device, 0, devinfo); device_set_desc_copy(self, devinfo); printf("%s: %s\n", device_get_nameunit(self), devinfo); /* Find endpoints. */ for (i = 0; i < id->bNumEndpoints; i++) { ed = usbd_interface2endpoint_descriptor(uaa->iface, i); if (!ed) { printf("kue%d: couldn't get ep %d\n", sc->kue_unit, i); USB_ATTACH_ERROR_RETURN; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { sc->kue_ed[KUE_ENDPT_RX] = ed->bEndpointAddress; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { sc->kue_ed[KUE_ENDPT_TX] = ed->bEndpointAddress; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { sc->kue_ed[KUE_ENDPT_INTR] = ed->bEndpointAddress; } } -#if __FreeBSD_version >= 500000 mtx_init(&sc->kue_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); -#endif KUE_LOCK(sc); /* Load the firmware into the NIC. */ if (kue_load_fw(sc)) { KUE_UNLOCK(sc); -#if __FreeBSD_version >= 500000 mtx_destroy(&sc->kue_mtx); -#endif USB_ATTACH_ERROR_RETURN; } /* Reset the adapter. */ kue_reset(sc); /* Read ethernet descriptor */ err = kue_ctl(sc, KUE_CTL_READ, KUE_CMD_GET_ETHER_DESCRIPTOR, 0, (char *)&sc->kue_desc, sizeof(sc->kue_desc)); sc->kue_mcfilters = malloc(KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN, M_USBDEV, M_NOWAIT); ifp = sc->kue_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { printf("kue%d: can not if_alloc()\n", sc->kue_unit); KUE_UNLOCK(sc); -#if __FreeBSD_version >= 500000 mtx_destroy(&sc->kue_mtx); -#endif USB_ATTACH_ERROR_RETURN; } ifp->if_softc = sc; if_initname(ifp, "kue", sc->kue_unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_NEEDSGIANT; ifp->if_ioctl = kue_ioctl; ifp->if_start = kue_start; ifp->if_watchdog = kue_watchdog; ifp->if_init = kue_init; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; sc->kue_qdat.ifp = ifp; sc->kue_qdat.if_rxstart = kue_rxstart; /* * Call MI attach routine. */ -#if __FreeBSD_version >= 500000 ether_ifattach(ifp, sc->kue_desc.kue_macaddr); -#else - ether_ifattach(ifp, ETHER_BPF_SUPPORTED); -#endif usb_register_netisr(); sc->kue_dying = 0; KUE_UNLOCK(sc); USB_ATTACH_SUCCESS_RETURN; } static int kue_detach(device_t dev) { struct kue_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); KUE_LOCK(sc); ifp = sc->kue_ifp; sc->kue_dying = 1; if (ifp != NULL) -#if __FreeBSD_version >= 500000 ether_ifdetach(ifp); if_free(ifp); -#else - ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); -#endif if (sc->kue_ep[KUE_ENDPT_TX] != NULL) usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_TX]); if (sc->kue_ep[KUE_ENDPT_RX] != NULL) usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_RX]); if (sc->kue_ep[KUE_ENDPT_INTR] != NULL) usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_INTR]); if (sc->kue_mcfilters != NULL) free(sc->kue_mcfilters, M_USBDEV); KUE_UNLOCK(sc); -#if __FreeBSD_version >= 500000 mtx_destroy(&sc->kue_mtx); -#endif return(0); } static void kue_rxstart(struct ifnet *ifp) { struct kue_softc *sc; struct ue_chain *c; sc = ifp->if_softc; KUE_LOCK(sc); c = &sc->kue_cdata.ue_rx_chain[sc->kue_cdata.ue_rx_prod]; c->ue_mbuf = usb_ether_newbuf(); if (c->ue_mbuf == NULL) { printf("%s: no memory for rx list " "-- packet dropped!\n", device_get_nameunit(sc->kue_dev)); ifp->if_ierrors++; KUE_UNLOCK(sc); return; } /* Setup new transfer. */ usbd_setup_xfer(c->ue_xfer, sc->kue_ep[KUE_ENDPT_RX], c, mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, kue_rxeof); usbd_transfer(c->ue_xfer); KUE_UNLOCK(sc); return; } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */ static void kue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct kue_softc *sc; struct ue_chain *c; struct mbuf *m; struct ifnet *ifp; int total_len = 0; u_int16_t len; c = priv; sc = c->ue_sc; KUE_LOCK(sc); ifp = sc->kue_ifp; if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { KUE_UNLOCK(sc); return; } if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { KUE_UNLOCK(sc); return; } if (usbd_ratecheck(&sc->kue_rx_notice)) printf("kue%d: usb error on rx: %s\n", sc->kue_unit, usbd_errstr(status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall(sc->kue_ep[KUE_ENDPT_RX]); goto done; } usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); m = c->ue_mbuf; if (total_len <= 1) goto done; len = *mtod(m, u_int16_t *); m_adj(m, sizeof(u_int16_t)); /* No errors; receive the packet. */ total_len = len; if (len < sizeof(struct ether_header)) { ifp->if_ierrors++; goto done; } ifp->if_ipackets++; m->m_pkthdr.rcvif = (void *)&sc->kue_qdat; m->m_pkthdr.len = m->m_len = total_len; /* Put the packet on the special USB input queue. */ usb_ether_input(m); KUE_UNLOCK(sc); return; done: /* Setup new transfer. */ usbd_setup_xfer(c->ue_xfer, sc->kue_ep[KUE_ENDPT_RX], c, mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, kue_rxeof); usbd_transfer(c->ue_xfer); KUE_UNLOCK(sc); return; } /* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void kue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct kue_softc *sc; struct ue_chain *c; struct ifnet *ifp; usbd_status err; c = priv; sc = c->ue_sc; KUE_LOCK(sc); ifp = sc->kue_ifp; ifp->if_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { KUE_UNLOCK(sc); return; } printf("kue%d: usb error on tx: %s\n", sc->kue_unit, usbd_errstr(status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall(sc->kue_ep[KUE_ENDPT_TX]); KUE_UNLOCK(sc); return; } usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &err); if (c->ue_mbuf != NULL) { c->ue_mbuf->m_pkthdr.rcvif = ifp; usb_tx_done(c->ue_mbuf); c->ue_mbuf = NULL; } if (err) ifp->if_oerrors++; else ifp->if_opackets++; KUE_UNLOCK(sc); return; } static int kue_encap(struct kue_softc *sc, struct mbuf *m, int idx) { int total_len; struct ue_chain *c; usbd_status err; c = &sc->kue_cdata.ue_tx_chain[idx]; /* * Copy the mbuf data into a contiguous buffer, leaving two * bytes at the beginning to hold the frame length. */ m_copydata(m, 0, m->m_pkthdr.len, c->ue_buf + 2); c->ue_mbuf = m; total_len = m->m_pkthdr.len + 2; total_len += 64 - (total_len % 64); /* Frame length is specified in the first 2 bytes of the buffer. */ c->ue_buf[0] = (u_int8_t)m->m_pkthdr.len; c->ue_buf[1] = (u_int8_t)(m->m_pkthdr.len >> 8); usbd_setup_xfer(c->ue_xfer, sc->kue_ep[KUE_ENDPT_TX], c, c->ue_buf, total_len, 0, 10000, kue_txeof); /* Transmit */ err = usbd_transfer(c->ue_xfer); if (err != USBD_IN_PROGRESS) { kue_stop(sc); return(EIO); } sc->kue_cdata.ue_tx_cnt++; return(0); } static void kue_start(struct ifnet *ifp) { struct kue_softc *sc; struct mbuf *m_head = NULL; sc = ifp->if_softc; KUE_LOCK(sc); if (ifp->if_drv_flags & IFF_DRV_OACTIVE) { KUE_UNLOCK(sc); return; } IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) { KUE_UNLOCK(sc); return; } if (kue_encap(sc, m_head, 0)) { IF_PREPEND(&ifp->if_snd, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; KUE_UNLOCK(sc); return; } /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; /* * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; KUE_UNLOCK(sc); return; } static void kue_init(void *xsc) { struct kue_softc *sc = xsc; struct ifnet *ifp = sc->kue_ifp; struct ue_chain *c; usbd_status err; int i; KUE_LOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) { KUE_UNLOCK(sc); return; } /* Set MAC address */ kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MAC, 0, IF_LLADDR(sc->kue_ifp), ETHER_ADDR_LEN); sc->kue_rxfilt = KUE_RXFILT_UNICAST|KUE_RXFILT_BROADCAST; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) sc->kue_rxfilt |= KUE_RXFILT_PROMISC; kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->kue_rxfilt); /* I'm not sure how to tune these. */ #ifdef notdef /* * Leave this one alone for now; setting it * wrong causes lockups on some machines/controllers. */ kue_setword(sc, KUE_CMD_SET_SOFS, 1); #endif kue_setword(sc, KUE_CMD_SET_URB_SIZE, 64); /* Init TX ring. */ if (usb_ether_tx_list_init(sc, &sc->kue_cdata, sc->kue_udev) == ENOBUFS) { printf("kue%d: tx list init failed\n", sc->kue_unit); KUE_UNLOCK(sc); return; } /* Init RX ring. */ if (usb_ether_rx_list_init(sc, &sc->kue_cdata, sc->kue_udev) == ENOBUFS) { printf("kue%d: rx list init failed\n", sc->kue_unit); KUE_UNLOCK(sc); return; } /* Load the multicast filter. */ kue_setmulti(sc); /* Open RX and TX pipes. */ err = usbd_open_pipe(sc->kue_iface, sc->kue_ed[KUE_ENDPT_RX], USBD_EXCLUSIVE_USE, &sc->kue_ep[KUE_ENDPT_RX]); if (err) { printf("kue%d: open rx pipe failed: %s\n", sc->kue_unit, usbd_errstr(err)); KUE_UNLOCK(sc); return; } err = usbd_open_pipe(sc->kue_iface, sc->kue_ed[KUE_ENDPT_TX], USBD_EXCLUSIVE_USE, &sc->kue_ep[KUE_ENDPT_TX]); if (err) { printf("kue%d: open tx pipe failed: %s\n", sc->kue_unit, usbd_errstr(err)); KUE_UNLOCK(sc); return; } /* Start up the receive pipe. */ for (i = 0; i < UE_RX_LIST_CNT; i++) { c = &sc->kue_cdata.ue_rx_chain[i]; usbd_setup_xfer(c->ue_xfer, sc->kue_ep[KUE_ENDPT_RX], c, mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, kue_rxeof); usbd_transfer(c->ue_xfer); } ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; KUE_UNLOCK(sc); return; } static int kue_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct kue_softc *sc = ifp->if_softc; int error = 0; KUE_LOCK(sc); switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING && ifp->if_flags & IFF_PROMISC && !(sc->kue_if_flags & IFF_PROMISC)) { sc->kue_rxfilt |= KUE_RXFILT_PROMISC; kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->kue_rxfilt); } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && !(ifp->if_flags & IFF_PROMISC) && sc->kue_if_flags & IFF_PROMISC) { sc->kue_rxfilt &= ~KUE_RXFILT_PROMISC; kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->kue_rxfilt); } else if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) kue_init(sc); } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) kue_stop(sc); } sc->kue_if_flags = ifp->if_flags; error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: kue_setmulti(sc); error = 0; break; default: error = ether_ioctl(ifp, command, data); break; } KUE_UNLOCK(sc); return(error); } static void kue_watchdog(struct ifnet *ifp) { struct kue_softc *sc; struct ue_chain *c; usbd_status stat; sc = ifp->if_softc; KUE_LOCK(sc); ifp->if_oerrors++; printf("kue%d: watchdog timeout\n", sc->kue_unit); c = &sc->kue_cdata.ue_tx_chain[0]; usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &stat); kue_txeof(c->ue_xfer, c, stat); if (ifp->if_snd.ifq_head != NULL) kue_start(ifp); KUE_UNLOCK(sc); return; } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void kue_stop(struct kue_softc *sc) { usbd_status err; struct ifnet *ifp; KUE_LOCK(sc); ifp = sc->kue_ifp; ifp->if_timer = 0; /* Stop transfers. */ if (sc->kue_ep[KUE_ENDPT_RX] != NULL) { err = usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_RX]); if (err) { printf("kue%d: abort rx pipe failed: %s\n", sc->kue_unit, usbd_errstr(err)); } err = usbd_close_pipe(sc->kue_ep[KUE_ENDPT_RX]); if (err) { printf("kue%d: close rx pipe failed: %s\n", sc->kue_unit, usbd_errstr(err)); } sc->kue_ep[KUE_ENDPT_RX] = NULL; } if (sc->kue_ep[KUE_ENDPT_TX] != NULL) { err = usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_TX]); if (err) { printf("kue%d: abort tx pipe failed: %s\n", sc->kue_unit, usbd_errstr(err)); } err = usbd_close_pipe(sc->kue_ep[KUE_ENDPT_TX]); if (err) { printf("kue%d: close tx pipe failed: %s\n", sc->kue_unit, usbd_errstr(err)); } sc->kue_ep[KUE_ENDPT_TX] = NULL; } if (sc->kue_ep[KUE_ENDPT_INTR] != NULL) { err = usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_INTR]); if (err) { printf("kue%d: abort intr pipe failed: %s\n", sc->kue_unit, usbd_errstr(err)); } err = usbd_close_pipe(sc->kue_ep[KUE_ENDPT_INTR]); if (err) { printf("kue%d: close intr pipe failed: %s\n", sc->kue_unit, usbd_errstr(err)); } sc->kue_ep[KUE_ENDPT_INTR] = NULL; } /* Free RX resources. */ usb_ether_rx_list_free(&sc->kue_cdata); /* Free TX resources. */ usb_ether_tx_list_free(&sc->kue_cdata); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); KUE_UNLOCK(sc); return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static void kue_shutdown(device_t dev) { struct kue_softc *sc; sc = device_get_softc(dev); kue_stop(sc); return; } Index: head/sys/dev/usb/if_kuereg.h =================================================================== --- head/sys/dev/usb/if_kuereg.h (revision 169488) +++ head/sys/dev/usb/if_kuereg.h (revision 169489) @@ -1,164 +1,162 @@ /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* * Definitions for the KLSI KL5KUSB101B USB to ethernet controller. * The KLSI part is controlled via vendor control requests, the structure * of which depend a bit on the firmware running on the internal * microcontroller. The one exception is the 'send scan data' command, * which is used to load the firmware. */ #define KUE_CMD_GET_ETHER_DESCRIPTOR 0x00 #define KUE_CMD_SET_MCAST_FILTERS 0x01 #define KUE_CMD_SET_PKT_FILTER 0x02 #define KUE_CMD_GET_ETHERSTATS 0x03 #define KUE_CMD_GET_GPIO 0x04 #define KUE_CMD_SET_GPIO 0x05 #define KUE_CMD_SET_MAC 0x06 #define KUE_CMD_GET_MAC 0x07 #define KUE_CMD_SET_URB_SIZE 0x08 #define KUE_CMD_SET_SOFS 0x09 #define KUE_CMD_SET_EVEN_PKTS 0x0A #define KUE_CMD_SEND_SCAN 0xFF struct kue_ether_desc { u_int8_t kue_len; u_int8_t kue_rsvd0; u_int8_t kue_rsvd1; u_int8_t kue_macaddr[ETHER_ADDR_LEN]; u_int8_t kue_etherstats[4]; u_int8_t kue_maxseg[2]; u_int8_t kue_mcastfilt[2]; u_int8_t kue_rsvd2; }; #define KUE_ETHERSTATS(x) \ (*(u_int32_t *)&(x)->kue_desc.kue_etherstats) #define KUE_MAXSEG(x) \ (*(u_int16_t *)&(x)->kue_desc.kue_maxseg) #define KUE_MCFILTCNT(x) \ ((*(u_int16_t *)&(x)->kue_desc.kue_mcastfilt) & 0x7FFF) #define KUE_MCFILT(x, y) \ (char *)&(sc->kue_mcfilters[y * ETHER_ADDR_LEN]) #define KUE_STAT_TX_OK 0x00000001 #define KUE_STAT_RX_OK 0x00000002 #define KUE_STAT_TX_ERR 0x00000004 #define KUE_STAT_RX_ERR 0x00000008 #define KUE_STAT_RX_NOBUF 0x00000010 #define KUE_STAT_TX_UCAST_BYTES 0x00000020 #define KUE_STAT_TX_UCAST_FRAMES 0x00000040 #define KUE_STAT_TX_MCAST_BYTES 0x00000080 #define KUE_STAT_TX_MCAST_FRAMES 0x00000100 #define KUE_STAT_TX_BCAST_BYTES 0x00000200 #define KUE_STAT_TX_BCAST_FRAMES 0x00000400 #define KUE_STAT_RX_UCAST_BYTES 0x00000800 #define KUE_STAT_RX_UCAST_FRAMES 0x00001000 #define KUE_STAT_RX_MCAST_BYTES 0x00002000 #define KUE_STAT_RX_MCAST_FRAMES 0x00004000 #define KUE_STAT_RX_BCAST_BYTES 0x00008000 #define KUE_STAT_RX_BCAST_FRAMES 0x00010000 #define KUE_STAT_RX_CRCERR 0x00020000 #define KUE_STAT_TX_QUEUE_LENGTH 0x00040000 #define KUE_STAT_RX_ALIGNERR 0x00080000 #define KUE_STAT_TX_SINGLECOLL 0x00100000 #define KUE_STAT_TX_MULTICOLL 0x00200000 #define KUE_STAT_TX_DEFERRED 0x00400000 #define KUE_STAT_TX_MAXCOLLS 0x00800000 #define KUE_STAT_RX_OVERRUN 0x01000000 #define KUE_STAT_TX_UNDERRUN 0x02000000 #define KUE_STAT_TX_SQE_ERR 0x04000000 #define KUE_STAT_TX_CARRLOSS 0x08000000 #define KUE_STAT_RX_LATECOLL 0x10000000 #define KUE_RXFILT_PROMISC 0x0001 #define KUE_RXFILT_ALLMULTI 0x0002 #define KUE_RXFILT_UNICAST 0x0004 #define KUE_RXFILT_BROADCAST 0x0008 #define KUE_RXFILT_MULTICAST 0x0010 #define KUE_TIMEOUT 1000 #define KUE_MIN_FRAMELEN 60 #define KUE_CTL_READ 0x01 #define KUE_CTL_WRITE 0x02 #define KUE_CONFIG_NO 1 #define KUE_IFACE_IDX 0 /* * The interrupt endpoint is currently unused * by the KLSI part. */ #define KUE_ENDPT_RX 0x0 #define KUE_ENDPT_TX 0x1 #define KUE_ENDPT_INTR 0x2 #define KUE_ENDPT_MAX 0x3 struct kue_type { u_int16_t kue_vid; u_int16_t kue_did; }; #define KUE_INC(x, y) (x) = (x + 1) % y struct kue_softc { struct ifnet *kue_ifp; device_t kue_dev; usbd_device_handle kue_udev; usbd_interface_handle kue_iface; struct kue_ether_desc kue_desc; int kue_ed[KUE_ENDPT_MAX]; usbd_pipe_handle kue_ep[KUE_ENDPT_MAX]; int kue_unit; int kue_if_flags; u_int16_t kue_rxfilt; u_int8_t *kue_mcfilters; struct ue_cdata kue_cdata; -#if __FreeBSD_version >= 500000 struct mtx kue_mtx; -#endif char kue_dying; struct timeval kue_rx_notice; struct usb_qdat kue_qdat; }; #if 0 #define KUE_LOCK(_sc) mtx_lock(&(_sc)->kue_mtx) #define KUE_UNLOCK(_sc) mtx_unlock(&(_sc)->kue_mtx) #else #define KUE_LOCK(_sc) #define KUE_UNLOCK(_sc) #endif Index: head/sys/dev/usb/if_rue.c =================================================================== --- head/sys/dev/usb/if_rue.c (revision 169488) +++ head/sys/dev/usb/if_rue.c (revision 169489) @@ -1,1407 +1,1386 @@ /*- * Copyright (c) 2001-2003, Shunsuke Akiyama . * Copyright (c) 1997, 1998, 1999, 2000 Bill Paul . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * RealTek RTL8150 USB to fast ethernet controller driver. * Datasheet is available from * ftp://ftp.realtek.com.tw/lancard/data_sheet/8150/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#if __FreeBSD_version < 500000 -#include -#endif #include #include #include #include #include "usbdevs.h" #include #include #include #include /* "device miibus" required. See GENERIC if you get errors here. */ #include "miibus_if.h" #ifdef USB_DEBUG static int ruedebug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, rue, CTLFLAG_RW, 0, "USB rue"); SYSCTL_INT(_hw_usb_rue, OID_AUTO, debug, CTLFLAG_RW, &ruedebug, 0, "rue debug level"); #define DPRINTFN(n, x) do { \ if (ruedebug > (n)) \ logprintf x; \ } while (0); #else #define DPRINTFN(n, x) #endif #define DPRINTF(x) DPRINTFN(0, x) /* * Various supported device vendors/products. */ static struct rue_type rue_devs[] = { { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAKTX }, { USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_USBKR100 }, { 0, 0 } }; static int rue_match(device_t); static int rue_attach(device_t); static int rue_detach(device_t); static int rue_encap(struct rue_softc *, struct mbuf *, int); #ifdef RUE_INTR_PIPE static void rue_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); #endif static void rue_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status); static void rue_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status); static void rue_tick(void *); static void rue_rxstart(struct ifnet *); static void rue_start(struct ifnet *); static int rue_ioctl(struct ifnet *, u_long, caddr_t); static void rue_init(void *); static void rue_stop(struct rue_softc *); static void rue_watchdog(struct ifnet *); static void rue_shutdown(device_t); static int rue_ifmedia_upd(struct ifnet *); static void rue_ifmedia_sts(struct ifnet *, struct ifmediareq *); static int rue_miibus_readreg(device_t, int, int); static int rue_miibus_writereg(device_t, int, int, int); static void rue_miibus_statchg(device_t); static void rue_setmulti(struct rue_softc *); static void rue_reset(struct rue_softc *); static int rue_read_mem(struct rue_softc *, u_int16_t, void *, u_int16_t); static int rue_write_mem(struct rue_softc *, u_int16_t, void *, u_int16_t); static int rue_csr_read_1(struct rue_softc *, int); static int rue_csr_write_1(struct rue_softc *, int, u_int8_t); static int rue_csr_read_2(struct rue_softc *, int); static int rue_csr_write_2(struct rue_softc *, int, u_int16_t); static int rue_csr_write_4(struct rue_softc *, int, u_int32_t); static device_method_t rue_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rue_match), DEVMETHOD(device_attach, rue_attach), DEVMETHOD(device_detach, rue_detach), DEVMETHOD(device_shutdown, rue_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, rue_miibus_readreg), DEVMETHOD(miibus_writereg, rue_miibus_writereg), DEVMETHOD(miibus_statchg, rue_miibus_statchg), { 0, 0 } }; static driver_t rue_driver = { "rue", rue_methods, sizeof(struct rue_softc) }; static devclass_t rue_devclass; DRIVER_MODULE(rue, uhub, rue_driver, rue_devclass, usbd_driver_load, 0); DRIVER_MODULE(miibus, rue, miibus_driver, miibus_devclass, 0, 0); MODULE_DEPEND(rue, usb, 1, 1, 1); MODULE_DEPEND(rue, ether, 1, 1, 1); MODULE_DEPEND(rue, miibus, 1, 1, 1); #define RUE_SETBIT(sc, reg, x) \ rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) | (x)) #define RUE_CLRBIT(sc, reg, x) \ rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) & ~(x)) #define RUE_SETBIT_2(sc, reg, x) \ rue_csr_write_2(sc, reg, rue_csr_read_2(sc, reg) | (x)) #define RUE_CLRBIT_2(sc, reg, x) \ rue_csr_write_2(sc, reg, rue_csr_read_2(sc, reg) & ~(x)) static int rue_read_mem(struct rue_softc *sc, u_int16_t addr, void *buf, u_int16_t len) { usb_device_request_t req; usbd_status err; if (sc->rue_dying) return (0); RUE_LOCK(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = UR_SET_ADDRESS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, len); err = usbd_do_request(sc->rue_udev, &req, buf); RUE_UNLOCK(sc); if (err) { printf("rue%d: control pipe read failed: %s\n", sc->rue_unit, usbd_errstr(err)); return (-1); } return (0); } static int rue_write_mem(struct rue_softc *sc, u_int16_t addr, void *buf, u_int16_t len) { usb_device_request_t req; usbd_status err; if (sc->rue_dying) return (0); RUE_LOCK(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UR_SET_ADDRESS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, len); err = usbd_do_request(sc->rue_udev, &req, buf); RUE_UNLOCK(sc); if (err) { printf("rue%d: control pipe write failed: %s\n", sc->rue_unit, usbd_errstr(err)); return (-1); } return (0); } static int rue_csr_read_1(struct rue_softc *sc, int reg) { int err; u_int8_t val = 0; err = rue_read_mem(sc, reg, &val, 1); if (err) return (0); return (val); } static int rue_csr_read_2(struct rue_softc *sc, int reg) { int err; u_int16_t val = 0; uWord w; USETW(w, val); err = rue_read_mem(sc, reg, &w, 2); val = UGETW(w); if (err) return (0); return (val); } static int rue_csr_write_1(struct rue_softc *sc, int reg, u_int8_t val) { int err; err = rue_write_mem(sc, reg, &val, 1); if (err) return (-1); return (0); } static int rue_csr_write_2(struct rue_softc *sc, int reg, u_int16_t val) { int err; uWord w; USETW(w, val); err = rue_write_mem(sc, reg, &w, 2); if (err) return (-1); return (0); } static int rue_csr_write_4(struct rue_softc *sc, int reg, u_int32_t val) { int err; uDWord dw; USETDW(dw, val); err = rue_write_mem(sc, reg, &dw, 4); if (err) return (-1); return (0); } static int rue_miibus_readreg(device_t dev, int phy, int reg) { struct rue_softc *sc = USBGETSOFTC(dev); int rval; int ruereg; if (phy != 0) /* RTL8150 supports PHY == 0, only */ return (0); switch (reg) { case MII_BMCR: ruereg = RUE_BMCR; break; case MII_BMSR: ruereg = RUE_BMSR; break; case MII_ANAR: ruereg = RUE_ANAR; break; case MII_ANER: ruereg = RUE_AER; break; case MII_ANLPAR: ruereg = RUE_ANLP; break; case MII_PHYIDR1: case MII_PHYIDR2: return (0); break; default: if (RUE_REG_MIN <= reg && reg <= RUE_REG_MAX) { rval = rue_csr_read_1(sc, reg); return (rval); } printf("rue%d: bad phy register\n", sc->rue_unit); return (0); } rval = rue_csr_read_2(sc, ruereg); return (rval); } static int rue_miibus_writereg(device_t dev, int phy, int reg, int data) { struct rue_softc *sc = USBGETSOFTC(dev); int ruereg; if (phy != 0) /* RTL8150 supports PHY == 0, only */ return (0); switch (reg) { case MII_BMCR: ruereg = RUE_BMCR; break; case MII_BMSR: ruereg = RUE_BMSR; break; case MII_ANAR: ruereg = RUE_ANAR; break; case MII_ANER: ruereg = RUE_AER; break; case MII_ANLPAR: ruereg = RUE_ANLP; break; case MII_PHYIDR1: case MII_PHYIDR2: return (0); break; default: if (RUE_REG_MIN <= reg && reg <= RUE_REG_MAX) { rue_csr_write_1(sc, reg, data); return (0); } printf("rue%d: bad phy register\n", sc->rue_unit); return (0); } rue_csr_write_2(sc, ruereg, data); return (0); } static void rue_miibus_statchg(device_t dev) { /* * When the code below is enabled the card starts doing weird * things after link going from UP to DOWN and back UP. * * Looks like some of register writes below messes up PHY * interface. * * No visible regressions were found after commenting this code * out, so that disable it for good. */ #if 0 struct rue_softc *sc = USBGETSOFTC(dev); struct mii_data *mii = GET_MII(sc); int bmcr; RUE_CLRBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE)); bmcr = rue_csr_read_2(sc, RUE_BMCR); if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) bmcr |= RUE_BMCR_SPD_SET; else bmcr &= ~RUE_BMCR_SPD_SET; if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) bmcr |= RUE_BMCR_DUPLEX; else bmcr &= ~RUE_BMCR_DUPLEX; rue_csr_write_2(sc, RUE_BMCR, bmcr); RUE_SETBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE)); #endif } /* * Program the 64-bit multicast hash filter. */ static void rue_setmulti(struct rue_softc *sc) { struct ifnet *ifp; int h = 0; u_int32_t hashes[2] = { 0, 0 }; struct ifmultiaddr *ifma; u_int32_t rxcfg; int mcnt = 0; ifp = sc->rue_ifp; rxcfg = rue_csr_read_2(sc, RUE_RCR); if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { rxcfg |= (RUE_RCR_AAM | RUE_RCR_AAP); rxcfg &= ~RUE_RCR_AM; rue_csr_write_2(sc, RUE_RCR, rxcfg); rue_csr_write_4(sc, RUE_MAR0, 0xFFFFFFFF); rue_csr_write_4(sc, RUE_MAR4, 0xFFFFFFFF); return; } /* first, zot all the existing hash bits */ rue_csr_write_4(sc, RUE_MAR0, 0); rue_csr_write_4(sc, RUE_MAR4, 0); /* now program new ones */ IF_ADDR_LOCK(ifp); -#if __FreeBSD_version >= 500000 TAILQ_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link) -#else - LIST_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link) -#endif { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = ether_crc32_be(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); mcnt++; } IF_ADDR_UNLOCK(ifp); if (mcnt) rxcfg |= RUE_RCR_AM; else rxcfg &= ~RUE_RCR_AM; rxcfg &= ~(RUE_RCR_AAM | RUE_RCR_AAP); rue_csr_write_2(sc, RUE_RCR, rxcfg); rue_csr_write_4(sc, RUE_MAR0, hashes[0]); rue_csr_write_4(sc, RUE_MAR4, hashes[1]); } static void rue_reset(struct rue_softc *sc) { int i; rue_csr_write_1(sc, RUE_CR, RUE_CR_SOFT_RST); for (i = 0; i < RUE_TIMEOUT; i++) { DELAY(500); if (!(rue_csr_read_1(sc, RUE_CR) & RUE_CR_SOFT_RST)) break; } if (i == RUE_TIMEOUT) printf("rue%d: reset never completed!\n", sc->rue_unit); DELAY(10000); } /* * Probe for a RTL8150 chip. */ USB_MATCH(rue) { USB_MATCH_START(rue, uaa); struct rue_type *t; if (uaa->iface == NULL) return (UMATCH_NONE); t = rue_devs; while (t->rue_vid) { if (uaa->vendor == t->rue_vid && uaa->product == t->rue_did) { return (UMATCH_VENDOR_PRODUCT); } t++; } return (UMATCH_NONE); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ USB_ATTACH(rue) { USB_ATTACH_START(rue, sc, uaa); char *devinfo; u_char eaddr[ETHER_ADDR_LEN]; struct ifnet *ifp; usbd_interface_handle iface; usbd_status err; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; int i; struct rue_type *t; devinfo = malloc(1024, M_USBDEV, M_WAITOK); bzero(sc, sizeof (struct rue_softc)); usbd_devinfo(uaa->device, 0, devinfo); sc->rue_dev = self; sc->rue_udev = uaa->device; sc->rue_unit = device_get_unit(self); if (usbd_set_config_no(sc->rue_udev, RUE_CONFIG_NO, 0)) { printf("rue%d: getting interface handle failed\n", sc->rue_unit); goto error; } err = usbd_device2interface_handle(uaa->device, RUE_IFACE_IDX, &iface); if (err) { printf("rue%d: getting interface handle failed\n", sc->rue_unit); goto error; } sc->rue_iface = iface; t = rue_devs; while (t->rue_vid) { if (uaa->vendor == t->rue_vid && uaa->product == t->rue_did) { sc->rue_info = t; break; } t++; } id = usbd_get_interface_descriptor(sc->rue_iface); usbd_devinfo(uaa->device, 0, devinfo); device_set_desc_copy(self, devinfo); printf("%s: %s\n", device_get_nameunit(self), devinfo); /* Find endpoints */ for (i = 0; i < id->bNumEndpoints; i++) { ed = usbd_interface2endpoint_descriptor(iface, i); if (ed == NULL) { printf("rue%d: couldn't get ep %d\n", sc->rue_unit, i); goto error; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { sc->rue_ed[RUE_ENDPT_RX] = ed->bEndpointAddress; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { sc->rue_ed[RUE_ENDPT_TX] = ed->bEndpointAddress; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { sc->rue_ed[RUE_ENDPT_INTR] = ed->bEndpointAddress; } } -#if __FreeBSD_version >= 500000 mtx_init(&sc->rue_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); -#endif RUE_LOCK(sc); /* Reset the adapter */ rue_reset(sc); /* Get station address from the EEPROM */ err = rue_read_mem(sc, RUE_EEPROM_IDR0, (caddr_t)&eaddr, ETHER_ADDR_LEN); if (err) { printf("rue%d: couldn't get station address\n", sc->rue_unit); goto error1; } ifp = sc->rue_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { printf("rue%d: can not if_alloc()\n", sc->rue_unit); goto error1; } ifp->if_softc = sc; if_initname(ifp, "rue", sc->rue_unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_NEEDSGIANT; ifp->if_ioctl = rue_ioctl; ifp->if_start = rue_start; ifp->if_watchdog = rue_watchdog; ifp->if_init = rue_init; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; /* MII setup */ if (mii_phy_probe(self, &sc->rue_miibus, rue_ifmedia_upd, rue_ifmedia_sts)) { printf("rue%d: MII without any PHY!\n", sc->rue_unit); goto error2; } sc->rue_qdat.ifp = ifp; sc->rue_qdat.if_rxstart = rue_rxstart; /* Call MI attach routine */ -#if __FreeBSD_version >= 500000 ether_ifattach(ifp, eaddr); -#else - ether_ifattach(ifp, ETHER_BPF_SUPPORTED); -#endif callout_handle_init(&sc->rue_stat_ch); usb_register_netisr(); sc->rue_dying = 0; RUE_UNLOCK(sc); free(devinfo, M_USBDEV); USB_ATTACH_SUCCESS_RETURN; error2: if_free(ifp); error1: RUE_UNLOCK(sc); -#if __FreeBSD_version >= 500000 mtx_destroy(&sc->rue_mtx); -#endif error: free(devinfo, M_USBDEV); USB_ATTACH_ERROR_RETURN; } static int rue_detach(device_t dev) { struct rue_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); RUE_LOCK(sc); ifp = sc->rue_ifp; sc->rue_dying = 1; untimeout(rue_tick, sc, sc->rue_stat_ch); -#if __FreeBSD_version >= 500000 ether_ifdetach(ifp); if_free(ifp); -#else - ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); -#endif if (sc->rue_ep[RUE_ENDPT_TX] != NULL) usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_TX]); if (sc->rue_ep[RUE_ENDPT_RX] != NULL) usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_RX]); #ifdef RUE_INTR_PIPE if (sc->rue_ep[RUE_ENDPT_INTR] != NULL) usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_INTR]); #endif RUE_UNLOCK(sc); -#if __FreeBSD_version >= 500000 mtx_destroy(&sc->rue_mtx); -#endif return (0); } #ifdef RUE_INTR_PIPE static void rue_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct rue_softc *sc = priv; struct ifnet *ifp; struct rue_intrpkt *p; RUE_LOCK(sc); ifp = sc->rue_ifp; if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { RUE_UNLOCK(sc); return; } if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { RUE_UNLOCK(sc); return; } printf("rue%d: usb error on intr: %s\n", sc->rue_unit, usbd_errstr(status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall(sc->rue_ep[RUE_ENDPT_INTR]); RUE_UNLOCK(sc); return; } usbd_get_xfer_status(xfer, NULL, (void **)&p, NULL, NULL); ifp->if_ierrors += p->rue_rxlost_cnt; ifp->if_ierrors += p->rue_crcerr_cnt; ifp->if_collisions += p->rue_col_cnt; RUE_UNLOCK(sc); } #endif static void rue_rxstart(struct ifnet *ifp) { struct rue_softc *sc; struct ue_chain *c; sc = ifp->if_softc; RUE_LOCK(sc); c = &sc->rue_cdata.ue_rx_chain[sc->rue_cdata.ue_rx_prod]; c->ue_mbuf = usb_ether_newbuf(); if (c->ue_mbuf == NULL) { printf("%s: no memory for rx list " "-- packet dropped!\n", device_get_nameunit(sc->rue_dev)); ifp->if_ierrors++; RUE_UNLOCK(sc); return; } /* Setup new transfer. */ usbd_setup_xfer(c->ue_xfer, sc->rue_ep[RUE_ENDPT_RX], c, mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, rue_rxeof); usbd_transfer(c->ue_xfer); RUE_UNLOCK(sc); } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */ static void rue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct ue_chain *c = priv; struct rue_softc *sc = c->ue_sc; struct mbuf *m; struct ifnet *ifp; int total_len = 0; struct rue_rxpkt r; if (sc->rue_dying) return; RUE_LOCK(sc); ifp = sc->rue_ifp; if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { RUE_UNLOCK(sc); return; } if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { RUE_UNLOCK(sc); return; } if (usbd_ratecheck(&sc->rue_rx_notice)) printf("rue%d: usb error on rx: %s\n", sc->rue_unit, usbd_errstr(status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall(sc->rue_ep[RUE_ENDPT_RX]); goto done; } usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); if (total_len <= ETHER_CRC_LEN) { ifp->if_ierrors++; goto done; } m = c->ue_mbuf; bcopy(mtod(m, char *) + total_len - 4, (char *)&r, sizeof (r)); /* Check recieve packet was valid or not */ if ((r.rue_rxstat & RUE_RXSTAT_VALID) == 0) { ifp->if_ierrors++; goto done; } /* No errors; receive the packet. */ total_len -= ETHER_CRC_LEN; ifp->if_ipackets++; m->m_pkthdr.rcvif = (void *)&sc->rue_qdat; m->m_pkthdr.len = m->m_len = total_len; /* Put the packet on the special USB input queue. */ usb_ether_input(m); RUE_UNLOCK(sc); return; done: /* Setup new transfer. */ usbd_setup_xfer(xfer, sc->rue_ep[RUE_ENDPT_RX], c, mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, rue_rxeof); usbd_transfer(xfer); RUE_UNLOCK(sc); } /* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void rue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct ue_chain *c = priv; struct rue_softc *sc = c->ue_sc; struct ifnet *ifp; usbd_status err; RUE_LOCK(sc); ifp = sc->rue_ifp; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { RUE_UNLOCK(sc); return; } printf("rue%d: usb error on tx: %s\n", sc->rue_unit, usbd_errstr(status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall(sc->rue_ep[RUE_ENDPT_TX]); RUE_UNLOCK(sc); return; } ifp->if_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &err); if (c->ue_mbuf != NULL) { c->ue_mbuf->m_pkthdr.rcvif = ifp; usb_tx_done(c->ue_mbuf); c->ue_mbuf = NULL; } if (err) ifp->if_oerrors++; else ifp->if_opackets++; RUE_UNLOCK(sc); } static void rue_tick(void *xsc) { struct rue_softc *sc = xsc; struct ifnet *ifp; struct mii_data *mii; if (sc == NULL) return; RUE_LOCK(sc); ifp = sc->rue_ifp; mii = GET_MII(sc); if (mii == NULL) { RUE_UNLOCK(sc); return; } mii_tick(mii); if (!sc->rue_link && mii->mii_media_status & IFM_ACTIVE && IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { sc->rue_link++; if (ifp->if_snd.ifq_head != NULL) rue_start(ifp); } sc->rue_stat_ch = timeout(rue_tick, sc, hz); RUE_UNLOCK(sc); } static int rue_encap(struct rue_softc *sc, struct mbuf *m, int idx) { int total_len; struct ue_chain *c; usbd_status err; c = &sc->rue_cdata.ue_tx_chain[idx]; /* * Copy the mbuf data into a contiguous buffer */ m_copydata(m, 0, m->m_pkthdr.len, c->ue_buf); c->ue_mbuf = m; total_len = m->m_pkthdr.len; /* * This is an undocumented behavior. * RTL8150 chip doesn't send frame length smaller than * RUE_MIN_FRAMELEN (60) byte packet. */ if (total_len < RUE_MIN_FRAMELEN) total_len = RUE_MIN_FRAMELEN; usbd_setup_xfer(c->ue_xfer, sc->rue_ep[RUE_ENDPT_TX], c, c->ue_buf, total_len, USBD_FORCE_SHORT_XFER, 10000, rue_txeof); /* Transmit */ err = usbd_transfer(c->ue_xfer); if (err != USBD_IN_PROGRESS) { rue_stop(sc); return (EIO); } sc->rue_cdata.ue_tx_cnt++; return (0); } static void rue_start(struct ifnet *ifp) { struct rue_softc *sc = ifp->if_softc; struct mbuf *m_head = NULL; RUE_LOCK(sc); if (!sc->rue_link) { RUE_UNLOCK(sc); return; } if (ifp->if_drv_flags & IFF_DRV_OACTIVE) { RUE_UNLOCK(sc); return; } IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) { RUE_UNLOCK(sc); return; } if (rue_encap(sc, m_head, 0)) { IF_PREPEND(&ifp->if_snd, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; RUE_UNLOCK(sc); return; } /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; /* * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; RUE_UNLOCK(sc); } static void rue_init(void *xsc) { struct rue_softc *sc = xsc; struct ifnet *ifp = sc->rue_ifp; struct mii_data *mii = GET_MII(sc); struct ue_chain *c; usbd_status err; int i; int rxcfg; RUE_LOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) { RUE_UNLOCK(sc); return; } /* * Cancel pending I/O and free all RX/TX buffers. */ rue_reset(sc); /* Set MAC address */ rue_write_mem(sc, RUE_IDR0, IF_LLADDR(sc->rue_ifp), ETHER_ADDR_LEN); /* Init TX ring. */ if (usb_ether_tx_list_init(sc, &sc->rue_cdata, sc->rue_udev) == ENOBUFS) { printf("rue%d: tx list init failed\n", sc->rue_unit); RUE_UNLOCK(sc); return; } /* Init RX ring. */ if (usb_ether_rx_list_init(sc, &sc->rue_cdata, sc->rue_udev) == ENOBUFS) { printf("rue%d: rx list init failed\n", sc->rue_unit); RUE_UNLOCK(sc); return; } #ifdef RUE_INTR_PIPE sc->rue_cdata.ue_ibuf = malloc(RUE_INTR_PKTLEN, M_USBDEV, M_NOWAIT); #endif /* * Set the initial TX and RX configuration. */ rue_csr_write_1(sc, RUE_TCR, RUE_TCR_CONFIG); rxcfg = RUE_RCR_CONFIG; /* Set capture broadcast bit to capture broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) rxcfg |= RUE_RCR_AB; else rxcfg &= ~RUE_RCR_AB; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) rxcfg |= RUE_RCR_AAP; else rxcfg &= ~RUE_RCR_AAP; rue_csr_write_2(sc, RUE_RCR, rxcfg); /* Load the multicast filter. */ rue_setmulti(sc); /* Enable RX and TX */ rue_csr_write_1(sc, RUE_CR, (RUE_CR_TE | RUE_CR_RE | RUE_CR_EP3CLREN)); mii_mediachg(mii); /* Open RX and TX pipes. */ err = usbd_open_pipe(sc->rue_iface, sc->rue_ed[RUE_ENDPT_RX], USBD_EXCLUSIVE_USE, &sc->rue_ep[RUE_ENDPT_RX]); if (err) { printf("rue%d: open rx pipe failed: %s\n", sc->rue_unit, usbd_errstr(err)); RUE_UNLOCK(sc); return; } err = usbd_open_pipe(sc->rue_iface, sc->rue_ed[RUE_ENDPT_TX], USBD_EXCLUSIVE_USE, &sc->rue_ep[RUE_ENDPT_TX]); if (err) { printf("rue%d: open tx pipe failed: %s\n", sc->rue_unit, usbd_errstr(err)); RUE_UNLOCK(sc); return; } #ifdef RUE_INTR_PIPE err = usbd_open_pipe_intr(sc->rue_iface, sc->rue_ed[RUE_ENDPT_INTR], USBD_SHORT_XFER_OK, &sc->rue_ep[RUE_ENDPT_INTR], sc, sc->rue_cdata.ue_ibuf, RUE_INTR_PKTLEN, rue_intr, RUE_INTR_INTERVAL); if (err) { printf("rue%d: open intr pipe failed: %s\n", sc->rue_unit, usbd_errstr(err)); RUE_UNLOCK(sc); return; } #endif /* Start up the receive pipe. */ for (i = 0; i < UE_RX_LIST_CNT; i++) { c = &sc->rue_cdata.ue_rx_chain[i]; usbd_setup_xfer(c->ue_xfer, sc->rue_ep[RUE_ENDPT_RX], c, mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, rue_rxeof); usbd_transfer(c->ue_xfer); } ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->rue_stat_ch = timeout(rue_tick, sc, hz); RUE_UNLOCK(sc); } /* * Set media options. */ static int rue_ifmedia_upd(struct ifnet *ifp) { struct rue_softc *sc = ifp->if_softc; struct mii_data *mii = GET_MII(sc); sc->rue_link = 0; if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH (miisc, &mii->mii_phys, mii_list) mii_phy_reset(miisc); } mii_mediachg(mii); return (0); } /* * Report current media status. */ static void rue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct rue_softc *sc = ifp->if_softc; struct mii_data *mii = GET_MII(sc); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; } static int rue_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct rue_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; struct mii_data *mii; int error = 0; RUE_LOCK(sc); switch (command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING && ifp->if_flags & IFF_PROMISC && !(sc->rue_if_flags & IFF_PROMISC)) { RUE_SETBIT_2(sc, RUE_RCR, (RUE_RCR_AAM | RUE_RCR_AAP)); rue_setmulti(sc); } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && !(ifp->if_flags & IFF_PROMISC) && sc->rue_if_flags & IFF_PROMISC) { RUE_CLRBIT_2(sc, RUE_RCR, (RUE_RCR_AAM | RUE_RCR_AAP)); rue_setmulti(sc); } else if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) rue_init(sc); } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) rue_stop(sc); } sc->rue_if_flags = ifp->if_flags; error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: rue_setmulti(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = GET_MII(sc); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; default: error = ether_ioctl(ifp, command, data); break; } RUE_UNLOCK(sc); return (error); } static void rue_watchdog(struct ifnet *ifp) { struct rue_softc *sc = ifp->if_softc; struct ue_chain *c; usbd_status stat; RUE_LOCK(sc); ifp->if_oerrors++; printf("rue%d: watchdog timeout\n", sc->rue_unit); c = &sc->rue_cdata.ue_tx_chain[0]; usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &stat); rue_txeof(c->ue_xfer, c, stat); if (ifp->if_snd.ifq_head != NULL) rue_start(ifp); RUE_UNLOCK(sc); } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void rue_stop(struct rue_softc *sc) { usbd_status err; struct ifnet *ifp; RUE_LOCK(sc); ifp = sc->rue_ifp; ifp->if_timer = 0; rue_csr_write_1(sc, RUE_CR, 0x00); rue_reset(sc); untimeout(rue_tick, sc, sc->rue_stat_ch); /* Stop transfers. */ if (sc->rue_ep[RUE_ENDPT_RX] != NULL) { err = usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_RX]); if (err) { printf("rue%d: abort rx pipe failed: %s\n", sc->rue_unit, usbd_errstr(err)); } err = usbd_close_pipe(sc->rue_ep[RUE_ENDPT_RX]); if (err) { printf("rue%d: close rx pipe failed: %s\n", sc->rue_unit, usbd_errstr(err)); } sc->rue_ep[RUE_ENDPT_RX] = NULL; } if (sc->rue_ep[RUE_ENDPT_TX] != NULL) { err = usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_TX]); if (err) { printf("rue%d: abort tx pipe failed: %s\n", sc->rue_unit, usbd_errstr(err)); } err = usbd_close_pipe(sc->rue_ep[RUE_ENDPT_TX]); if (err) { printf("rue%d: close tx pipe failed: %s\n", sc->rue_unit, usbd_errstr(err)); } sc->rue_ep[RUE_ENDPT_TX] = NULL; } #ifdef RUE_INTR_PIPE if (sc->rue_ep[RUE_ENDPT_INTR] != NULL) { err = usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_INTR]); if (err) { printf("rue%d: abort intr pipe failed: %s\n", sc->rue_unit, usbd_errstr(err)); } err = usbd_close_pipe(sc->rue_ep[RUE_ENDPT_INTR]); if (err) { printf("rue%d: close intr pipe failed: %s\n", sc->rue_unit, usbd_errstr(err)); } sc->rue_ep[RUE_ENDPT_INTR] = NULL; } #endif /* Free RX resources. */ usb_ether_rx_list_free(&sc->rue_cdata); /* Free TX resources. */ usb_ether_tx_list_free(&sc->rue_cdata); #ifdef RUE_INTR_PIPE free(sc->rue_cdata.ue_ibuf, M_USBDEV); sc->rue_cdata.ue_ibuf = NULL; #endif sc->rue_link = 0; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); RUE_UNLOCK(sc); } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static void rue_shutdown(device_t dev) { struct rue_softc *sc; sc = device_get_softc(dev); sc->rue_dying++; RUE_LOCK(sc); rue_reset(sc); rue_stop(sc); RUE_UNLOCK(sc); } Index: head/sys/dev/usb/if_ruereg.h =================================================================== --- head/sys/dev/usb/if_ruereg.h (revision 169488) +++ head/sys/dev/usb/if_ruereg.h (revision 169489) @@ -1,227 +1,225 @@ /*- * Copyright (c) 2001-2003, Shunsuke Akiyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _IF_RUEREG_H_ #define _IF_RUEREG_H_ #define RUE_INTR_PIPE 1 /* Use INTR PIPE */ #define RUE_CONFIG_NO 1 #define RUE_IFACE_IDX 0 #define RUE_ENDPT_RX 0x0 #define RUE_ENDPT_TX 0x1 #define RUE_ENDPT_INTR 0x2 #define RUE_ENDPT_MAX 0x3 #define RUE_INTR_PKTLEN 0x8 #define RUE_TIMEOUT 1000 #define ETHER_ALIGN 2 #define RUE_MIN_FRAMELEN 60 #define RUE_INTR_INTERVAL 100 /* ms */ /* * Registers */ #define RUE_IDR0 0x0120 #define RUE_IDR1 0x0121 #define RUE_IDR2 0x0122 #define RUE_IDR3 0x0123 #define RUE_IDR4 0x0124 #define RUE_IDR5 0x0125 #define RUE_MAR0 0x0126 #define RUE_MAR1 0x0127 #define RUE_MAR2 0x0128 #define RUE_MAR3 0x0129 #define RUE_MAR4 0x012A #define RUE_MAR5 0x012B #define RUE_MAR6 0x012C #define RUE_MAR7 0x012D #define RUE_CR 0x012E /* B, R/W */ #define RUE_CR_SOFT_RST 0x10 #define RUE_CR_RE 0x08 #define RUE_CR_TE 0x04 #define RUE_CR_EP3CLREN 0x02 #define RUE_TCR 0x012F /* B, R/W */ #define RUE_TCR_TXRR1 0x80 #define RUE_TCR_TXRR0 0x40 #define RUE_TCR_IFG1 0x10 #define RUE_TCR_IFG0 0x08 #define RUE_TCR_NOCRC 0x01 #define RUE_TCR_CONFIG (RUE_TCR_TXRR1|RUE_TCR_TXRR0|RUE_TCR_IFG1|RUE_TCR_IFG0) #define RUE_RCR 0x0130 /* W, R/W */ #define RUE_RCR_TAIL 0x80 #define RUE_RCR_AER 0x40 #define RUE_RCR_AR 0x20 #define RUE_RCR_AM 0x10 #define RUE_RCR_AB 0x08 #define RUE_RCR_AD 0x04 #define RUE_RCR_AAM 0x02 #define RUE_RCR_AAP 0x01 #define RUE_RCR_CONFIG (RUE_RCR_TAIL|RUE_RCR_AD) #define RUE_TSR 0x0132 #define RUE_RSR 0x0133 #define RUE_CON0 0x0135 #define RUE_CON1 0x0136 #define RUE_MSR 0x0137 #define RUE_PHYADD 0x0138 #define RUE_PHYDAT 0x0139 #define RUE_PHYCNT 0x013B /* B, R/W */ #define RUE_PHYCNT_PHYOWN 0x40 #define RUE_PHYCNT_RWCR 0x20 #define RUE_GPPC 0x013D #define RUE_WAKECNT 0x013E #define RUE_BMCR 0x0140 #define RUE_BMCR_SPD_SET 0x2000 #define RUE_BMCR_DUPLEX 0x0100 #define RUE_BMSR 0x0142 #define RUE_ANAR 0x0144 /* W, R/W */ #define RUE_ANAR_PAUSE 0x0400 #define RUE_ANLP 0x0146 /* W, R/O */ #define RUE_ANLP_PAUSE 0x0400 #define RUE_AER 0x0148 #define RUE_NWAYT 0x014A #define RUE_CSCR 0x014C #define RUE_CRC0 0x014E #define RUE_CRC1 0x0150 #define RUE_CRC2 0x0152 #define RUE_CRC3 0x0154 #define RUE_CRC4 0x0156 #define RUE_BYTEMASK0 0x0158 #define RUE_BYTEMASK1 0x0160 #define RUE_BYTEMASK2 0x0168 #define RUE_BYTEMASK3 0x0170 #define RUE_BYTEMASK4 0x0178 #define RUE_PHY1 0x0180 #define RUE_PHY2 0x0184 #define RUE_TW1 0x0186 #define RUE_REG_MIN 0x0120 #define RUE_REG_MAX 0x0189 /* * EEPROM address declarations */ #define RUE_EEPROM_BASE 0x1200 #define RUE_EEPROM_IDR0 (RUE_EEPROM_BASE + 0x02) #define RUE_EEPROM_IDR1 (RUE_EEPROM_BASE + 0x03) #define RUE_EEPROM_IDR2 (RUE_EEPROM_BASE + 0x03) #define RUE_EEPROM_IDR3 (RUE_EEPROM_BASE + 0x03) #define RUE_EEPROM_IDR4 (RUE_EEPROM_BASE + 0x03) #define RUE_EEPROM_IDR5 (RUE_EEPROM_BASE + 0x03) #define RUE_EEPROM_INTERVAL (RUE_EEPROM_BASE + 0x17) struct rue_intrpkt { u_int8_t rue_tsr; u_int8_t rue_rsr; u_int8_t rue_gep_msr; u_int8_t rue_waksr; u_int8_t rue_txok_cnt; u_int8_t rue_rxlost_cnt; u_int8_t rue_crcerr_cnt; u_int8_t rue_col_cnt; }; struct rue_rxpkt { u_int16_t rue_pktlen : 12; u_int16_t rue_rxstat : 4; }; #define RUE_RXSTAT_VALID 0x01 #define RUE_RXSTAT_RUNT 0x02 #define RUE_RXSTAT_PMATCH 0x04 #define RUE_RXSTAT_MCAST 0x08 #define RUE_RXSTAT_MASK RUE_RXSTAT_VALID struct rue_type { u_int16_t rue_vid; u_int16_t rue_did; }; struct rue_softc { struct ifnet *rue_ifp; device_t rue_dev; device_t rue_miibus; usbd_device_handle rue_udev; usbd_interface_handle rue_iface; struct rue_type *rue_info; int rue_ed[RUE_ENDPT_MAX]; usbd_pipe_handle rue_ep[RUE_ENDPT_MAX]; int rue_unit; u_int8_t rue_link; int rue_if_flags; struct ue_cdata rue_cdata; struct callout_handle rue_stat_ch; -#if __FreeBSD_version >= 500000 struct mtx rue_mtx; -#endif char rue_dying; struct timeval rue_rx_notice; struct usb_qdat rue_qdat; }; #if defined(__FreeBSD__) #define GET_MII(sc) (device_get_softc((sc)->rue_miibus)) #elif defined(__NetBSD__) #define GET_MII(sc) (&(sc)->rue_mii) #elif defined(__OpenBSD__) #define GET_MII(sc) (&(sc)->rue_mii) #endif #if 0 #define RUE_LOCK(_sc) mtx_lock(&(_sc)->rue_mtx) #define RUE_UNLOCK(_sc) mtx_unlock(&(_sc)->rue_mtx) #else #define RUE_LOCK(_sc) #define RUE_UNLOCK(_sc) #endif #endif /* _IF_RUEREG_H_ */ Index: head/sys/dev/usb/if_udav.c =================================================================== --- head/sys/dev/usb/if_udav.c (revision 169488) +++ head/sys/dev/usb/if_udav.c (revision 169489) @@ -1,1978 +1,1971 @@ /* $NetBSD: if_udav.c,v 1.2 2003/09/04 15:17:38 tsutsui Exp $ */ /* $nabe: if_udav.c,v 1.3 2003/08/21 16:57:19 nabe Exp $ */ /* $FreeBSD$ */ /*- * Copyright (c) 2003 * Shingo WATANABE . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* * DM9601(DAVICOM USB to Ethernet MAC Controller with Integrated 10/100 PHY) * The spec can be found at the following url. * http://www.davicom.com.tw/big5/download/Data%20Sheet/DM9601-DS-P01-930914.pdf */ /* * TODO: * Interrupt Endpoint support * External PHYs * powerhook() support? */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #if defined(__NetBSD__) #include "opt_ns.h" #endif #if defined(__NetBSD__) #include "bpfilter.h" #endif #if defined(__FreeBSD__) #define NBPFILTER 1 #endif #if defined(__NetBSD__) #include "rnd.h" #endif #include #include #include #include #include #include #include #if defined(__FreeBSD__) #include #include #include #endif #if defined(__NetBSD__) #include #endif #if defined(NRND) && NRND > 0 #include #endif #include #include #include #include #include #include #if NBPFILTER > 0 #include #endif #if defined(__NetBSD__) #ifndef BPF_MTAP #define BPF_MTAP(_ifp, _m) do { \ if ((_ifp)->if_bpf)) { \ bpf_mtap((_ifp)->if_bpf, (_m)) ; \ } \ } while (0) #endif #endif #if defined(__NetBSD__) #include #ifdef INET #include #include #endif /* INET */ #elif defined(__FreeBSD__) /* defined(__NetBSD__) */ #include #include #endif /* defined(__FreeBSD__) */ #if defined(__NetBSD__) #ifdef NS #include #include #endif #endif /* defined (__NetBSD__) */ #include #include -#if __FreeBSD_version < 500000 -#include -#endif #include #include #include #include #include #include "usbdevs.h" #include #include #include #if defined(__FreeBSD__) MODULE_DEPEND(udav, usb, 1, 1, 1); MODULE_DEPEND(udav, ether, 1, 1, 1); MODULE_DEPEND(udav, miibus, 1, 1, 1); #endif /* "device miibus" required. See GENERIC if you get errors here. */ #include "miibus_if.h" #if !defined(__FreeBSD__) /* Function declarations */ USB_DECLARE_DRIVER(udav); #endif #if defined(__FreeBSD__) static int udav_match(device_t); static int udav_attach(device_t); static int udav_detach(device_t); static void udav_shutdown(device_t); #endif static int udav_openpipes(struct udav_softc *); static void udav_start(struct ifnet *); static int udav_send(struct udav_softc *, struct mbuf *, int); static void udav_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status); #if defined(__FreeBSD__) static void udav_rxstart(struct ifnet *ifp); #endif static void udav_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status); static void udav_tick(void *); static void udav_tick_task(void *); static int udav_ioctl(struct ifnet *, u_long, caddr_t); static void udav_stop_task(struct udav_softc *); static void udav_stop(struct ifnet *, int); static void udav_watchdog(struct ifnet *); static int udav_ifmedia_change(struct ifnet *); static void udav_ifmedia_status(struct ifnet *, struct ifmediareq *); static void udav_lock_mii(struct udav_softc *); static void udav_unlock_mii(struct udav_softc *); static int udav_miibus_readreg(device_t, int, int); static void udav_miibus_writereg(device_t, int, int, int); static void udav_miibus_statchg(device_t); #if defined(__NetBSD__) static int udav_init(struct ifnet *); #elif defined(__FreeBSD__) static void udav_init(void *); #endif static void udav_setmulti(struct udav_softc *); static void udav_reset(struct udav_softc *); static int udav_csr_read(struct udav_softc *, int, void *, int); static int udav_csr_write(struct udav_softc *, int, void *, int); static int udav_csr_read1(struct udav_softc *, int); static int udav_csr_write1(struct udav_softc *, int, unsigned char); #if 0 static int udav_mem_read(struct udav_softc *, int, void *, int); static int udav_mem_write(struct udav_softc *, int, void *, int); static int udav_mem_write1(struct udav_softc *, int, unsigned char); #endif #if defined(__FreeBSD__) static device_method_t udav_methods[] = { /* Device interface */ DEVMETHOD(device_probe, udav_match), DEVMETHOD(device_attach, udav_attach), DEVMETHOD(device_detach, udav_detach), DEVMETHOD(device_shutdown, udav_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, udav_miibus_readreg), DEVMETHOD(miibus_writereg, udav_miibus_writereg), DEVMETHOD(miibus_statchg, udav_miibus_statchg), { 0, 0 } }; static driver_t udav_driver = { "udav", udav_methods, sizeof(struct udav_softc) }; static devclass_t udav_devclass; DRIVER_MODULE(udav, uhub, udav_driver, udav_devclass, usbd_driver_load, 0); DRIVER_MODULE(miibus, udav, miibus_driver, miibus_devclass, 0, 0); #endif /* defined(__FreeBSD__) */ /* Macros */ #ifdef UDAV_DEBUG #define DPRINTF(x) if (udavdebug) logprintf x #define DPRINTFN(n,x) if (udavdebug >= (n)) logprintf x int udavdebug = 0; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define delay(d) DELAY(d) #define UDAV_SETBIT(sc, reg, x) \ udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) | (x)) #define UDAV_CLRBIT(sc, reg, x) \ udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) & ~(x)) static const struct udav_type { struct usb_devno udav_dev; u_int16_t udav_flags; #define UDAV_EXT_PHY 0x0001 } udav_devs [] = { /* Corega USB-TXC */ {{ USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXC }, 0}, #if 0 /* DAVICOM DM9601 Generic? */ /* XXX: The following ids was obtained from the data sheet. */ {{ 0x0a46, 0x9601 }, 0}, #endif }; #define udav_lookup(v, p) ((const struct udav_type *)usb_lookup(udav_devs, v, p)) /* Probe */ USB_MATCH(udav) { USB_MATCH_START(udav, uaa); if (uaa->iface != NULL) return (UMATCH_NONE); return (udav_lookup(uaa->vendor, uaa->product) != NULL ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE); } /* Attach */ USB_ATTACH(udav) { USB_ATTACH_START(udav, sc, uaa); usbd_device_handle dev = uaa->device; usbd_interface_handle iface; usbd_status err; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; char devinfo[1024]; const char *devname ; struct ifnet *ifp; #if defined(__NetBSD__) struct mii_data *mii; #endif u_char eaddr[ETHER_ADDR_LEN]; int i; #if defined(__NetBSD__) int s; #endif bzero(sc, sizeof(struct udav_softc)); usbd_devinfo(dev, 0, devinfo); USB_ATTACH_SETUP; devname = device_get_nameunit(sc->sc_dev); printf("%s: %s\n", devname, devinfo); /* Move the device into the configured state. */ err = usbd_set_config_no(dev, UDAV_CONFIG_NO, 1); if (err) { printf("%s: setting config no failed\n", devname); goto bad; } usb_init_task(&sc->sc_tick_task, udav_tick_task, sc); lockinit(&sc->sc_mii_lock, PZERO, "udavmii", 0, 0); usb_init_task(&sc->sc_stop_task, (void (*)(void *)) udav_stop_task, sc); /* get control interface */ err = usbd_device2interface_handle(dev, UDAV_IFACE_INDEX, &iface); if (err) { printf("%s: failed to get interface, err=%s\n", devname, usbd_errstr(err)); goto bad; } sc->sc_udev = dev; sc->sc_ctl_iface = iface; sc->sc_flags = udav_lookup(uaa->vendor, uaa->product)->udav_flags; /* get interface descriptor */ id = usbd_get_interface_descriptor(sc->sc_ctl_iface); /* find endpoints */ sc->sc_bulkin_no = sc->sc_bulkout_no = sc->sc_intrin_no = -1; for (i = 0; i < id->bNumEndpoints; i++) { ed = usbd_interface2endpoint_descriptor(sc->sc_ctl_iface, i); if (ed == NULL) { printf("%s: couldn't get endpoint %d\n", devname, i); goto bad; } if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK && UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) sc->sc_bulkin_no = ed->bEndpointAddress; /* RX */ else if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK && UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT) sc->sc_bulkout_no = ed->bEndpointAddress; /* TX */ else if ((ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT && UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) sc->sc_intrin_no = ed->bEndpointAddress; /* Status */ } if (sc->sc_bulkin_no == -1 || sc->sc_bulkout_no == -1 || sc->sc_intrin_no == -1) { printf("%s: missing endpoint\n", devname); goto bad; } -#if defined(__FreeBSD__) && __FreeBSD_version >= 500000 +#if defined(__FreeBSD__) mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); #endif #if defined(__NetBSD__) s = splnet(); #elif defined(__FreeBSD__) UDAV_LOCK(sc); #endif /* reset the adapter */ udav_reset(sc); /* Get Ethernet Address */ err = udav_csr_read(sc, UDAV_PAR, (void *)eaddr, ETHER_ADDR_LEN); if (err) { printf("%s: read MAC address failed\n", devname); #if defined(__NetBSD__) splx(s); #elif defined(__FreeBSD__) UDAV_UNLOCK(sc); mtx_destroy(&sc->sc_mtx); #endif goto bad; } /* Print Ethernet Address */ printf("%s: Ethernet address %s\n", devname, ether_sprintf(eaddr)); /* initialize interface infomation */ #if defined(__FreeBSD__) ifp = GET_IFP(sc) = if_alloc(IFT_ETHER); if (ifp == NULL) { printf("%s: can not if_alloc\n", devname); UDAV_UNLOCK(sc); mtx_destroy(&sc->sc_mtx); goto bad; } #else ifp = GET_IFP(sc); #endif ifp->if_softc = sc; ifp->if_mtu = ETHERMTU; #if defined(__NetBSD__) strncpy(ifp->if_xname, devname, IFNAMSIZ); #elif defined(__FreeBSD__) if_initname(ifp, "udav", device_get_unit(self)); #endif ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_NEEDSGIANT; ifp->if_start = udav_start; ifp->if_ioctl = udav_ioctl; ifp->if_watchdog = udav_watchdog; ifp->if_init = udav_init; #if defined(__NetBSD__) ifp->if_stop = udav_stop; #endif #if defined(__FreeBSD__) ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; #endif #if defined(__NetBSD__) IFQ_SET_READY(&ifp->if_snd); #endif #if defined(__NetBSD__) /* * Do ifmedia setup. */ mii = &sc->sc_mii; mii->mii_ifp = ifp; mii->mii_readreg = udav_miibus_readreg; mii->mii_writereg = udav_miibus_writereg; mii->mii_statchg = udav_miibus_statchg; mii->mii_flags = MIIF_AUTOTSLEEP; ifmedia_init(&mii->mii_media, 0, udav_ifmedia_change, udav_ifmedia_status); mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0); if (LIST_FIRST(&mii->mii_phys) == NULL) { ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL); ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE); } else ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO); /* attach the interface */ if_attach(ifp); Ether_ifattach(ifp, eaddr); #elif defined(__FreeBSD__) if (mii_phy_probe(self, &sc->sc_miibus, udav_ifmedia_change, udav_ifmedia_status)) { printf("%s: MII without any PHY!\n", device_get_nameunit(sc->sc_dev)); if_free(ifp); UDAV_UNLOCK(sc); mtx_destroy(&sc->sc_mtx); USB_ATTACH_ERROR_RETURN; } sc->sc_qdat.ifp = ifp; sc->sc_qdat.if_rxstart = udav_rxstart; /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); #endif #if defined(NRND) && NRND > 0 rnd_attach_source(&sc->rnd_source, devname, RND_TYPE_NET, 0); #endif usb_callout_init(sc->sc_stat_ch); #if defined(__FreeBSD__) usb_register_netisr(); #endif sc->sc_attached = 1; #if defined(__NetBSD__) splx(s); #elif defined(__FreeBSD__) UDAV_UNLOCK(sc); #endif usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, dev, USBDEV(sc->sc_dev)); USB_ATTACH_SUCCESS_RETURN; bad: sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; } /* detach */ USB_DETACH(udav) { USB_DETACH_START(udav, sc); struct ifnet *ifp = GET_IFP(sc); #if defined(__NetBSD__) int s; #endif DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); /* Detached before attached finished */ if (!sc->sc_attached) return (0); UDAV_LOCK(sc); usb_uncallout(sc->sc_stat_ch, udav_tick, sc); /* Remove any pending tasks */ usb_rem_task(sc->sc_udev, &sc->sc_tick_task); usb_rem_task(sc->sc_udev, &sc->sc_stop_task); #if defined(__NetBSD__) s = splusb(); #elif defined(__FreeBSD__) UDAV_LOCK(sc); #endif if (--sc->sc_refcnt >= 0) { /* Wait for processes to go away */ usb_detach_wait(USBDEV(sc->sc_dev)); } #if defined(__FreeBSD__) if (ifp->if_drv_flags & IFF_DRV_RUNNING) #else if (ifp->if_flags & IFF_RUNNING) #endif udav_stop(GET_IFP(sc), 1); #if defined(NRND) && NRND > 0 rnd_detach_source(&sc->rnd_source); #endif #if defined(__NetBSD__) mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY); ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY); #endif ether_ifdetach(ifp); #if defined(__NetBSD__) if_detach(ifp); #endif #if defined(__FreeBSD__) if_free(ifp); #endif #ifdef DIAGNOSTIC if (sc->sc_pipe_tx != NULL) printf("%s: detach has active tx endpoint.\n", device_get_nameunit(sc->sc_dev)); if (sc->sc_pipe_rx != NULL) printf("%s: detach has active rx endpoint.\n", device_get_nameunit(sc->sc_dev)); if (sc->sc_pipe_intr != NULL) printf("%s: detach has active intr endpoint.\n", device_get_nameunit(sc->sc_dev)); #endif sc->sc_attached = 0; #if defined(__NetBSD__) splx(s); #elif defined(__FreeBSD__) UDAV_UNLOCK(sc); #endif #if defined(__FreeBSD__) mtx_destroy(&sc->sc_mtx); #endif usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, USBDEV(sc->sc_dev)); return (0); } #if 0 /* read memory */ static int udav_mem_read(struct udav_softc *sc, int offset, void *buf, int len) { usb_device_request_t req; usbd_status err; if (sc == NULL) return (0); DPRINTFN(0x200, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); if (sc->sc_dying) return (0); offset &= 0xffff; len &= 0xff; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = UDAV_REQ_MEM_READ; USETW(req.wValue, 0x0000); USETW(req.wIndex, offset); USETW(req.wLength, len); sc->sc_refcnt++; err = usbd_do_request(sc->sc_udev, &req, buf); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); if (err) { DPRINTF(("%s: %s: read failed. off=%04x, err=%d\n", device_get_nameunit(sc->sc_dev), __func__, offset, err)); } return (err); } /* write memory */ static int udav_mem_write(struct udav_softc *sc, int offset, void *buf, int len) { usb_device_request_t req; usbd_status err; if (sc == NULL) return (0); DPRINTFN(0x200, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); if (sc->sc_dying) return (0); offset &= 0xffff; len &= 0xff; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UDAV_REQ_MEM_WRITE; USETW(req.wValue, 0x0000); USETW(req.wIndex, offset); USETW(req.wLength, len); sc->sc_refcnt++; err = usbd_do_request(sc->sc_udev, &req, buf); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); if (err) { DPRINTF(("%s: %s: write failed. off=%04x, err=%d\n", device_get_nameunit(sc->sc_dev), __func__, offset, err)); } return (err); } /* write memory */ static int udav_mem_write1(struct udav_softc *sc, int offset, unsigned char ch) { usb_device_request_t req; usbd_status err; if (sc == NULL) return (0); DPRINTFN(0x200, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); if (sc->sc_dying) return (0); offset &= 0xffff; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UDAV_REQ_MEM_WRITE1; USETW(req.wValue, ch); USETW(req.wIndex, offset); USETW(req.wLength, 0x0000); sc->sc_refcnt++; err = usbd_do_request(sc->sc_udev, &req, NULL); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); if (err) { DPRINTF(("%s: %s: write failed. off=%04x, err=%d\n", device_get_nameunit(sc->sc_dev), __func__, offset, err)); } return (err); } #endif /* read register(s) */ static int udav_csr_read(struct udav_softc *sc, int offset, void *buf, int len) { usb_device_request_t req; usbd_status err; if (sc == NULL) return (0); DPRINTFN(0x200, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); if (sc->sc_dying) return (0); offset &= 0xff; len &= 0xff; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = UDAV_REQ_REG_READ; USETW(req.wValue, 0x0000); USETW(req.wIndex, offset); USETW(req.wLength, len); sc->sc_refcnt++; err = usbd_do_request(sc->sc_udev, &req, buf); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); if (err) { DPRINTF(("%s: %s: read failed. off=%04x, err=%d\n", device_get_nameunit(sc->sc_dev), __func__, offset, err)); } return (err); } /* write register(s) */ static int udav_csr_write(struct udav_softc *sc, int offset, void *buf, int len) { usb_device_request_t req; usbd_status err; if (sc == NULL) return (0); DPRINTFN(0x200, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); if (sc->sc_dying) return (0); offset &= 0xff; len &= 0xff; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UDAV_REQ_REG_WRITE; USETW(req.wValue, 0x0000); USETW(req.wIndex, offset); USETW(req.wLength, len); sc->sc_refcnt++; err = usbd_do_request(sc->sc_udev, &req, buf); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); if (err) { DPRINTF(("%s: %s: write failed. off=%04x, err=%d\n", device_get_nameunit(sc->sc_dev), __func__, offset, err)); } return (err); } static int udav_csr_read1(struct udav_softc *sc, int offset) { u_int8_t val = 0; if (sc == NULL) return (0); DPRINTFN(0x200, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); if (sc->sc_dying) return (0); return (udav_csr_read(sc, offset, &val, 1) ? 0 : val); } /* write a register */ static int udav_csr_write1(struct udav_softc *sc, int offset, unsigned char ch) { usb_device_request_t req; usbd_status err; if (sc == NULL) return (0); DPRINTFN(0x200, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); if (sc->sc_dying) return (0); offset &= 0xff; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UDAV_REQ_REG_WRITE1; USETW(req.wValue, ch); USETW(req.wIndex, offset); USETW(req.wLength, 0x0000); sc->sc_refcnt++; err = usbd_do_request(sc->sc_udev, &req, NULL); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); if (err) { DPRINTF(("%s: %s: write failed. off=%04x, err=%d\n", device_get_nameunit(sc->sc_dev), __func__, offset, err)); } return (err); } #if defined(__NetBSD__) static int udav_init(struct ifnet *ifp) #elif defined(__FreeBSD__) static void udav_init(void *xsc) #endif { #if defined(__NetBSD__) struct udav_softc *sc = ifp->if_softc; #elif defined(__FreeBSD__) struct udav_softc *sc = (struct udav_softc *)xsc; struct ifnet *ifp = GET_IFP(sc); #endif struct mii_data *mii = GET_MII(sc); u_char *eaddr; #if defined(__NetBSD__) int s; #endif DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); if (sc->sc_dying) #if defined(__NetBSD__) return (EIO); #elif defined(__FreeBSD__) return ; #endif #if defined(__NetBSD__) s = splnet(); #elif defined(__FreeBSD__) UDAV_LOCK(sc); #endif /* Cancel pending I/O and free all TX/RX buffers */ udav_stop(ifp, 1); #if defined(__NetBSD__) eaddr = LLADDR(ifp->if_sadl); #elif defined(__FreeBSD__) eaddr = IF_LLADDR(ifp); #endif udav_csr_write(sc, UDAV_PAR, eaddr, ETHER_ADDR_LEN); /* Initialize network control register */ /* Disable loopback */ UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_LBK0 | UDAV_NCR_LBK1); /* Initialize RX control register */ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_DIS_LONG | UDAV_RCR_DIS_CRC); /* If we want promiscuous mode, accept all physical frames. */ if (ifp->if_flags & IFF_PROMISC) UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC); else UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC); /* Initialize transmit ring */ if (usb_ether_tx_list_init(sc, &sc->sc_cdata, sc->sc_udev) == ENOBUFS) { printf("%s: tx list init failed\n", device_get_nameunit(sc->sc_dev)); #if defined(__NetBSD__) splx(s); return (EIO); #elif defined(__FreeBSD__) UDAV_UNLOCK(sc); return ; #endif } /* Initialize receive ring */ if (usb_ether_rx_list_init(sc, &sc->sc_cdata, sc->sc_udev) == ENOBUFS) { printf("%s: rx list init failed\n", device_get_nameunit(sc->sc_dev)); #if defined(__NetBSD__) splx(s); return (EIO); #elif defined(__FreeBSD__) UDAV_UNLOCK(sc); return ; #endif } /* Load the multicast filter */ udav_setmulti(sc); /* Enable RX */ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_RXEN); /* clear POWER_DOWN state of internal PHY */ UDAV_SETBIT(sc, UDAV_GPCR, UDAV_GPCR_GEP_CNTL0); UDAV_CLRBIT(sc, UDAV_GPR, UDAV_GPR_GEPIO0); mii_mediachg(mii); if (sc->sc_pipe_tx == NULL || sc->sc_pipe_rx == NULL) { if (udav_openpipes(sc)) { #if defined(__NetBSD__) splx(s); return (EIO); #elif defined(__FreeBSD__) UDAV_UNLOCK(sc); return ; #endif } } #if defined(__FreeBSD__) ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; #else ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; #endif #if defined(__NetBSD__) splx(s); #elif defined(__FreeBSD__) UDAV_UNLOCK(sc); #endif usb_callout(sc->sc_stat_ch, hz, udav_tick, sc); #if defined(__NetBSD__) return (0); #elif defined(__FreeBSD__) return ; #endif } static void udav_reset(struct udav_softc *sc) { int i; DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); if (sc->sc_dying) return; /* Select PHY */ #if 1 /* * XXX: force select internal phy. * external phy routines are not tested. */ UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); #else if (sc->sc_flags & UDAV_EXT_PHY) { UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); } else { UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); } #endif UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_RST); for (i = 0; i < UDAV_TX_TIMEOUT; i++) { if (!(udav_csr_read1(sc, UDAV_NCR) & UDAV_NCR_RST)) break; delay(10); /* XXX */ } delay(10000); /* XXX */ } #if defined(__NetBSD__) || defined(__OpenBSD__) int udav_activate(device_t self, enum devact act) { struct udav_softc *sc = (struct udav_softc *)self; DPRINTF(("%s: %s: enter, act=%d\n", device_get_nameunit(sc->sc_dev), __func__, act)); switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); break; case DVACT_DEACTIVATE: if_deactivate(&sc->sc_ec.ec_if); sc->sc_dying = 1; break; } return (0); } #endif #define UDAV_BITS 6 #define UDAV_CALCHASH(addr) \ (ether_crc32_le((addr), ETHER_ADDR_LEN) & ((1 << UDAV_BITS) - 1)) static void udav_setmulti(struct udav_softc *sc) { struct ifnet *ifp; #if defined(__NetBSD__) struct ether_multi *enm; struct ether_multistep step; #elif defined(__FreeBSD__) struct ifmultiaddr *ifma; #endif u_int8_t hashes[8]; int h = 0; DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); if (sc->sc_dying) return; ifp = GET_IFP(sc); if (ifp->if_flags & IFF_PROMISC) { UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC); return; } else if (ifp->if_flags & IFF_ALLMULTI) { #if defined(__NetBSD__) allmulti: #endif ifp->if_flags |= IFF_ALLMULTI; UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL); UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_PRMSC); return; } /* first, zot all the existing hash bits */ memset(hashes, 0x00, sizeof(hashes)); hashes[7] |= 0x80; /* broadcast address */ udav_csr_write(sc, UDAV_MAR, hashes, sizeof(hashes)); /* now program new ones */ #if defined(__NetBSD__) ETHER_FIRST_MULTI(step, &sc->sc_ec, enm); while (enm != NULL) { if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN) != 0) goto allmulti; h = UDAV_CALCHASH(enm->enm_addrlo); hashes[h>>3] |= 1 << (h & 0x7); ETHER_NEXT_MULTI(step, enm); } #elif defined(__FreeBSD__) IF_ADDR_LOCK(ifp); -#if __FreeBSD_version >= 500000 TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) -#else - LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) -#endif { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = UDAV_CALCHASH(LLADDR((struct sockaddr_dl *) ifma->ifma_addr)); hashes[h>>3] |= 1 << (h & 0x7); } IF_ADDR_UNLOCK(ifp); #endif /* disable all multicast */ ifp->if_flags &= ~IFF_ALLMULTI; UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_ALL); /* write hash value to the register */ udav_csr_write(sc, UDAV_MAR, hashes, sizeof(hashes)); } static int udav_openpipes(struct udav_softc *sc) { struct ue_chain *c; usbd_status err; int i; int error = 0; if (sc->sc_dying) return (EIO); sc->sc_refcnt++; /* Open RX pipe */ err = usbd_open_pipe(sc->sc_ctl_iface, sc->sc_bulkin_no, USBD_EXCLUSIVE_USE, &sc->sc_pipe_rx); if (err) { printf("%s: open rx pipe failed: %s\n", device_get_nameunit(sc->sc_dev), usbd_errstr(err)); error = EIO; goto done; } /* Open TX pipe */ err = usbd_open_pipe(sc->sc_ctl_iface, sc->sc_bulkout_no, USBD_EXCLUSIVE_USE, &sc->sc_pipe_tx); if (err) { printf("%s: open tx pipe failed: %s\n", device_get_nameunit(sc->sc_dev), usbd_errstr(err)); error = EIO; goto done; } #if 0 /* XXX: interrupt endpoint is not yet supported */ /* Open Interrupt pipe */ err = usbd_open_pipe_intr(sc->sc_ctl_iface, sc->sc_intrin_no, USBD_EXCLUSIVE_USE, &sc->sc_pipe_intr, sc, &sc->sc_cdata.ue_ibuf, UDAV_INTR_PKGLEN, udav_intr, UDAV_INTR_INTERVAL); if (err) { printf("%s: open intr pipe failed: %s\n", device_get_nameunit(sc->sc_dev), usbd_errstr(err)); error = EIO; goto done; } #endif /* Start up the receive pipe. */ for (i = 0; i < UE_RX_LIST_CNT; i++) { c = &sc->sc_cdata.ue_rx_chain[i]; usbd_setup_xfer(c->ue_xfer, sc->sc_pipe_rx, c, c->ue_buf, UE_BUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT, udav_rxeof); (void)usbd_transfer(c->ue_xfer); DPRINTF(("%s: %s: start read\n", device_get_nameunit(sc->sc_dev), __func__)); } done: if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); return (error); } static void udav_start(struct ifnet *ifp) { struct udav_softc *sc = ifp->if_softc; struct mbuf *m_head = NULL; DPRINTF(("%s: %s: enter, link=%d\n", device_get_nameunit(sc->sc_dev), __func__, sc->sc_link)); if (sc->sc_dying) return; if (!sc->sc_link) return; #if defined(__FreeBSD__) if (ifp->if_drv_flags & IFF_DRV_OACTIVE) #else if (ifp->if_flags & IFF_OACTIVE) #endif return; #if defined(__NetBSD__) IFQ_POLL(&ifp->if_snd, m_head); #elif defined(__FreeBSD__) IF_DEQUEUE(&ifp->if_snd, m_head); #endif if (m_head == NULL) return; if (udav_send(sc, m_head, 0)) { #if defined(__FreeBSD__) IF_PREPEND(&ifp->if_snd, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; #else ifp->if_flags |= IFF_OACTIVE; #endif return; } #if defined(__NetBSD__) IFQ_DEQUEUE(&ifp->if_snd, m_head); #endif #if NBPFILTER > 0 BPF_MTAP(ifp, m_head); #endif #if defined(__FreeBSD__) ifp->if_drv_flags |= IFF_DRV_OACTIVE; #else ifp->if_flags |= IFF_OACTIVE; #endif /* Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; } static int udav_send(struct udav_softc *sc, struct mbuf *m, int idx) { int total_len; struct ue_chain *c; usbd_status err; DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev),__func__)); c = &sc->sc_cdata.ue_tx_chain[idx]; /* Copy the mbuf data into a contiguous buffer */ /* first 2 bytes are packet length */ m_copydata(m, 0, m->m_pkthdr.len, c->ue_buf + 2); c->ue_mbuf = m; total_len = m->m_pkthdr.len; if (total_len < UDAV_MIN_FRAME_LEN) { memset(c->ue_buf + 2 + total_len, 0, UDAV_MIN_FRAME_LEN - total_len); total_len = UDAV_MIN_FRAME_LEN; } /* Frame length is specified in the first 2bytes of the buffer */ c->ue_buf[0] = (u_int8_t)total_len; c->ue_buf[1] = (u_int8_t)(total_len >> 8); total_len += 2; usbd_setup_xfer(c->ue_xfer, sc->sc_pipe_tx, c, c->ue_buf, total_len, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, UDAV_TX_TIMEOUT, udav_txeof); /* Transmit */ sc->sc_refcnt++; err = usbd_transfer(c->ue_xfer); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); if (err != USBD_IN_PROGRESS) { printf("%s: udav_send error=%s\n", device_get_nameunit(sc->sc_dev), usbd_errstr(err)); /* Stop the interface */ usb_add_task(sc->sc_udev, &sc->sc_stop_task, USB_TASKQ_DRIVER); return (EIO); } DPRINTF(("%s: %s: send %d bytes\n", device_get_nameunit(sc->sc_dev), __func__, total_len)); sc->sc_cdata.ue_tx_cnt++; return (0); } static void udav_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct ue_chain *c = priv; struct udav_softc *sc = c->ue_sc; struct ifnet *ifp = GET_IFP(sc); #if defined(__NetBSD__) int s; #endif if (sc->sc_dying) return; #if defined(__NetBSD__) s = splnet(); #elif defined(__FreeBSD__) UDAV_LOCK(sc); #endif DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); ifp->if_timer = 0; #if defined(__FreeBSD__) ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; #else ifp->if_flags &= ~IFF_OACTIVE; #endif if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { #if defined(__NetBSD__) splx(s); #elif defined(__FreeBSD__) UDAV_UNLOCK(sc); #endif return; } ifp->if_oerrors++; printf("%s: usb error on tx: %s\n", device_get_nameunit(sc->sc_dev), usbd_errstr(status)); if (status == USBD_STALLED) { sc->sc_refcnt++; usbd_clear_endpoint_stall(sc->sc_pipe_tx); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); } #if defined(__NetBSD__) splx(s); #elif defined(__FreeBSD__) UDAV_UNLOCK(sc); #endif return; } ifp->if_opackets++; m_freem(c->ue_mbuf); c->ue_mbuf = NULL; #if defined(__NetBSD__) if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) #elif defined(__FreeBSD__) if ( ifp->if_snd.ifq_head != NULL ) #endif udav_start(ifp); #if defined(__NetBSD__) splx(s); #elif defined(__FreeBSD__) UDAV_UNLOCK(sc); #endif } static void udav_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct ue_chain *c = priv; struct udav_softc *sc = c->ue_sc; struct ifnet *ifp = GET_IFP(sc); struct mbuf *m; u_int32_t total_len; u_int8_t *pktstat; #if defined(__NetBSD__) int s; #endif DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev),__func__)); if (sc->sc_dying) return; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) return; sc->sc_rx_errs++; if (usbd_ratecheck(&sc->sc_rx_notice)) { printf("%s: %u usb errors on rx: %s\n", device_get_nameunit(sc->sc_dev), sc->sc_rx_errs, usbd_errstr(status)); sc->sc_rx_errs = 0; } if (status == USBD_STALLED) { sc->sc_refcnt++; usbd_clear_endpoint_stall(sc->sc_pipe_rx); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); } goto done; } usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); /* copy data to mbuf */ m = c->ue_mbuf; memcpy(mtod(m, char *), c->ue_buf, total_len); /* first byte in received data */ pktstat = mtod(m, u_int8_t *); m_adj(m, sizeof(u_int8_t)); DPRINTF(("%s: RX Status: 0x%02x\n", device_get_nameunit(sc->sc_dev), *pktstat)); total_len = UGETW(mtod(m, u_int8_t *)); m_adj(m, sizeof(u_int16_t)); if (*pktstat & UDAV_RSR_LCS) { ifp->if_collisions++; goto done; } if (total_len < sizeof(struct ether_header) || *pktstat & UDAV_RSR_ERR) { ifp->if_ierrors++; goto done; } ifp->if_ipackets++; total_len -= ETHER_CRC_LEN; m->m_pkthdr.len = m->m_len = total_len; #if defined(__NetBSD__) m->m_pkthdr.rcvif = ifp; #elif defined(__FreeBSD__) m->m_pkthdr.rcvif = (struct ifnet *)&sc->sc_qdat; #endif #if defined(__NetBSD__) s = splnet(); #elif defined(__FreeBSD__) UDAV_LOCK(sc); #endif #if defined(__NetBSD__) c->ue_mbuf = usb_ether_newbuf(); if (c->ue_mbuf == NULL) { printf("%s: no memory for rx list " "-- packet dropped!\n", device_get_nameunit(sc->sc_dev)); ifp->if_ierrors++; goto done1; } #endif #if NBPFILTER > 0 BPF_MTAP(ifp, m); #endif DPRINTF(("%s: %s: deliver %d\n", device_get_nameunit(sc->sc_dev), __func__, m->m_len)); #if defined(__NetBSD__) IF_INPUT(ifp, m); #endif #if defined(__FreeBSD__) usb_ether_input(m); UDAV_UNLOCK(sc); return ; #endif #if defined(__NetBSD__) done1: splx(s); #elif defined(__FreeBSD__) UDAV_UNLOCK(sc); #endif done: /* Setup new transfer */ usbd_setup_xfer(xfer, sc->sc_pipe_rx, c, c->ue_buf, UE_BUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT, udav_rxeof); sc->sc_refcnt++; usbd_transfer(xfer); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); DPRINTF(("%s: %s: start rx\n", device_get_nameunit(sc->sc_dev), __func__)); } #if 0 static void udav_intr() { } #endif static int udav_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct udav_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; struct mii_data *mii; #if defined(__NetBSD__) int s; #endif int error = 0; DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); if (sc->sc_dying) return (EIO); #if defined(__NetBSD__) s = splnet(); #elif defined(__FreeBSD__) UDAV_LOCK(sc); #endif switch (cmd) { #if defined(__FreeBSD__) case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING && ifp->if_flags & IFF_PROMISC) { UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC); } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && !(ifp->if_flags & IFF_PROMISC)) { if (ifp->if_flags & IFF_ALLMULTI) UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_PRMSC); else UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC); } else if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) udav_init(sc); } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) udav_stop(ifp, 1); } error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: udav_setmulti(sc); error = 0; break; #endif case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = GET_MII(sc); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); break; default: error = ether_ioctl(ifp, cmd, data); #if defined(__NetBSD__) if (error == ENETRESET) { udav_setmulti(sc); error = 0; } #endif break; } #if defined(__NetBSD__) splx(s); #elif defined(__FreeBSD__) UDAV_UNLOCK(sc); #endif return (error); } static void udav_watchdog(struct ifnet *ifp) { struct udav_softc *sc = ifp->if_softc; struct ue_chain *c; usbd_status stat; #if defined(__NetBSD__) int s; #endif DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); ifp->if_oerrors++; printf("%s: watchdog timeout\n", device_get_nameunit(sc->sc_dev)); #if defined(__NetBSD__) s = splusb(); #elif defined(__FreeBSD__) UDAV_LOCK(sc) #endif c = &sc->sc_cdata.ue_tx_chain[0]; usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &stat); udav_txeof(c->ue_xfer, c, stat); #if defined(__NetBSD__) if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) #elif defined(__FreeBSD__) if ( ifp->if_snd.ifq_head != NULL ) #endif udav_start(ifp); #if defined(__NetBSD__) splx(s); #elif defined(__FreeBSD__) UDAV_UNLOCK(sc); #endif } static void udav_stop_task(struct udav_softc *sc) { udav_stop(GET_IFP(sc), 1); } /* Stop the adapter and free any mbufs allocated to the RX and TX lists. */ static void udav_stop(struct ifnet *ifp, int disable) { struct udav_softc *sc = ifp->if_softc; usbd_status err; DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); ifp->if_timer = 0; udav_reset(sc); usb_uncallout(sc->sc_stat_ch, udav_tick, sc); /* Stop transfers */ /* RX endpoint */ if (sc->sc_pipe_rx != NULL) { err = usbd_abort_pipe(sc->sc_pipe_rx); if (err) printf("%s: abort rx pipe failed: %s\n", device_get_nameunit(sc->sc_dev), usbd_errstr(err)); err = usbd_close_pipe(sc->sc_pipe_rx); if (err) printf("%s: close rx pipe failed: %s\n", device_get_nameunit(sc->sc_dev), usbd_errstr(err)); sc->sc_pipe_rx = NULL; } /* TX endpoint */ if (sc->sc_pipe_tx != NULL) { err = usbd_abort_pipe(sc->sc_pipe_tx); if (err) printf("%s: abort tx pipe failed: %s\n", device_get_nameunit(sc->sc_dev), usbd_errstr(err)); err = usbd_close_pipe(sc->sc_pipe_tx); if (err) printf("%s: close tx pipe failed: %s\n", device_get_nameunit(sc->sc_dev), usbd_errstr(err)); sc->sc_pipe_tx = NULL; } #if 0 /* XXX: Interrupt endpoint is not yet supported!! */ /* Interrupt endpoint */ if (sc->sc_pipe_intr != NULL) { err = usbd_abort_pipe(sc->sc_pipe_intr); if (err) printf("%s: abort intr pipe failed: %s\n", device_get_nameunit(sc->sc_dev), usbd_errstr(err)); err = usbd_close_pipe(sc->sc_pipe_intr); if (err) printf("%s: close intr pipe failed: %s\n", device_get_nameunit(sc->sc_dev), usbd_errstr(err)); sc->sc_pipe_intr = NULL; } #endif /* Free RX resources. */ usb_ether_rx_list_free(&sc->sc_cdata); /* Free TX resources. */ usb_ether_tx_list_free(&sc->sc_cdata); sc->sc_link = 0; #if defined(__FreeBSD__) ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); #else ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); #endif } /* Set media options */ static int udav_ifmedia_change(struct ifnet *ifp) { struct udav_softc *sc = ifp->if_softc; struct mii_data *mii = GET_MII(sc); DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); if (sc->sc_dying) return (0); sc->sc_link = 0; if (mii->mii_instance) { struct mii_softc *miisc; for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL; miisc = LIST_NEXT(miisc, mii_list)) mii_phy_reset(miisc); } return (mii_mediachg(mii)); } /* Report current media status. */ static void udav_ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr) { struct udav_softc *sc = ifp->if_softc; struct mii_data *mii = GET_MII(sc); DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); if (sc->sc_dying) return; #if defined(__FreeBSD__) if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { #else if ((ifp->if_flags & IFF_RUNNING) == 0) { #endif ifmr->ifm_active = IFM_ETHER | IFM_NONE; ifmr->ifm_status = 0; return; } mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; } static void udav_tick(void *xsc) { struct udav_softc *sc = xsc; if (sc == NULL) return; DPRINTFN(0xff, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); if (sc->sc_dying) return; /* Perform periodic stuff in process context */ usb_add_task(sc->sc_udev, &sc->sc_tick_task, USB_TASKQ_DRIVER); } static void udav_tick_task(void *xsc) { struct udav_softc *sc = xsc; struct ifnet *ifp; struct mii_data *mii; #if defined(__NetBSD__) int s; #endif if (sc == NULL) return; DPRINTFN(0xff, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); if (sc->sc_dying) return; ifp = GET_IFP(sc); mii = GET_MII(sc); if (mii == NULL) return; #if defined(__NetBSD__) s = splnet(); #elif defined(__FreeBSD__) UDAV_LOCK(sc); #endif mii_tick(mii); if (!sc->sc_link) { mii_pollstat(mii); if (mii->mii_media_status & IFM_ACTIVE && IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { DPRINTF(("%s: %s: got link\n", device_get_nameunit(sc->sc_dev), __func__)); sc->sc_link++; #if defined(__NetBSD__) if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) #elif defined(__FreeBSD__) if ( ifp->if_snd.ifq_head != NULL ) #endif udav_start(ifp); } } usb_callout(sc->sc_stat_ch, hz, udav_tick, sc); #if defined(__NetBSD__) splx(s); #elif defined(__FreeBSD__) UDAV_UNLOCK(sc); #endif } /* Get exclusive access to the MII registers */ static void udav_lock_mii(struct udav_softc *sc) { DPRINTFN(0xff, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); sc->sc_refcnt++; #if defined(__NetBSD__) lockmgr(&sc->sc_mii_lock, LK_EXCLUSIVE, NULL); #elif defined(__FreeBSD__) lockmgr(&sc->sc_mii_lock, LK_EXCLUSIVE, NULL, NULL); #endif } static void udav_unlock_mii(struct udav_softc *sc) { DPRINTFN(0xff, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); #if defined(__NetBSD__) lockmgr(&sc->sc_mii_lock, LK_RELEASE, NULL); #elif defined(__FreeBSD__) lockmgr(&sc->sc_mii_lock, LK_RELEASE, NULL, NULL); #endif if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); } static int udav_miibus_readreg(device_t dev, int phy, int reg) { struct udav_softc *sc; u_int8_t val[2]; u_int16_t data16; if (dev == NULL) return (0); sc = USBGETSOFTC(dev); DPRINTFN(0xff, ("%s: %s: enter, phy=%d reg=0x%04x\n", device_get_nameunit(sc->sc_dev), __func__, phy, reg)); if (sc->sc_dying) { #ifdef DIAGNOSTIC printf("%s: %s: dying\n", device_get_nameunit(sc->sc_dev), __func__); #endif return (0); } /* XXX: one PHY only for the internal PHY */ if (phy != 0) { DPRINTFN(0xff, ("%s: %s: phy=%d is not supported\n", device_get_nameunit(sc->sc_dev), __func__, phy)); return (0); } udav_lock_mii(sc); /* select internal PHY and set PHY register address */ udav_csr_write1(sc, UDAV_EPAR, UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK)); /* select PHY operation and start read command */ udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRR); /* XXX: should be wait? */ /* end read command */ UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRR); /* retrieve the result from data registers */ udav_csr_read(sc, UDAV_EPDRL, val, 2); udav_unlock_mii(sc); data16 = val[0] | (val[1] << 8); DPRINTFN(0xff, ("%s: %s: phy=%d reg=0x%04x => 0x%04x\n", device_get_nameunit(sc->sc_dev), __func__, phy, reg, data16)); return (data16); } static void udav_miibus_writereg(device_t dev, int phy, int reg, int data) { struct udav_softc *sc; u_int8_t val[2]; if (dev == NULL) return; sc = USBGETSOFTC(dev); DPRINTFN(0xff, ("%s: %s: enter, phy=%d reg=0x%04x data=0x%04x\n", device_get_nameunit(sc->sc_dev), __func__, phy, reg, data)); if (sc->sc_dying) { #ifdef DIAGNOSTIC printf("%s: %s: dying\n", device_get_nameunit(sc->sc_dev), __func__); #endif return; } /* XXX: one PHY only for the internal PHY */ if (phy != 0) { DPRINTFN(0xff, ("%s: %s: phy=%d is not supported\n", device_get_nameunit(sc->sc_dev), __func__, phy)); return; } udav_lock_mii(sc); /* select internal PHY and set PHY register address */ udav_csr_write1(sc, UDAV_EPAR, UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK)); /* put the value to the data registers */ val[0] = data & 0xff; val[1] = (data >> 8) & 0xff; udav_csr_write(sc, UDAV_EPDRL, val, 2); /* select PHY operation and start write command */ udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRW); /* XXX: should be wait? */ /* end write command */ UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRW); udav_unlock_mii(sc); return; } static void udav_miibus_statchg(device_t dev) { #ifdef UDAV_DEBUG struct udav_softc *sc; if (dev == NULL) return; sc = USBGETSOFTC(dev); DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__)); #endif /* Nothing to do */ } #if defined(__FreeBSD__) /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static void udav_shutdown(device_t dev) { struct udav_softc *sc; sc = device_get_softc(dev); udav_stop_task(sc); return; } static void udav_rxstart(struct ifnet *ifp) { struct udav_softc *sc; struct ue_chain *c; sc = ifp->if_softc; UDAV_LOCK(sc); c = &sc->sc_cdata.ue_rx_chain[sc->sc_cdata.ue_rx_prod]; c->ue_mbuf = usb_ether_newbuf(); if (c->ue_mbuf == NULL) { printf("%s: no memory for rx list " "-- packet dropped!\n", device_get_nameunit(sc->sc_dev)); ifp->if_ierrors++; UDAV_UNLOCK(sc); return; } /* Setup new transfer. */ usbd_setup_xfer(c->ue_xfer, sc->sc_pipe_rx, c, c->ue_buf, UE_BUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT, udav_rxeof); usbd_transfer(c->ue_xfer); UDAV_UNLOCK(sc); return; } #endif Index: head/sys/dev/usb/uhid.c =================================================================== --- head/sys/dev/usb/uhid.c (revision 169488) +++ head/sys/dev/usb/uhid.c (revision 169489) @@ -1,801 +1,795 @@ /* $NetBSD: uhid.c,v 1.46 2001/11/13 06:24:55 lukem Exp $ */ /* Also already merged from NetBSD: * $NetBSD: uhid.c,v 1.54 2002/09/23 05:51:21 simonb Exp $ */ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf */ #include #include #include #include #include -#if __FreeBSD_version >= 500000 #include -#endif #include #include #if defined(__NetBSD__) || defined(__OpenBSD__) #include #include #include #elif defined(__FreeBSD__) #include #include #include #include #include #endif #include #include #if __FreeBSD_version >= 500014 #include #else #include #endif #include #include #include #include #include #include #include "usbdevs.h" #include #include #include /* Replacement report descriptors for devices shipped with broken ones */ #include #include /* For hid blacklist quirk */ #include #ifdef USB_DEBUG #define DPRINTF(x) if (uhiddebug) logprintf x #define DPRINTFN(n,x) if (uhiddebug>(n)) logprintf x int uhiddebug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, uhid, CTLFLAG_RW, 0, "USB uhid"); SYSCTL_INT(_hw_usb_uhid, OID_AUTO, debug, CTLFLAG_RW, &uhiddebug, 0, "uhid debug level"); #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif struct uhid_softc { device_t sc_dev; /* base device */ usbd_device_handle sc_udev; usbd_interface_handle sc_iface; /* interface */ usbd_pipe_handle sc_intrpipe; /* interrupt pipe */ int sc_ep_addr; int sc_isize; int sc_osize; int sc_fsize; u_int8_t sc_iid; u_int8_t sc_oid; u_int8_t sc_fid; u_char *sc_ibuf; u_char *sc_obuf; void *sc_repdesc; int sc_repdesc_size; struct clist sc_q; struct selinfo sc_rsel; struct proc *sc_async; /* process that wants SIGIO */ u_char sc_state; /* driver state */ #define UHID_OPEN 0x01 /* device is open */ #define UHID_ASLP 0x02 /* waiting for device data */ #define UHID_NEEDCLEAR 0x04 /* needs clearing endpoint stall */ #define UHID_IMMED 0x08 /* return read data immediately */ int sc_refcnt; u_char sc_dying; #if defined(__FreeBSD__) struct cdev *dev; #endif }; #define UHIDUNIT(dev) (minor(dev)) #define UHID_CHUNK 128 /* chunk size for read */ #define UHID_BSIZE 1020 /* buffer size */ #if defined(__NetBSD__) || defined(__OpenBSD__) cdev_decl(uhid); #elif defined(__FreeBSD__) d_open_t uhidopen; d_close_t uhidclose; d_read_t uhidread; d_write_t uhidwrite; d_ioctl_t uhidioctl; d_poll_t uhidpoll; static struct cdevsw uhid_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = uhidopen, .d_close = uhidclose, .d_read = uhidread, .d_write = uhidwrite, .d_ioctl = uhidioctl, .d_poll = uhidpoll, .d_name = "uhid", #if __FreeBSD_version < 500014 .d_bmaj -1 #endif }; #endif static void uhid_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); static int uhid_do_read(struct uhid_softc *, struct uio *uio, int); static int uhid_do_write(struct uhid_softc *, struct uio *uio, int); static int uhid_do_ioctl(struct uhid_softc *, u_long, caddr_t, int, usb_proc_ptr); USB_DECLARE_DRIVER(uhid); USB_MATCH(uhid) { USB_MATCH_START(uhid, uaa); usb_interface_descriptor_t *id; if (uaa->iface == NULL) return (UMATCH_NONE); id = usbd_get_interface_descriptor(uaa->iface); if (id == NULL) return (UMATCH_NONE); if (id->bInterfaceClass != UICLASS_HID) { /* The Xbox 360 gamepad doesn't use the HID class. */ if (id->bInterfaceClass != UICLASS_VENDOR || id->bInterfaceSubClass != UISUBCLASS_XBOX360_CONTROLLER || id->bInterfaceProtocol != UIPROTO_XBOX360_GAMEPAD) return (UMATCH_NONE); } if (usbd_get_quirks(uaa->device)->uq_flags & UQ_HID_IGNORE) return (UMATCH_NONE); #if 0 if (uaa->matchlvl) return (uaa->matchlvl); #endif return (UMATCH_IFACECLASS_GENERIC); } USB_ATTACH(uhid) { USB_ATTACH_START(uhid, sc, uaa); usbd_interface_handle iface = uaa->iface; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; int size; void *desc; const void *descptr; usbd_status err; char devinfo[1024]; sc->sc_udev = uaa->device; sc->sc_iface = iface; id = usbd_get_interface_descriptor(iface); usbd_devinfo(uaa->device, USBD_SHOW_INTERFACE_CLASS, devinfo); USB_ATTACH_SETUP; ed = usbd_interface2endpoint_descriptor(iface, 0); if (ed == NULL) { printf("%s: could not read endpoint descriptor\n", device_get_nameunit(sc->sc_dev)); sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; } DPRINTFN(10,("uhid_attach: bLength=%d bDescriptorType=%d " "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" " bInterval=%d\n", ed->bLength, ed->bDescriptorType, ed->bEndpointAddress & UE_ADDR, UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out", ed->bmAttributes & UE_XFERTYPE, UGETW(ed->wMaxPacketSize), ed->bInterval)); if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN || (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { printf("%s: unexpected endpoint\n", device_get_nameunit(sc->sc_dev)); sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; } sc->sc_ep_addr = ed->bEndpointAddress; descptr = NULL; if (uaa->vendor == USB_VENDOR_WACOM) { /* The report descriptor for the Wacom Graphire is broken. */ if (uaa->product == USB_PRODUCT_WACOM_GRAPHIRE) { size = sizeof uhid_graphire_report_descr; descptr = uhid_graphire_report_descr; } else if (uaa->product == USB_PRODUCT_WACOM_GRAPHIRE3_4X5) { static uByte reportbuf[] = {2, 2, 2}; /* * The Graphire3 needs 0x0202 to be written to * feature report ID 2 before it'll start * returning digitizer data. */ usbd_set_report(uaa->iface, UHID_FEATURE_REPORT, 2, &reportbuf, sizeof reportbuf); size = sizeof uhid_graphire3_4x5_report_descr; descptr = uhid_graphire3_4x5_report_descr; } } else if (id->bInterfaceClass == UICLASS_VENDOR && id->bInterfaceSubClass == UISUBCLASS_XBOX360_CONTROLLER && id->bInterfaceProtocol == UIPROTO_XBOX360_GAMEPAD) { static uByte reportbuf[] = {1, 3, 0}; /* The LEDs on the gamepad are blinking by default, turn off. */ usbd_set_report(uaa->iface, UHID_OUTPUT_REPORT, 0, &reportbuf, sizeof reportbuf); /* The Xbox 360 gamepad has no report descriptor. */ size = sizeof uhid_xb360gp_report_descr; descptr = uhid_xb360gp_report_descr; } if (descptr) { desc = malloc(size, M_USBDEV, M_NOWAIT); if (desc == NULL) err = USBD_NOMEM; else { err = USBD_NORMAL_COMPLETION; memcpy(desc, descptr, size); } } else { desc = NULL; err = usbd_read_report_desc(uaa->iface, &desc, &size,M_USBDEV); } if (err) { printf("%s: no report descriptor\n", device_get_nameunit(sc->sc_dev)); sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; } (void)usbd_set_idle(iface, 0, 0); sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid); sc->sc_osize = hid_report_size(desc, size, hid_output, &sc->sc_oid); sc->sc_fsize = hid_report_size(desc, size, hid_feature, &sc->sc_fid); sc->sc_repdesc = desc; sc->sc_repdesc_size = size; #if defined(__FreeBSD__) sc->dev = make_dev(&uhid_cdevsw, device_get_unit(self), UID_ROOT, GID_OPERATOR, 0644, "uhid%d", device_get_unit(self)); #endif USB_ATTACH_SUCCESS_RETURN; } #if defined(__NetBSD__) || defined(__OpenBSD__) int uhid_activate(device_t self, enum devact act) { struct uhid_softc *sc = (struct uhid_softc *)self; switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); case DVACT_DEACTIVATE: sc->sc_dying = 1; break; } return (0); } #endif USB_DETACH(uhid) { USB_DETACH_START(uhid, sc); int s; #if defined(__NetBSD__) || defined(__OpenBSD__) int maj, mn; #endif #if defined(__NetBSD__) || defined(__OpenBSD__) DPRINTF(("uhid_detach: sc=%p flags=%d\n", sc, flags)); #else DPRINTF(("uhid_detach: sc=%p\n", sc)); #endif sc->sc_dying = 1; if (sc->sc_intrpipe != NULL) usbd_abort_pipe(sc->sc_intrpipe); if (sc->sc_state & UHID_OPEN) { s = splusb(); if (--sc->sc_refcnt >= 0) { /* Wake everyone */ wakeup(&sc->sc_q); /* Wait for processes to go away. */ usb_detach_wait(USBDEV(sc->sc_dev)); } splx(s); } #if defined(__NetBSD__) || defined(__OpenBSD__) /* locate the major number */ for (maj = 0; maj < nchrdev; maj++) if (cdevsw[maj].d_open == uhidopen) break; /* Nuke the vnodes for any open instances (calls close). */ mn = self->dv_unit; vdevgone(maj, mn, mn, VCHR); #elif defined(__FreeBSD__) destroy_dev(sc->dev); #endif if (sc->sc_repdesc) free(sc->sc_repdesc, M_USBDEV); return (0); } void uhid_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) { struct uhid_softc *sc = addr; #ifdef USB_DEBUG if (uhiddebug > 5) { u_int32_t cc, i; usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL); DPRINTF(("uhid_intr: status=%d cc=%d\n", status, cc)); DPRINTF(("uhid_intr: data =")); for (i = 0; i < cc; i++) DPRINTF((" %02x", sc->sc_ibuf[i])); DPRINTF(("\n")); } #endif if (status == USBD_CANCELLED) return; if (status != USBD_NORMAL_COMPLETION) { DPRINTF(("uhid_intr: status=%d\n", status)); if (status == USBD_STALLED) sc->sc_state |= UHID_NEEDCLEAR; return; } (void) b_to_q(sc->sc_ibuf, sc->sc_isize, &sc->sc_q); if (sc->sc_state & UHID_ASLP) { sc->sc_state &= ~UHID_ASLP; DPRINTFN(5, ("uhid_intr: waking %p\n", &sc->sc_q)); wakeup(&sc->sc_q); } selwakeuppri(&sc->sc_rsel, PZERO); if (sc->sc_async != NULL) { DPRINTFN(3, ("uhid_intr: sending SIGIO %p\n", sc->sc_async)); PROC_LOCK(sc->sc_async); psignal(sc->sc_async, SIGIO); PROC_UNLOCK(sc->sc_async); } } int uhidopen(struct cdev *dev, int flag, int mode, usb_proc_ptr p) { struct uhid_softc *sc; usbd_status err; USB_GET_SC_OPEN(uhid, UHIDUNIT(dev), sc); DPRINTF(("uhidopen: sc=%p\n", sc)); if (sc->sc_dying) return (ENXIO); if (sc->sc_state & UHID_OPEN) return (EBUSY); sc->sc_state |= UHID_OPEN; if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1) { sc->sc_state &= ~UHID_OPEN; return (ENOMEM); } sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK); sc->sc_obuf = malloc(sc->sc_osize, M_USBDEV, M_WAITOK); /* Set up interrupt pipe. */ err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, sc->sc_ibuf, sc->sc_isize, uhid_intr, USBD_DEFAULT_INTERVAL); if (err) { DPRINTF(("uhidopen: usbd_open_pipe_intr failed, " "error=%d\n",err)); free(sc->sc_ibuf, M_USBDEV); free(sc->sc_obuf, M_USBDEV); sc->sc_ibuf = sc->sc_obuf = NULL; sc->sc_state &= ~UHID_OPEN; return (EIO); } sc->sc_state &= ~UHID_IMMED; sc->sc_async = 0; return (0); } int uhidclose(struct cdev *dev, int flag, int mode, usb_proc_ptr p) { struct uhid_softc *sc; USB_GET_SC(uhid, UHIDUNIT(dev), sc); DPRINTF(("uhidclose: sc=%p\n", sc)); /* Disable interrupts. */ usbd_abort_pipe(sc->sc_intrpipe); usbd_close_pipe(sc->sc_intrpipe); sc->sc_intrpipe = 0; ndflush(&sc->sc_q, sc->sc_q.c_cc); clfree(&sc->sc_q); free(sc->sc_ibuf, M_USBDEV); free(sc->sc_obuf, M_USBDEV); sc->sc_ibuf = sc->sc_obuf = NULL; sc->sc_state &= ~UHID_OPEN; sc->sc_async = 0; return (0); } int uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag) { int s; int error = 0; size_t length; u_char buffer[UHID_CHUNK]; usbd_status err; DPRINTFN(1, ("uhidread\n")); if (sc->sc_state & UHID_IMMED) { DPRINTFN(1, ("uhidread immed\n")); err = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT, sc->sc_iid, buffer, sc->sc_isize); if (err) return (EIO); return (uiomove(buffer, sc->sc_isize, uio)); } s = splusb(); while (sc->sc_q.c_cc == 0) { if (flag & O_NONBLOCK) { splx(s); return (EWOULDBLOCK); } sc->sc_state |= UHID_ASLP; DPRINTFN(5, ("uhidread: sleep on %p\n", &sc->sc_q)); error = tsleep(&sc->sc_q, PZERO | PCATCH, "uhidrea", 0); DPRINTFN(5, ("uhidread: woke, error=%d\n", error)); if (sc->sc_dying) error = EIO; if (error) { sc->sc_state &= ~UHID_ASLP; break; } if (sc->sc_state & UHID_NEEDCLEAR) { DPRINTFN(-1,("uhidread: clearing stall\n")); sc->sc_state &= ~UHID_NEEDCLEAR; usbd_clear_endpoint_stall(sc->sc_intrpipe); } } splx(s); /* Transfer as many chunks as possible. */ while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) { length = min(sc->sc_q.c_cc, uio->uio_resid); if (length > sizeof(buffer)) length = sizeof(buffer); /* Remove a small chunk from the input queue. */ (void) q_to_b(&sc->sc_q, buffer, length); DPRINTFN(5, ("uhidread: got %lu chars\n", (u_long)length)); /* Copy the data to the user process. */ if ((error = uiomove(buffer, length, uio)) != 0) break; } return (error); } int uhidread(struct cdev *dev, struct uio *uio, int flag) { struct uhid_softc *sc; int error; USB_GET_SC(uhid, UHIDUNIT(dev), sc); sc->sc_refcnt++; error = uhid_do_read(sc, uio, flag); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); return (error); } int uhid_do_write(struct uhid_softc *sc, struct uio *uio, int flag) { int error; int size; usbd_status err; DPRINTFN(1, ("uhidwrite\n")); if (sc->sc_dying) return (EIO); size = sc->sc_osize; error = 0; if (uio->uio_resid != size) return (EINVAL); error = uiomove(sc->sc_obuf, size, uio); if (!error) { if (sc->sc_oid) err = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT, sc->sc_obuf[0], sc->sc_obuf+1, size-1); else err = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT, 0, sc->sc_obuf, size); if (err) error = EIO; } return (error); } int uhidwrite(struct cdev *dev, struct uio *uio, int flag) { struct uhid_softc *sc; int error; USB_GET_SC(uhid, UHIDUNIT(dev), sc); sc->sc_refcnt++; error = uhid_do_write(sc, uio, flag); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); return (error); } int uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) { struct usb_ctl_report_desc *rd; struct usb_ctl_report *re; int size, id; usbd_status err; DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd)); if (sc->sc_dying) return (EIO); switch (cmd) { case FIONBIO: /* All handled in the upper FS layer. */ break; case FIOASYNC: if (*(int *)addr) { if (sc->sc_async != NULL) return (EBUSY); -#if __FreeBSD_version >= 500000 sc->sc_async = p->td_proc; -#else - sc->sc_async = p; -#endif DPRINTF(("uhid_do_ioctl: FIOASYNC %p\n", sc->sc_async)); } else sc->sc_async = NULL; break; /* XXX this is not the most general solution. */ case TIOCSPGRP: if (sc->sc_async == NULL) return (EINVAL); if (*(int *)addr != sc->sc_async->p_pgid) return (EPERM); break; case USB_GET_REPORT_DESC: rd = (struct usb_ctl_report_desc *)addr; size = min(sc->sc_repdesc_size, sizeof rd->ucrd_data); rd->ucrd_size = size; memcpy(rd->ucrd_data, sc->sc_repdesc, size); break; case USB_SET_IMMED: if (*(int *)addr) { /* XXX should read into ibuf, but does it matter? */ err = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT, sc->sc_iid, sc->sc_ibuf, sc->sc_isize); if (err) return (EOPNOTSUPP); sc->sc_state |= UHID_IMMED; } else sc->sc_state &= ~UHID_IMMED; break; case USB_GET_REPORT: re = (struct usb_ctl_report *)addr; switch (re->ucr_report) { case UHID_INPUT_REPORT: size = sc->sc_isize; id = sc->sc_iid; break; case UHID_OUTPUT_REPORT: size = sc->sc_osize; id = sc->sc_oid; break; case UHID_FEATURE_REPORT: size = sc->sc_fsize; id = sc->sc_fid; break; default: return (EINVAL); } err = usbd_get_report(sc->sc_iface, re->ucr_report, id, re->ucr_data, size); if (err) return (EIO); break; case USB_SET_REPORT: re = (struct usb_ctl_report *)addr; switch (re->ucr_report) { case UHID_INPUT_REPORT: size = sc->sc_isize; id = sc->sc_iid; break; case UHID_OUTPUT_REPORT: size = sc->sc_osize; id = sc->sc_oid; break; case UHID_FEATURE_REPORT: size = sc->sc_fsize; id = sc->sc_fid; break; default: return (EINVAL); } err = usbd_set_report(sc->sc_iface, re->ucr_report, id, re->ucr_data, size); if (err) return (EIO); break; case USB_GET_REPORT_ID: *(int *)addr = 0; /* XXX: we only support reportid 0? */ break; default: return (EINVAL); } return (0); } int uhidioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) { struct uhid_softc *sc; int error; USB_GET_SC(uhid, UHIDUNIT(dev), sc); sc->sc_refcnt++; error = uhid_do_ioctl(sc, cmd, addr, flag, p); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); return (error); } int uhidpoll(struct cdev *dev, int events, usb_proc_ptr p) { struct uhid_softc *sc; int revents = 0; int s; USB_GET_SC(uhid, UHIDUNIT(dev), sc); if (sc->sc_dying) return (EIO); s = splusb(); if (events & (POLLOUT | POLLWRNORM)) revents |= events & (POLLOUT | POLLWRNORM); if (events & (POLLIN | POLLRDNORM)) { if (sc->sc_q.c_cc > 0) revents |= events & (POLLIN | POLLRDNORM); else selrecord(p, &sc->sc_rsel); } splx(s); return (revents); } #if defined(__FreeBSD__) DRIVER_MODULE(uhid, uhub, uhid_driver, uhid_devclass, usbd_driver_load, 0); #endif Index: head/sys/dev/usb/ukbd.c =================================================================== --- head/sys/dev/usb/ukbd.c (revision 169488) +++ head/sys/dev/usb/ukbd.c (revision 169489) @@ -1,1517 +1,1509 @@ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Modifications for SUN TYPE 6 USB Keyboard by * Jörg Peter Schley (jps@scxnet.de) */ #include __FBSDID("$FreeBSD$"); /* * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf */ #include "opt_compat.h" #include "opt_kbd.h" #include "opt_ukbd.h" #include #include #include #include #include #include #include -#if __FreeBSD_version >= 500000 #include -#else -#include -#endif -#if __FreeBSD_version >= 500014 #include -#else -#include -#endif #include #include #include #include #include #include #include "usbdevs.h" #include #include #include #include #define UKBD_EMULATE_ATSCANCODE 1 #define DRIVER_NAME "ukbd" #define delay(d) DELAY(d) #ifdef USB_DEBUG #define DPRINTF(x) if (ukbddebug) logprintf x #define DPRINTFN(n,x) if (ukbddebug>(n)) logprintf x int ukbddebug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ukbd, CTLFLAG_RW, 0, "USB ukbd"); SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, debug, CTLFLAG_RW, &ukbddebug, 0, "ukbd debug level"); #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define UPROTO_BOOT_KEYBOARD 1 #define NKEYCODE 6 struct ukbd_data { u_int8_t modifiers; #define MOD_CONTROL_L 0x01 #define MOD_CONTROL_R 0x10 #define MOD_SHIFT_L 0x02 #define MOD_SHIFT_R 0x20 #define MOD_ALT_L 0x04 #define MOD_ALT_R 0x40 #define MOD_WIN_L 0x08 #define MOD_WIN_R 0x80 u_int8_t reserved; u_int8_t keycode[NKEYCODE]; }; #define MAXKEYS (NMOD+2*NKEYCODE) typedef struct ukbd_softc { device_t sc_dev; /* base device */ } ukbd_softc_t; #define UKBD_CHUNK 128 /* chunk size for read */ #define UKBD_BSIZE 1020 /* buffer size */ typedef void usbd_intr_t(usbd_xfer_handle, usbd_private_handle, usbd_status); typedef void usbd_disco_t(void *); static int ukbd_resume(device_t self); static usbd_intr_t ukbd_intr; static int ukbd_driver_load(module_t mod, int what, void *arg); USB_DECLARE_DRIVER_INIT(ukbd, DEVMETHOD(device_resume, ukbd_resume)); USB_MATCH(ukbd) { USB_MATCH_START(ukbd, uaa); keyboard_switch_t *sw; void *arg[2]; int unit = device_get_unit(self); sw = kbd_get_switch(DRIVER_NAME); if (sw == NULL) return (UMATCH_NONE); arg[0] = (void *)uaa; arg[1] = (void *)ukbd_intr; if ((*sw->probe)(unit, (void *)arg, 0)) return (UMATCH_NONE); if (usbd_get_quirks(uaa->device)->uq_flags & UQ_KBD_IGNORE) return (UMATCH_NONE); return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO); } USB_ATTACH(ukbd) { USB_ATTACH_START(ukbd, sc, uaa); usbd_interface_handle iface = uaa->iface; usb_interface_descriptor_t *id; char devinfo[1024]; keyboard_switch_t *sw; keyboard_t *kbd; void *arg[2]; int unit = device_get_unit(self); sw = kbd_get_switch(DRIVER_NAME); if (sw == NULL) USB_ATTACH_ERROR_RETURN; id = usbd_get_interface_descriptor(iface); usbd_devinfo(uaa->device, USBD_SHOW_INTERFACE_CLASS, devinfo); USB_ATTACH_SETUP; arg[0] = (void *)uaa; arg[1] = (void *)ukbd_intr; kbd = NULL; if ((*sw->probe)(unit, (void *)arg, 0)) USB_ATTACH_ERROR_RETURN; if ((*sw->init)(unit, &kbd, (void *)arg, 0)) USB_ATTACH_ERROR_RETURN; (*sw->enable)(kbd); #ifdef KBD_INSTALL_CDEV if (kbd_attach(kbd)) USB_ATTACH_ERROR_RETURN; #endif if (bootverbose) (*sw->diag)(kbd, bootverbose); USB_ATTACH_SUCCESS_RETURN; } int ukbd_detach(device_t self) { keyboard_t *kbd; int error; kbd = kbd_get_keyboard(kbd_find_keyboard(DRIVER_NAME, device_get_unit(self))); if (kbd == NULL) { DPRINTF(("%s: keyboard not attached!?\n", device_get_nameunit(self))); return ENXIO; } (*kbdsw[kbd->kb_index]->disable)(kbd); #ifdef KBD_INSTALL_CDEV error = kbd_detach(kbd); if (error) return error; #endif error = (*kbdsw[kbd->kb_index]->term)(kbd); if (error) return error; DPRINTF(("%s: disconnected\n", device_get_nameunit(self))); return (0); } static int ukbd_resume(device_t self) { keyboard_t *kbd; kbd = kbd_get_keyboard(kbd_find_keyboard(DRIVER_NAME, device_get_unit(self))); if (kbd) (*kbdsw[kbd->kb_index]->clear_state)(kbd); return (0); } void ukbd_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) { keyboard_t *kbd = (keyboard_t *)addr; (*kbdsw[kbd->kb_index]->intr)(kbd, (void *)status); } DRIVER_MODULE(ukbd, uhub, ukbd_driver, ukbd_devclass, ukbd_driver_load, 0); #define UKBD_DEFAULT 0 #define KEY_ERROR 0x01 #define KEY_PRESS 0 #define KEY_RELEASE 0x400 #define KEY_INDEX(c) ((c) & ~KEY_RELEASE) #define SCAN_PRESS 0 #define SCAN_RELEASE 0x80 #define SCAN_PREFIX_E0 0x100 #define SCAN_PREFIX_E1 0x200 #define SCAN_PREFIX_CTL 0x400 #define SCAN_PREFIX_SHIFT 0x800 #define SCAN_PREFIX (SCAN_PREFIX_E0 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL \ | SCAN_PREFIX_SHIFT) #define SCAN_CHAR(c) ((c) & 0x7f) #define NMOD 8 static struct { int mask, key; } ukbd_mods[NMOD] = { { MOD_CONTROL_L, 0xe0 }, { MOD_CONTROL_R, 0xe4 }, { MOD_SHIFT_L, 0xe1 }, { MOD_SHIFT_R, 0xe5 }, { MOD_ALT_L, 0xe2 }, { MOD_ALT_R, 0xe6 }, { MOD_WIN_L, 0xe3 }, { MOD_WIN_R, 0xe7 }, }; #define NN 0 /* no translation */ /* * Translate USB keycodes to AT keyboard scancodes. */ /* * FIXME: Mac USB keyboard generates: * 0x53: keypad NumLock/Clear * 0x66: Power * 0x67: keypad = * 0x68: F13 * 0x69: F14 * 0x6a: F15 */ static u_int8_t ukbd_trtab[256] = { 0, 0, 0, 0, 30, 48, 46, 32, /* 00 - 07 */ 18, 33, 34, 35, 23, 36, 37, 38, /* 08 - 0F */ 50, 49, 24, 25, 16, 19, 31, 20, /* 10 - 17 */ 22, 47, 17, 45, 21, 44, 2, 3, /* 18 - 1F */ 4, 5, 6, 7, 8, 9, 10, 11, /* 20 - 27 */ 28, 1, 14, 15, 57, 12, 13, 26, /* 28 - 2F */ 27, 43, 43, 39, 40, 41, 51, 52, /* 30 - 37 */ 53, 58, 59, 60, 61, 62, 63, 64, /* 38 - 3F */ 65, 66, 67, 68, 87, 88, 92, 70, /* 40 - 47 */ 104, 102, 94, 96, 103, 99, 101, 98, /* 48 - 4F */ 97, 100, 95, 69, 91, 55, 74, 78, /* 50 - 57 */ 89, 79, 80, 81, 75, 76, 77, 71, /* 58 - 5F */ 72, 73, 82, 83, 86, 107, 122, NN, /* 60 - 67 */ NN, NN, NN, NN, NN, NN, NN, NN, /* 68 - 6F */ NN, NN, NN, NN, 115, 108, 111, 113, /* 70 - 77 */ 109, 110, 112, 118, 114, 116, 117, 119, /* 78 - 7F */ 121, 120, NN, NN, NN, NN, NN, 115, /* 80 - 87 */ 112, 125, 121, 123, NN, NN, NN, NN, /* 88 - 8F */ NN, NN, NN, NN, NN, NN, NN, NN, /* 90 - 97 */ NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9F */ NN, NN, NN, NN, NN, NN, NN, NN, /* A0 - A7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* A8 - AF */ NN, NN, NN, NN, NN, NN, NN, NN, /* B0 - B7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* B8 - BF */ NN, NN, NN, NN, NN, NN, NN, NN, /* C0 - C7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* C8 - CF */ NN, NN, NN, NN, NN, NN, NN, NN, /* D0 - D7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* D8 - DF */ 29, 42, 56, 105, 90, 54, 93, 106, /* E0 - E7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* E8 - EF */ NN, NN, NN, NN, NN, NN, NN, NN, /* F0 - F7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* F8 - FF */ }; typedef struct ukbd_state { usbd_interface_handle ks_iface; /* interface */ usbd_pipe_handle ks_intrpipe; /* interrupt pipe */ struct usb_attach_arg *ks_uaa; int ks_ep_addr; struct ukbd_data ks_ndata; struct ukbd_data ks_odata; u_long ks_ntime[NKEYCODE]; u_long ks_otime[NKEYCODE]; #define INPUTBUFSIZE (NMOD + 2*NKEYCODE) u_int ks_input[INPUTBUFSIZE]; /* input buffer */ int ks_inputs; int ks_inputhead; int ks_inputtail; int ks_ifstate; #define INTRENABLED (1 << 0) #define DISCONNECTED (1 << 1) usb_callout_t ks_timeout_handle; int ks_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */ int ks_flags; /* flags */ #define COMPOSE (1 << 0) int ks_polling; int ks_state; /* shift/lock key state */ int ks_accents; /* accent key index (> 0) */ u_int ks_composed_char; /* composed char code (> 0) */ #ifdef UKBD_EMULATE_ATSCANCODE u_int ks_buffered_char[2]; #endif } ukbd_state_t; /* keyboard driver declaration */ static int ukbd_configure(int flags); static kbd_probe_t ukbd_probe; static kbd_init_t ukbd_init; static kbd_term_t ukbd_term; static kbd_intr_t ukbd_interrupt; static kbd_test_if_t ukbd_test_if; static kbd_enable_t ukbd_enable; static kbd_disable_t ukbd_disable; static kbd_read_t ukbd_read; static kbd_check_t ukbd_check; static kbd_read_char_t ukbd_read_char; static kbd_check_char_t ukbd_check_char; static kbd_ioctl_t ukbd_ioctl; static kbd_lock_t ukbd_lock; static kbd_clear_state_t ukbd_clear_state; static kbd_get_state_t ukbd_get_state; static kbd_set_state_t ukbd_set_state; static kbd_poll_mode_t ukbd_poll; keyboard_switch_t ukbdsw = { ukbd_probe, ukbd_init, ukbd_term, ukbd_interrupt, ukbd_test_if, ukbd_enable, ukbd_disable, ukbd_read, ukbd_check, ukbd_read_char, ukbd_check_char, ukbd_ioctl, ukbd_lock, ukbd_clear_state, ukbd_get_state, ukbd_set_state, genkbd_get_fkeystr, ukbd_poll, genkbd_diag, }; KEYBOARD_DRIVER(ukbd, ukbdsw, ukbd_configure); /* local functions */ static int ukbd_enable_intr(keyboard_t *kbd, int on, usbd_intr_t *func); static void ukbd_timeout(void *arg); static int ukbd_getc(ukbd_state_t *state); static int probe_keyboard(struct usb_attach_arg *uaa, int flags); static int init_keyboard(ukbd_state_t *state, int *type, int flags); static void set_leds(ukbd_state_t *state, int leds); static int set_typematic(keyboard_t *kbd, int code); #ifdef UKBD_EMULATE_ATSCANCODE static int keycode2scancode(int keycode, int shift, int up); #endif /* local variables */ /* the initial key map, accent map and fkey strings */ #if defined(UKBD_DFLT_KEYMAP) && !defined(KLD_MODULE) #define KBD_DFLT_KEYMAP #include "ukbdmap.h" #endif #include /* structures for the default keyboard */ static keyboard_t default_kbd; static ukbd_state_t default_kbd_state; static keymap_t default_keymap; static accentmap_t default_accentmap; static fkeytab_t default_fkeytab[NUM_FKEYS]; /* * The back door to the keyboard driver! * This function is called by the console driver, via the kbdio module, * to tickle keyboard drivers when the low-level console is being initialized. * Almost nothing in the kernel has been initialied yet. Try to probe * keyboards if possible. * NOTE: because of the way the low-level conole is initialized, this routine * may be called more than once!! */ static int ukbd_configure(int flags) { return 0; #if 0 /* not yet */ keyboard_t *kbd; device_t device; struct usb_attach_arg *uaa; void *arg[2]; device = devclass_get_device(ukbd_devclass, UKBD_DEFAULT); if (device == NULL) return 0; uaa = (struct usb_attach_arg *)device_get_ivars(device); if (uaa == NULL) return 0; /* probe the default keyboard */ arg[0] = (void *)uaa; arg[1] = (void *)ukbd_intr; kbd = NULL; if (ukbd_probe(UKBD_DEFAULT, arg, flags)) return 0; if (ukbd_init(UKBD_DEFAULT, &kbd, arg, flags)) return 0; /* return the number of found keyboards */ return 1; #endif } /* low-level functions */ /* detect a keyboard */ static int ukbd_probe(int unit, void *arg, int flags) { void **data; struct usb_attach_arg *uaa; data = (void **)arg; uaa = (struct usb_attach_arg *)data[0]; /* XXX */ if (unit == UKBD_DEFAULT) { if (KBD_IS_PROBED(&default_kbd)) return 0; } if (probe_keyboard(uaa, flags)) return ENXIO; return 0; } /* reset and initialize the device */ static int ukbd_init(int unit, keyboard_t **kbdp, void *arg, int flags) { keyboard_t *kbd; ukbd_state_t *state; keymap_t *keymap; accentmap_t *accmap; fkeytab_t *fkeymap; int fkeymap_size; void **data = (void **)arg; struct usb_attach_arg *uaa = (struct usb_attach_arg *)data[0]; /* XXX */ if (unit == UKBD_DEFAULT) { *kbdp = kbd = &default_kbd; if (KBD_IS_INITIALIZED(kbd) && KBD_IS_CONFIGURED(kbd)) return 0; state = &default_kbd_state; keymap = &default_keymap; accmap = &default_accentmap; fkeymap = default_fkeytab; fkeymap_size = sizeof(default_fkeytab)/sizeof(default_fkeytab[0]); } else if (*kbdp == NULL) { *kbdp = kbd = malloc(sizeof(*kbd), M_DEVBUF, M_NOWAIT); if (kbd == NULL) return ENOMEM; bzero(kbd, sizeof(*kbd)); state = malloc(sizeof(*state), M_DEVBUF, M_NOWAIT); keymap = malloc(sizeof(key_map), M_DEVBUF, M_NOWAIT); accmap = malloc(sizeof(accent_map), M_DEVBUF, M_NOWAIT); fkeymap = malloc(sizeof(fkey_tab), M_DEVBUF, M_NOWAIT); fkeymap_size = sizeof(fkey_tab)/sizeof(fkey_tab[0]); if ((state == NULL) || (keymap == NULL) || (accmap == NULL) || (fkeymap == NULL)) { if (state != NULL) free(state, M_DEVBUF); if (keymap != NULL) free(keymap, M_DEVBUF); if (accmap != NULL) free(accmap, M_DEVBUF); if (fkeymap != NULL) free(fkeymap, M_DEVBUF); free(kbd, M_DEVBUF); return ENOMEM; } } else if (KBD_IS_INITIALIZED(*kbdp) && KBD_IS_CONFIGURED(*kbdp)) { return 0; } else { kbd = *kbdp; state = (ukbd_state_t *)kbd->kb_data; keymap = kbd->kb_keymap; accmap = kbd->kb_accentmap; fkeymap = kbd->kb_fkeytab; fkeymap_size = kbd->kb_fkeytab_size; } if (!KBD_IS_PROBED(kbd)) { kbd_init_struct(kbd, DRIVER_NAME, KB_OTHER, unit, flags, 0, 0); bzero(state, sizeof(*state)); bcopy(&key_map, keymap, sizeof(key_map)); bcopy(&accent_map, accmap, sizeof(accent_map)); bcopy(fkey_tab, fkeymap, imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab))); kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size); kbd->kb_data = (void *)state; if (probe_keyboard(uaa, flags)) return ENXIO; else KBD_FOUND_DEVICE(kbd); ukbd_clear_state(kbd); state->ks_mode = K_XLATE; state->ks_iface = uaa->iface; state->ks_uaa = uaa; state->ks_ifstate = 0; usb_callout_init(state->ks_timeout_handle); /* * FIXME: set the initial value for lock keys in ks_state * according to the BIOS data? */ KBD_PROBE_DONE(kbd); } if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) { if (KBD_HAS_DEVICE(kbd) && init_keyboard((ukbd_state_t *)kbd->kb_data, &kbd->kb_type, kbd->kb_flags)) return ENXIO; ukbd_ioctl(kbd, KDSETLED, (caddr_t)&(state->ks_state)); KBD_INIT_DONE(kbd); } if (!KBD_IS_CONFIGURED(kbd)) { if (kbd_register(kbd) < 0) return ENXIO; if (ukbd_enable_intr(kbd, TRUE, (usbd_intr_t *)data[1]) == 0) ukbd_timeout((void *)kbd); KBD_CONFIG_DONE(kbd); } return 0; } static int ukbd_enable_intr(keyboard_t *kbd, int on, usbd_intr_t *func) { ukbd_state_t *state = (ukbd_state_t *)kbd->kb_data; usbd_status err; if (on) { /* Set up interrupt pipe. */ if (state->ks_ifstate & INTRENABLED) return EBUSY; state->ks_ifstate |= INTRENABLED; err = usbd_open_pipe_intr(state->ks_iface, state->ks_ep_addr, USBD_SHORT_XFER_OK, &state->ks_intrpipe, kbd, &state->ks_ndata, sizeof(state->ks_ndata), func, USBD_DEFAULT_INTERVAL); if (err) return (EIO); } else { /* Disable interrupts. */ usbd_abort_pipe(state->ks_intrpipe); usbd_close_pipe(state->ks_intrpipe); state->ks_ifstate &= ~INTRENABLED; } return (0); } /* finish using this keyboard */ static int ukbd_term(keyboard_t *kbd) { ukbd_state_t *state; int error; int s; s = splusb(); state = (ukbd_state_t *)kbd->kb_data; DPRINTF(("ukbd_term: ks_ifstate=0x%x\n", state->ks_ifstate)); usb_uncallout(state->ks_timeout_handle, ukbd_timeout, kbd); if (state->ks_ifstate & INTRENABLED) ukbd_enable_intr(kbd, FALSE, NULL); if (state->ks_ifstate & INTRENABLED) { splx(s); DPRINTF(("ukbd_term: INTRENABLED!\n")); return ENXIO; } error = kbd_unregister(kbd); DPRINTF(("ukbd_term: kbd_unregister() %d\n", error)); if (error == 0) { kbd->kb_flags = 0; if (kbd != &default_kbd) { free(kbd->kb_keymap, M_DEVBUF); free(kbd->kb_accentmap, M_DEVBUF); free(kbd->kb_fkeytab, M_DEVBUF); free(state, M_DEVBUF); free(kbd, M_DEVBUF); } } splx(s); return error; } /* keyboard interrupt routine */ static void ukbd_timeout(void *arg) { keyboard_t *kbd; ukbd_state_t *state; int s; kbd = (keyboard_t *)arg; state = (ukbd_state_t *)kbd->kb_data; s = splusb(); (*kbdsw[kbd->kb_index]->intr)(kbd, (void *)USBD_NORMAL_COMPLETION); usb_callout(state->ks_timeout_handle, hz / 40, ukbd_timeout, arg); splx(s); } static int ukbd_interrupt(keyboard_t *kbd, void *arg) { usbd_status status = (usbd_status)arg; ukbd_state_t *state; struct ukbd_data *ud; struct timeval tv; u_long now; int mod, omod; int key, c; int i, j; DPRINTFN(5, ("ukbd_intr: status=%d\n", status)); if (status == USBD_CANCELLED) return 0; state = (ukbd_state_t *)kbd->kb_data; ud = &state->ks_ndata; if (status != USBD_NORMAL_COMPLETION) { DPRINTF(("ukbd_intr: status=%d\n", status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(state->ks_intrpipe); return 0; } if (ud->keycode[0] == KEY_ERROR) return 0; /* ignore */ getmicrouptime(&tv); now = (u_long)tv.tv_sec*1000 + (u_long)tv.tv_usec/1000; #define ADDKEY1(c) \ if (state->ks_inputs < INPUTBUFSIZE) { \ state->ks_input[state->ks_inputtail] = (c); \ ++state->ks_inputs; \ state->ks_inputtail = (state->ks_inputtail + 1)%INPUTBUFSIZE; \ } mod = ud->modifiers; omod = state->ks_odata.modifiers; if (mod != omod) { for (i = 0; i < NMOD; i++) if (( mod & ukbd_mods[i].mask) != (omod & ukbd_mods[i].mask)) ADDKEY1(ukbd_mods[i].key | (mod & ukbd_mods[i].mask ? KEY_PRESS : KEY_RELEASE)); } /* Check for released keys. */ for (i = 0; i < NKEYCODE; i++) { key = state->ks_odata.keycode[i]; if (key == 0) continue; for (j = 0; j < NKEYCODE; j++) { if (ud->keycode[j] == 0) continue; if (key == ud->keycode[j]) goto rfound; } ADDKEY1(key | KEY_RELEASE); rfound: ; } /* Check for pressed keys. */ for (i = 0; i < NKEYCODE; i++) { key = ud->keycode[i]; if (key == 0) continue; state->ks_ntime[i] = now + kbd->kb_delay1; for (j = 0; j < NKEYCODE; j++) { if (state->ks_odata.keycode[j] == 0) continue; if (key == state->ks_odata.keycode[j]) { state->ks_ntime[i] = state->ks_otime[j]; if (state->ks_otime[j] > now) goto pfound; state->ks_ntime[i] = now + kbd->kb_delay2; break; } } ADDKEY1(key | KEY_PRESS); pfound: ; } state->ks_odata = *ud; bcopy(state->ks_ntime, state->ks_otime, sizeof(state->ks_ntime)); if (state->ks_inputs <= 0) return 0; #ifdef USB_DEBUG for (i = state->ks_inputhead, j = 0; j < state->ks_inputs; ++j, i = (i + 1)%INPUTBUFSIZE) { c = state->ks_input[i]; DPRINTF(("0x%x (%d) %s\n", c, c, (c & KEY_RELEASE) ? "released":"pressed")); } if (ud->modifiers) DPRINTF(("mod:0x%04x ", ud->modifiers)); for (i = 0; i < NKEYCODE; i++) { if (ud->keycode[i]) DPRINTF(("%d ", ud->keycode[i])); } DPRINTF(("\n")); #endif /* USB_DEBUG */ if (state->ks_polling) return 0; if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) { /* let the callback function to process the input */ (*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT, kbd->kb_callback.kc_arg); } else { /* read and discard the input; no one is waiting for it */ do { c = ukbd_read_char(kbd, FALSE); } while (c != NOKEY); } return 0; } static int ukbd_getc(ukbd_state_t *state) { int c; int s; if (state->ks_polling) { DPRINTFN(1,("ukbd_getc: polling\n")); s = splusb(); while (state->ks_inputs <= 0) usbd_dopoll(state->ks_iface); splx(s); } s = splusb(); if (state->ks_inputs <= 0) { c = -1; } else { c = state->ks_input[state->ks_inputhead]; --state->ks_inputs; state->ks_inputhead = (state->ks_inputhead + 1)%INPUTBUFSIZE; } splx(s); return c; } /* test the interface to the device */ static int ukbd_test_if(keyboard_t *kbd) { return 0; } /* * Enable the access to the device; until this function is called, * the client cannot read from the keyboard. */ static int ukbd_enable(keyboard_t *kbd) { int s; s = splusb(); KBD_ACTIVATE(kbd); splx(s); return 0; } /* disallow the access to the device */ static int ukbd_disable(keyboard_t *kbd) { int s; s = splusb(); KBD_DEACTIVATE(kbd); splx(s); return 0; } /* read one byte from the keyboard if it's allowed */ static int ukbd_read(keyboard_t *kbd, int wait) { ukbd_state_t *state; int usbcode; #ifdef UKBD_EMULATE_ATSCANCODE int keycode; int scancode; #endif state = (ukbd_state_t *)kbd->kb_data; #ifdef UKBD_EMULATE_ATSCANCODE if (state->ks_buffered_char[0]) { scancode = state->ks_buffered_char[0]; if (scancode & SCAN_PREFIX) { state->ks_buffered_char[0] = scancode & ~SCAN_PREFIX; return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1); } else { state->ks_buffered_char[0] = state->ks_buffered_char[1]; state->ks_buffered_char[1] = 0; return scancode; } } #endif /* UKBD_EMULATE_ATSCANCODE */ /* XXX */ usbcode = ukbd_getc(state); if (!KBD_IS_ACTIVE(kbd) || (usbcode == -1)) return -1; ++kbd->kb_count; #ifdef UKBD_EMULATE_ATSCANCODE keycode = ukbd_trtab[KEY_INDEX(usbcode)]; if (keycode == NN) return -1; scancode = keycode2scancode(keycode, state->ks_ndata.modifiers, usbcode & KEY_RELEASE); if (scancode & SCAN_PREFIX) { if (scancode & SCAN_PREFIX_CTL) { state->ks_buffered_char[0] = 0x1d | (scancode & SCAN_RELEASE); /* Ctrl */ state->ks_buffered_char[1] = scancode & ~SCAN_PREFIX; } else if (scancode & SCAN_PREFIX_SHIFT) { state->ks_buffered_char[0] = 0x2a | (scancode & SCAN_RELEASE); /* Shift */ state->ks_buffered_char[1] = scancode & ~SCAN_PREFIX_SHIFT; } else { state->ks_buffered_char[0] = scancode & ~SCAN_PREFIX; state->ks_buffered_char[1] = 0; } return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1); } return scancode; #else /* !UKBD_EMULATE_ATSCANCODE */ return usbcode; #endif /* UKBD_EMULATE_ATSCANCODE */ } /* check if data is waiting */ static int ukbd_check(keyboard_t *kbd) { if (!KBD_IS_ACTIVE(kbd)) return FALSE; #ifdef UKBD_EMULATE_ATSCANCODE if (((ukbd_state_t *)kbd->kb_data)->ks_buffered_char[0]) return TRUE; #endif if (((ukbd_state_t *)kbd->kb_data)->ks_inputs > 0) return TRUE; return FALSE; } /* read char from the keyboard */ static u_int ukbd_read_char(keyboard_t *kbd, int wait) { ukbd_state_t *state; u_int action; int usbcode; int keycode; #ifdef UKBD_EMULATE_ATSCANCODE int scancode; #endif state = (ukbd_state_t *)kbd->kb_data; next_code: /* do we have a composed char to return? */ if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) { action = state->ks_composed_char; state->ks_composed_char = 0; if (action > UCHAR_MAX) return ERRKEY; return action; } #ifdef UKBD_EMULATE_ATSCANCODE /* do we have a pending raw scan code? */ if (state->ks_mode == K_RAW) { if (state->ks_buffered_char[0]) { scancode = state->ks_buffered_char[0]; if (scancode & SCAN_PREFIX) { state->ks_buffered_char[0] = scancode & ~SCAN_PREFIX; return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1); } else { state->ks_buffered_char[0] = state->ks_buffered_char[1]; state->ks_buffered_char[1] = 0; return scancode; } } } #endif /* UKBD_EMULATE_ATSCANCODE */ /* see if there is something in the keyboard port */ /* XXX */ usbcode = ukbd_getc(state); if (usbcode == -1) return NOKEY; ++kbd->kb_count; #ifdef UKBD_EMULATE_ATSCANCODE /* USB key index -> key code -> AT scan code */ keycode = ukbd_trtab[KEY_INDEX(usbcode)]; if (keycode == NN) return NOKEY; /* return an AT scan code for the K_RAW mode */ if (state->ks_mode == K_RAW) { scancode = keycode2scancode(keycode, state->ks_ndata.modifiers, usbcode & KEY_RELEASE); if (scancode & SCAN_PREFIX) { if (scancode & SCAN_PREFIX_CTL) { state->ks_buffered_char[0] = 0x1d | (scancode & SCAN_RELEASE); state->ks_buffered_char[1] = scancode & ~SCAN_PREFIX; } else if (scancode & SCAN_PREFIX_SHIFT) { state->ks_buffered_char[0] = 0x2a | (scancode & SCAN_RELEASE); state->ks_buffered_char[1] = scancode & ~SCAN_PREFIX_SHIFT; } else { state->ks_buffered_char[0] = scancode & ~SCAN_PREFIX; state->ks_buffered_char[1] = 0; } return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1); } return scancode; } #else /* !UKBD_EMULATE_ATSCANCODE */ /* return the byte as is for the K_RAW mode */ if (state->ks_mode == K_RAW) return usbcode; /* USB key index -> key code */ keycode = ukbd_trtab[KEY_INDEX(usbcode)]; if (keycode == NN) return NOKEY; #endif /* UKBD_EMULATE_ATSCANCODE */ switch (keycode) { case 0x38: /* left alt (compose key) */ if (usbcode & KEY_RELEASE) { if (state->ks_flags & COMPOSE) { state->ks_flags &= ~COMPOSE; if (state->ks_composed_char > UCHAR_MAX) state->ks_composed_char = 0; } } else { if (!(state->ks_flags & COMPOSE)) { state->ks_flags |= COMPOSE; state->ks_composed_char = 0; } } break; /* XXX: I don't like these... */ case 0x5c: /* print screen */ if (state->ks_flags & ALTS) keycode = 0x54; /* sysrq */ break; case 0x68: /* pause/break */ if (state->ks_flags & CTLS) keycode = 0x6c; /* break */ break; } /* return the key code in the K_CODE mode */ if (usbcode & KEY_RELEASE) keycode |= SCAN_RELEASE; if (state->ks_mode == K_CODE) return keycode; /* compose a character code */ if (state->ks_flags & COMPOSE) { switch (keycode) { /* key pressed, process it */ case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */ state->ks_composed_char *= 10; state->ks_composed_char += keycode - 0x40; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */ state->ks_composed_char *= 10; state->ks_composed_char += keycode - 0x47; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */ state->ks_composed_char *= 10; state->ks_composed_char += keycode - 0x4E; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x52: /* keypad 0 */ state->ks_composed_char *= 10; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; /* key released, no interest here */ case SCAN_RELEASE | 0x47: case SCAN_RELEASE | 0x48: case SCAN_RELEASE | 0x49: /* keypad 7,8,9 */ case SCAN_RELEASE | 0x4B: case SCAN_RELEASE | 0x4C: case SCAN_RELEASE | 0x4D: /* keypad 4,5,6 */ case SCAN_RELEASE | 0x4F: case SCAN_RELEASE | 0x50: case SCAN_RELEASE | 0x51: /* keypad 1,2,3 */ case SCAN_RELEASE | 0x52: /* keypad 0 */ goto next_code; case 0x38: /* left alt key */ break; default: if (state->ks_composed_char > 0) { state->ks_flags &= ~COMPOSE; state->ks_composed_char = 0; return ERRKEY; } break; } } /* keycode to key action */ action = genkbd_keyaction(kbd, SCAN_CHAR(keycode), keycode & SCAN_RELEASE, &state->ks_state, &state->ks_accents); if (action == NOKEY) goto next_code; else return action; } /* check if char is waiting */ static int ukbd_check_char(keyboard_t *kbd) { ukbd_state_t *state; if (!KBD_IS_ACTIVE(kbd)) return FALSE; state = (ukbd_state_t *)kbd->kb_data; if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) return TRUE; return ukbd_check(kbd); } /* some useful control functions */ static int ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) { /* trasnlate LED_XXX bits into the device specific bits */ static u_char ledmap[8] = { 0, 2, 1, 3, 4, 6, 5, 7, }; ukbd_state_t *state = kbd->kb_data; int s; int i; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) int ival; #endif s = splusb(); switch (cmd) { case KDGKBMODE: /* get keyboard mode */ *(int *)arg = state->ks_mode; break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 7): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSKBMODE: /* set keyboard mode */ switch (*(int *)arg) { case K_XLATE: if (state->ks_mode != K_XLATE) { /* make lock key state and LED state match */ state->ks_state &= ~LOCK_MASK; state->ks_state |= KBD_LED_VAL(kbd); } /* FALLTHROUGH */ case K_RAW: case K_CODE: if (state->ks_mode != *(int *)arg) { ukbd_clear_state(kbd); state->ks_mode = *(int *)arg; } break; default: splx(s); return EINVAL; } break; case KDGETLED: /* get keyboard LED */ *(int *)arg = KBD_LED_VAL(kbd); break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 66): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSETLED: /* set keyboard LED */ /* NOTE: lock key state in ks_state won't be changed */ if (*(int *)arg & ~LOCK_MASK) { splx(s); return EINVAL; } i = *(int *)arg; /* replace CAPS LED with ALTGR LED for ALTGR keyboards */ if (state->ks_mode == K_XLATE && kbd->kb_keymap->n_keys > ALTGR_OFFSET) { if (i & ALKED) i |= CLKED; else i &= ~CLKED; } if (KBD_HAS_DEVICE(kbd)) { set_leds(state, ledmap[i & LED_MASK]); /* XXX: error check? */ } KBD_LED_VAL(kbd) = *(int *)arg; break; case KDGKBSTATE: /* get lock key state */ *(int *)arg = state->ks_state & LOCK_MASK; break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 20): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSKBSTATE: /* set lock key state */ if (*(int *)arg & ~LOCK_MASK) { splx(s); return EINVAL; } state->ks_state &= ~LOCK_MASK; state->ks_state |= *(int *)arg; splx(s); /* set LEDs and quit */ return ukbd_ioctl(kbd, KDSETLED, arg); case KDSETREPEAT: /* set keyboard repeat rate (new interface) */ splx(s); if (!KBD_HAS_DEVICE(kbd)) return 0; if (((int *)arg)[1] < 0) return EINVAL; if (((int *)arg)[0] < 0) return EINVAL; else if (((int *)arg)[0] == 0) /* fastest possible value */ kbd->kb_delay1 = 200; else kbd->kb_delay1 = ((int *)arg)[0]; kbd->kb_delay2 = ((int *)arg)[1]; return 0; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 67): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSETRAD: /* set keyboard repeat rate (old interface) */ splx(s); return set_typematic(kbd, *(int *)arg); case PIO_KEYMAP: /* set keyboard translation table */ case PIO_KEYMAPENT: /* set keyboard translation table entry */ case PIO_DEADKEYMAP: /* set accent key translation table */ state->ks_accents = 0; /* FALLTHROUGH */ default: splx(s); return genkbd_commonioctl(kbd, cmd, arg); #ifdef USB_DEBUG case USB_SETDEBUG: ukbddebug = *(int *)arg; break; #endif } splx(s); return 0; } /* lock the access to the keyboard */ static int ukbd_lock(keyboard_t *kbd, int lock) { /* XXX ? */ return TRUE; } /* clear the internal state of the keyboard */ static void ukbd_clear_state(keyboard_t *kbd) { ukbd_state_t *state; state = (ukbd_state_t *)kbd->kb_data; state->ks_flags = 0; state->ks_polling = 0; state->ks_state &= LOCK_MASK; /* preserve locking key state */ state->ks_accents = 0; state->ks_composed_char = 0; #ifdef UKBD_EMULATE_ATSCANCODE state->ks_buffered_char[0] = 0; state->ks_buffered_char[1] = 0; #endif bzero(&state->ks_ndata, sizeof(state->ks_ndata)); bzero(&state->ks_odata, sizeof(state->ks_odata)); bzero(&state->ks_ntime, sizeof(state->ks_ntime)); bzero(&state->ks_otime, sizeof(state->ks_otime)); } /* save the internal state */ static int ukbd_get_state(keyboard_t *kbd, void *buf, size_t len) { if (len == 0) return sizeof(ukbd_state_t); if (len < sizeof(ukbd_state_t)) return -1; bcopy(kbd->kb_data, buf, sizeof(ukbd_state_t)); return 0; } /* set the internal state */ static int ukbd_set_state(keyboard_t *kbd, void *buf, size_t len) { if (len < sizeof(ukbd_state_t)) return ENOMEM; bcopy(buf, kbd->kb_data, sizeof(ukbd_state_t)); return 0; } static int ukbd_poll(keyboard_t *kbd, int on) { ukbd_state_t *state; usbd_device_handle dev; int s; state = (ukbd_state_t *)kbd->kb_data; usbd_interface2device_handle(state->ks_iface, &dev); s = splusb(); if (on) { if (state->ks_polling == 0) usbd_set_polling(dev, on); ++state->ks_polling; } else { --state->ks_polling; if (state->ks_polling == 0) usbd_set_polling(dev, on); } splx(s); return 0; } /* local functions */ static int probe_keyboard(struct usb_attach_arg *uaa, int flags) { usb_interface_descriptor_t *id; if (!uaa->iface) /* we attach to ifaces only */ return EINVAL; /* Check that this is a keyboard that speaks the boot protocol. */ id = usbd_get_interface_descriptor(uaa->iface); if (id && id->bInterfaceClass == UICLASS_HID && id->bInterfaceSubClass == UISUBCLASS_BOOT && id->bInterfaceProtocol == UPROTO_BOOT_KEYBOARD) return 0; /* found it */ return EINVAL; } static int init_keyboard(ukbd_state_t *state, int *type, int flags) { usb_endpoint_descriptor_t *ed; usbd_status err; *type = KB_OTHER; state->ks_ifstate |= DISCONNECTED; ed = usbd_interface2endpoint_descriptor(state->ks_iface, 0); if (!ed) { printf("ukbd: could not read endpoint descriptor\n"); return EIO; } DPRINTFN(10,("ukbd:init_keyboard: \ bLength=%d bDescriptorType=%d bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d bInterval=%d\n", ed->bLength, ed->bDescriptorType, UE_GET_ADDR(ed->bEndpointAddress), UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN ? "in":"out", UE_GET_XFERTYPE(ed->bmAttributes), UGETW(ed->wMaxPacketSize), ed->bInterval)); if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN || UE_GET_XFERTYPE(ed->bmAttributes) != UE_INTERRUPT) { printf("ukbd: unexpected endpoint\n"); return EINVAL; } if ((usbd_get_quirks(state->ks_uaa->device)->uq_flags & UQ_NO_SET_PROTO) == 0) { err = usbd_set_protocol(state->ks_iface, 0); DPRINTFN(5, ("ukbd:init_keyboard: protocol set\n")); if (err) { printf("ukbd: set protocol failed\n"); return EIO; } } /* Ignore if SETIDLE fails since it is not crucial. */ usbd_set_idle(state->ks_iface, 0, 0); state->ks_ep_addr = ed->bEndpointAddress; state->ks_ifstate &= ~DISCONNECTED; return 0; } static void set_leds(ukbd_state_t *state, int leds) { u_int8_t res = leds; DPRINTF(("ukbd:set_leds: state=%p leds=%d\n", state, leds)); usbd_set_report_async(state->ks_iface, UHID_OUTPUT_REPORT, 0, &res, 1); } static int set_typematic(keyboard_t *kbd, int code) { static int delays[] = { 250, 500, 750, 1000 }; static int rates[] = { 34, 38, 42, 46, 50, 55, 59, 63, 68, 76, 84, 92, 100, 110, 118, 126, 136, 152, 168, 184, 200, 220, 236, 252, 272, 304, 336, 368, 400, 440, 472, 504 }; if (code & ~0x7f) return EINVAL; kbd->kb_delay1 = delays[(code >> 5) & 3]; kbd->kb_delay2 = rates[code & 0x1f]; return 0; } #ifdef UKBD_EMULATE_ATSCANCODE static int keycode2scancode(int keycode, int shift, int up) { static int scan[] = { 0x1c, 0x1d, 0x35, 0x37 | SCAN_PREFIX_SHIFT, /* PrintScreen */ 0x38, 0x47, 0x48, 0x49, 0x4b, 0x4d, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x46, /* XXX Pause/Break */ 0x5b, 0x5c, 0x5d, /* SUN TYPE 6 USB KEYBOARD */ 0x68, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x25, 0x1f, 0x1e, 0x20, }; int scancode; scancode = keycode; if ((keycode >= 89) && (keycode < 89 + sizeof(scan)/sizeof(scan[0]))) scancode = scan[keycode - 89] | SCAN_PREFIX_E0; /* Pause/Break */ if ((keycode == 104) && !(shift & (MOD_CONTROL_L | MOD_CONTROL_R))) scancode = 0x45 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL; if (shift & (MOD_SHIFT_L | MOD_SHIFT_R)) scancode &= ~SCAN_PREFIX_SHIFT; return (scancode | (up ? SCAN_RELEASE : SCAN_PRESS)); } #endif /* UKBD_EMULATE_ATSCANCODE */ static int ukbd_driver_load(module_t mod, int what, void *arg) { switch (what) { case MOD_LOAD: kbd_add_driver(&ukbd_kbd_driver); break; case MOD_UNLOAD: kbd_delete_driver(&ukbd_kbd_driver); break; } return usbd_driver_load(mod, what, 0); } Index: head/sys/dev/usb/ums.c =================================================================== --- head/sys/dev/usb/ums.c (revision 169488) +++ head/sys/dev/usb/ums.c (revision 169489) @@ -1,871 +1,863 @@ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf */ #include #include #include #include #include #include #include #include #include #include #include -#if __FreeBSD_version >= 500014 #include -#else -#include -#endif #include #include #include #include #include #include #include #include "usbdevs.h" #include #include -#if __FreeBSD_version >= 500000 #include -#else -#include -#endif #ifdef USB_DEBUG #define DPRINTF(x) if (umsdebug) logprintf x #define DPRINTFN(n,x) if (umsdebug>(n)) logprintf x int umsdebug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums"); SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RW, &umsdebug, 0, "ums debug level"); #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define UMSUNIT(s) (minor(s)&0x1f) #define MS_TO_TICKS(ms) ((ms) * hz / 1000) #define QUEUE_BUFSIZE 400 /* MUST be divisible by 5 _and_ 8 */ struct ums_softc { device_t sc_dev; /* base device */ usbd_interface_handle sc_iface; /* interface */ usbd_pipe_handle sc_intrpipe; /* interrupt pipe */ int sc_ep_addr; u_char *sc_ibuf; u_int8_t sc_iid; int sc_isize; struct hid_location sc_loc_x, sc_loc_y, sc_loc_z, sc_loc_t; struct hid_location *sc_loc_btn; usb_callout_t callout_handle; /* for spurious button ups */ int sc_enabled; int sc_disconnected; /* device is gone */ int flags; /* device configuration */ #define UMS_Z 0x01 /* z direction available */ #define UMS_SPUR_BUT_UP 0x02 /* spurious button up events */ #define UMS_T 0x04 /* aa direction available (tilt) */ int nbuttons; #define MAX_BUTTONS 31 /* chosen because sc_buttons is int */ u_char qbuf[QUEUE_BUFSIZE]; /* must be divisable by 3&4 */ u_char dummy[100]; /* XXX just for safety and for now */ int qcount, qhead, qtail; mousehw_t hw; mousemode_t mode; mousestatus_t status; int state; # define UMS_ASLEEP 0x01 /* readFromDevice is waiting */ # define UMS_SELECT 0x02 /* select is waiting */ struct selinfo rsel; /* process waiting in select */ struct cdev *dev; /* specfs */ }; #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) #define MOUSE_FLAGS (HIO_RELATIVE) static void ums_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status); static void ums_add_to_queue(struct ums_softc *sc, int dx, int dy, int dz, int dt, int buttons); static void ums_add_to_queue_timeout(void *priv); static int ums_enable(void *); static void ums_disable(void *); static d_open_t ums_open; static d_close_t ums_close; static d_read_t ums_read; static d_ioctl_t ums_ioctl; static d_poll_t ums_poll; static struct cdevsw ums_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = ums_open, .d_close = ums_close, .d_read = ums_read, .d_ioctl = ums_ioctl, .d_poll = ums_poll, .d_name = "ums", #if __FreeBSD_version < 500014 .d_bmaj -1 #endif }; USB_DECLARE_DRIVER(ums); USB_MATCH(ums) { USB_MATCH_START(ums, uaa); usb_interface_descriptor_t *id; int size, ret; void *desc; usbd_status err; if (!uaa->iface) return (UMATCH_NONE); id = usbd_get_interface_descriptor(uaa->iface); if (!id || id->bInterfaceClass != UICLASS_HID) return (UMATCH_NONE); err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP); if (err) return (UMATCH_NONE); if (hid_is_collection(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) ret = UMATCH_IFACECLASS; else ret = UMATCH_NONE; free(desc, M_TEMP); return (ret); } USB_ATTACH(ums) { USB_ATTACH_START(ums, sc, uaa); usbd_interface_handle iface = uaa->iface; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; int size; void *desc; usbd_status err; char devinfo[1024]; u_int32_t flags; int i; struct hid_location loc_btn; sc->sc_disconnected = 1; sc->sc_iface = iface; id = usbd_get_interface_descriptor(iface); usbd_devinfo(uaa->device, USBD_SHOW_INTERFACE_CLASS, devinfo); USB_ATTACH_SETUP; ed = usbd_interface2endpoint_descriptor(iface, 0); if (!ed) { printf("%s: could not read endpoint descriptor\n", device_get_nameunit(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } DPRINTFN(10,("ums_attach: bLength=%d bDescriptorType=%d " "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" " bInterval=%d\n", ed->bLength, ed->bDescriptorType, UE_GET_ADDR(ed->bEndpointAddress), UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN ? "in":"out", UE_GET_XFERTYPE(ed->bmAttributes), UGETW(ed->wMaxPacketSize), ed->bInterval)); if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN || UE_GET_XFERTYPE(ed->bmAttributes) != UE_INTERRUPT) { printf("%s: unexpected endpoint\n", device_get_nameunit(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP); if (err) USB_ATTACH_ERROR_RETURN; if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), hid_input, &sc->sc_loc_x, &flags)) { printf("%s: mouse has no X report\n", device_get_nameunit(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { printf("%s: X report 0x%04x not supported\n", device_get_nameunit(sc->sc_dev), flags); USB_ATTACH_ERROR_RETURN; } if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), hid_input, &sc->sc_loc_y, &flags)) { printf("%s: mouse has no Y report\n", device_get_nameunit(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { printf("%s: Y report 0x%04x not supported\n", device_get_nameunit(sc->sc_dev), flags); USB_ATTACH_ERROR_RETURN; } /* try to guess the Z activator: first check Z, then WHEEL */ if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), hid_input, &sc->sc_loc_z, &flags) || hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), hid_input, &sc->sc_loc_z, &flags) || hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), hid_input, &sc->sc_loc_z, &flags)) { if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */ } else { sc->flags |= UMS_Z; } } /* The Microsoft Wireless Intellimouse 2.0 reports it's wheel * using 0x0048 (i've called it HUG_TWHEEL) and seems to expect * you to know that the byte after the wheel is the tilt axis. * There are no other HID axis descriptors other than X,Y and * TWHEEL */ if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), hid_input, &sc->sc_loc_t, &flags)) { sc->sc_loc_t.pos = sc->sc_loc_t.pos + 8; sc->flags |= UMS_T; } /* figure out the number of buttons */ for (i = 1; i <= MAX_BUTTONS; i++) if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), hid_input, &loc_btn, 0)) break; sc->nbuttons = i - 1; sc->sc_loc_btn = malloc(sizeof(struct hid_location)*sc->nbuttons, M_USBDEV, M_NOWAIT); if (!sc->sc_loc_btn) { printf("%s: no memory\n", device_get_nameunit(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } printf("%s: %d buttons%s%s.\n", device_get_nameunit(sc->sc_dev), sc->nbuttons, sc->flags & UMS_Z? " and Z dir" : "", sc->flags & UMS_T?" and a TILT dir": ""); for (i = 1; i <= sc->nbuttons; i++) hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), hid_input, &sc->sc_loc_btn[i-1], 0); sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid); sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_NOWAIT); if (!sc->sc_ibuf) { printf("%s: no memory\n", device_get_nameunit(sc->sc_dev)); free(sc->sc_loc_btn, M_USB); USB_ATTACH_ERROR_RETURN; } sc->sc_ep_addr = ed->bEndpointAddress; sc->sc_disconnected = 0; free(desc, M_TEMP); #ifdef USB_DEBUG DPRINTF(("ums_attach: sc=%p\n", sc)); DPRINTF(("ums_attach: X\t%d/%d\n", sc->sc_loc_x.pos, sc->sc_loc_x.size)); DPRINTF(("ums_attach: Y\t%d/%d\n", sc->sc_loc_y.pos, sc->sc_loc_y.size)); if (sc->flags & UMS_Z) DPRINTF(("ums_attach: Z\t%d/%d\n", sc->sc_loc_z.pos, sc->sc_loc_z.size)); for (i = 1; i <= sc->nbuttons; i++) { DPRINTF(("ums_attach: B%d\t%d/%d\n", i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size)); } DPRINTF(("ums_attach: size=%d, id=%d\n", sc->sc_isize, sc->sc_iid)); #endif if (sc->nbuttons > MOUSE_MSC_MAXBUTTON) sc->hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->hw.buttons = sc->nbuttons; sc->hw.iftype = MOUSE_IF_USB; sc->hw.type = MOUSE_MOUSE; sc->hw.model = MOUSE_MODEL_GENERIC; sc->hw.hwid = 0; sc->mode.protocol = MOUSE_PROTO_MSC; sc->mode.rate = -1; sc->mode.resolution = MOUSE_RES_UNKNOWN; sc->mode.accelfactor = 0; sc->mode.level = 0; sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->mode.syncmask[1] = MOUSE_MSC_SYNC; sc->status.flags = 0; sc->status.button = sc->status.obutton = 0; sc->status.dx = sc->status.dy = sc->status.dz = 0; #ifndef __FreeBSD__ sc->rsel.si_flags = 0; sc->rsel.si_pid = 0; #endif sc->dev = make_dev(&ums_cdevsw, device_get_unit(self), UID_ROOT, GID_OPERATOR, 0644, "ums%d", device_get_unit(self)); usb_callout_init(sc->callout_handle); if (usbd_get_quirks(uaa->device)->uq_flags & UQ_SPUR_BUT_UP) { DPRINTF(("%s: Spurious button up events\n", device_get_nameunit(sc->sc_dev))); sc->flags |= UMS_SPUR_BUT_UP; } USB_ATTACH_SUCCESS_RETURN; } static int ums_detach(device_t self) { struct ums_softc *sc = device_get_softc(self); if (sc->sc_enabled) ums_disable(sc); DPRINTF(("%s: disconnected\n", device_get_nameunit(self))); free(sc->sc_loc_btn, M_USB); free(sc->sc_ibuf, M_USB); /* someone waiting for data */ /* * XXX If we wakeup the process here, the device will be gone by * the time the process gets a chance to notice. *_close and friends * should be fixed to handle this case. * Or we should do a delayed detach for this. * Does this delay now force tsleep to exit with an error? */ if (sc->state & UMS_ASLEEP) { sc->state &= ~UMS_ASLEEP; wakeup(sc); } if (sc->state & UMS_SELECT) { sc->state &= ~UMS_SELECT; selwakeuppri(&sc->rsel, PZERO); } destroy_dev(sc->dev); return 0; } void ums_intr(xfer, addr, status) usbd_xfer_handle xfer; usbd_private_handle addr; usbd_status status; { struct ums_softc *sc = addr; u_char *ibuf; int dx, dy, dz, dt; int buttons = 0; int i; #define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i)) DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status)); DPRINTFN(5, ("ums_intr: data =")); for (i = 0; i < sc->sc_isize; i++) DPRINTFN(5, (" %02x", sc->sc_ibuf[i])); DPRINTFN(5, ("\n")); if (status == USBD_CANCELLED) return; if (status != USBD_NORMAL_COMPLETION) { DPRINTF(("ums_intr: status=%d\n", status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_intrpipe); if(status != USBD_IOERROR) return; } ibuf = sc->sc_ibuf; /* * The M$ Wireless Intellimouse 2.0 sends 1 extra leading byte of * data compared to most USB mice. This byte frequently switches * from 0x01 (usual state) to 0x02. I assume it is to allow * extra, non-standard, reporting (say battery-life). However * at the same time it generates a left-click message on the button * byte which causes spurious left-click's where there shouldn't be. * This should sort that. * Currently it's the only user of UMS_T so use it as an identifier. * We probably should switch to some more official quirk. */ if (sc->flags & UMS_T) { if (sc->sc_iid) { if (*ibuf++ == 0x02) return; } } else { if (sc->sc_iid) { if (*ibuf++ != sc->sc_iid) return; } } dx = hid_get_data(ibuf, &sc->sc_loc_x); dy = -hid_get_data(ibuf, &sc->sc_loc_y); dz = -hid_get_data(ibuf, &sc->sc_loc_z); if (sc->flags & UMS_T) dt = -hid_get_data(ibuf, &sc->sc_loc_t); else dt = 0; for (i = 0; i < sc->nbuttons; i++) if (hid_get_data(ibuf, &sc->sc_loc_btn[i])) buttons |= (1 << UMS_BUT(i)); if (dx || dy || dz || dt || (sc->flags & UMS_Z) || buttons != sc->status.button) { DPRINTFN(5, ("ums_intr: x:%d y:%d z:%d t:%d buttons:0x%x\n", dx, dy, dz, dt, buttons)); sc->status.button = buttons; sc->status.dx += dx; sc->status.dy += dy; sc->status.dz += dz; /* sc->status.dt += dt;*/ /* no way to export this yet */ /* Discard data in case of full buffer */ if (sc->qcount == sizeof(sc->qbuf)) { DPRINTF(("Buffer full, discarded packet")); return; } /* * The Qtronix keyboard has a built in PS/2 port for a mouse. * The firmware once in a while posts a spurious button up * event. This event we ignore by doing a timeout for 50 msecs. * If we receive dx=dy=dz=buttons=0 before we add the event to * the queue. * In any other case we delete the timeout event. */ if (sc->flags & UMS_SPUR_BUT_UP && dx == 0 && dy == 0 && dz == 0 && dt == 0 && buttons == 0) { usb_callout(sc->callout_handle, MS_TO_TICKS(50 /*msecs*/), ums_add_to_queue_timeout, (void *) sc); } else { usb_uncallout(sc->callout_handle, ums_add_to_queue_timeout, (void *) sc); ums_add_to_queue(sc, dx, dy, dz, dt, buttons); } } } static void ums_add_to_queue_timeout(void *priv) { struct ums_softc *sc = priv; int s; s = splusb(); ums_add_to_queue(sc, 0, 0, 0, 0, 0); splx(s); } static void ums_add_to_queue(struct ums_softc *sc, int dx, int dy, int dz, int dt, int buttons) { /* Discard data in case of full buffer */ if (sc->qhead+sc->mode.packetsize > sizeof(sc->qbuf)) { DPRINTF(("Buffer full, discarded packet")); return; } if (dx > 254) dx = 254; if (dx < -256) dx = -256; if (dy > 254) dy = 254; if (dy < -256) dy = -256; if (dz > 126) dz = 126; if (dz < -128) dz = -128; if (dt > 126) dt = 126; if (dt < -128) dt = -128; sc->qbuf[sc->qhead] = sc->mode.syncmask[1]; sc->qbuf[sc->qhead] |= ~buttons & MOUSE_MSC_BUTTONS; sc->qbuf[sc->qhead+1] = dx >> 1; sc->qbuf[sc->qhead+2] = dy >> 1; sc->qbuf[sc->qhead+3] = dx - (dx >> 1); sc->qbuf[sc->qhead+4] = dy - (dy >> 1); if (sc->mode.level == 1) { sc->qbuf[sc->qhead+5] = dz >> 1; sc->qbuf[sc->qhead+6] = dz - (dz >> 1); sc->qbuf[sc->qhead+7] = ((~buttons >> 3) & MOUSE_SYS_EXTBUTTONS); } sc->qhead += sc->mode.packetsize; sc->qcount += sc->mode.packetsize; /* wrap round at end of buffer */ if (sc->qhead >= sizeof(sc->qbuf)) sc->qhead = 0; /* someone waiting for data */ if (sc->state & UMS_ASLEEP) { sc->state &= ~UMS_ASLEEP; wakeup(sc); } if (sc->state & UMS_SELECT) { sc->state &= ~UMS_SELECT; selwakeuppri(&sc->rsel, PZERO); } } static int ums_enable(v) void *v; { struct ums_softc *sc = v; usbd_status err; if (sc->sc_enabled) return EBUSY; sc->sc_enabled = 1; sc->qcount = 0; sc->qhead = sc->qtail = 0; sc->status.flags = 0; sc->status.button = sc->status.obutton = 0; sc->status.dx = sc->status.dy = sc->status.dz /* = sc->status.dt */ = 0; callout_handle_init((struct callout_handle *)&sc->callout_handle); /* Set up interrupt pipe. */ err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, sc->sc_ibuf, sc->sc_isize, ums_intr, USBD_DEFAULT_INTERVAL); if (err) { DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n", err)); sc->sc_enabled = 0; return (EIO); } return (0); } static void ums_disable(priv) void *priv; { struct ums_softc *sc = priv; usb_uncallout(sc->callout_handle, ums_add_to_queue_timeout, sc); /* Disable interrupts. */ usbd_abort_pipe(sc->sc_intrpipe); usbd_close_pipe(sc->sc_intrpipe); sc->sc_enabled = 0; if (sc->qcount != 0) DPRINTF(("Discarded %d bytes in queue\n", sc->qcount)); } static int ums_open(struct cdev *dev, int flag, int fmt, usb_proc_ptr p) { struct ums_softc *sc; USB_GET_SC_OPEN(ums, UMSUNIT(dev), sc); return ums_enable(sc); } static int ums_close(struct cdev *dev, int flag, int fmt, usb_proc_ptr p) { struct ums_softc *sc; USB_GET_SC(ums, UMSUNIT(dev), sc); if (!sc) return 0; if (sc->sc_enabled) ums_disable(sc); return 0; } static int ums_read(struct cdev *dev, struct uio *uio, int flag) { struct ums_softc *sc; int s; char buf[sizeof(sc->qbuf)]; int l = 0; int error; USB_GET_SC(ums, UMSUNIT(dev), sc); s = splusb(); if (!sc) { splx(s); return EIO; } while (sc->qcount == 0 ) { if (flag & O_NONBLOCK) { /* non-blocking I/O */ splx(s); return EWOULDBLOCK; } sc->state |= UMS_ASLEEP; /* blocking I/O */ error = tsleep(sc, PZERO | PCATCH, "umsrea", 0); if (error) { splx(s); return error; } else if (!sc->sc_enabled) { splx(s); return EINTR; } /* check whether the device is still there */ sc = devclass_get_softc(ums_devclass, UMSUNIT(dev)); if (!sc) { splx(s); return EIO; } } /* * XXX we could optimise the use of splx/splusb somewhat. The writer * process only extends qcount and qtail. We could copy them and use the copies * to do the copying out of the queue. */ while ((sc->qcount > 0) && (uio->uio_resid > 0)) { l = (sc->qcount < uio->uio_resid? sc->qcount:uio->uio_resid); if (l > sizeof(buf)) l = sizeof(buf); if (l > sizeof(sc->qbuf) - sc->qtail) /* transfer till end of buf */ l = sizeof(sc->qbuf) - sc->qtail; splx(s); uiomove(&sc->qbuf[sc->qtail], l, uio); s = splusb(); if ( sc->qcount - l < 0 ) { DPRINTF(("qcount below 0, count=%d l=%d\n", sc->qcount, l)); sc->qcount = l; } sc->qcount -= l; /* remove the bytes from the buffer */ sc->qtail = (sc->qtail + l) % sizeof(sc->qbuf); } splx(s); return 0; } static int ums_poll(struct cdev *dev, int events, usb_proc_ptr p) { struct ums_softc *sc; int revents = 0; int s; USB_GET_SC(ums, UMSUNIT(dev), sc); if (!sc) return 0; s = splusb(); if (events & (POLLIN | POLLRDNORM)) { if (sc->qcount) { revents = events & (POLLIN | POLLRDNORM); } else { sc->state |= UMS_SELECT; selrecord(p, &sc->rsel); } } splx(s); return revents; } int ums_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) { struct ums_softc *sc; int error = 0; int s; mousemode_t mode; USB_GET_SC(ums, UMSUNIT(dev), sc); if (!sc) return EIO; switch(cmd) { case MOUSE_GETHWINFO: *(mousehw_t *)addr = sc->hw; break; case MOUSE_GETMODE: *(mousemode_t *)addr = sc->mode; break; case MOUSE_SETMODE: mode = *(mousemode_t *)addr; if (mode.level == -1) /* don't change the current setting */ ; else if ((mode.level < 0) || (mode.level > 1)) return (EINVAL); s = splusb(); sc->mode.level = mode.level; if (sc->mode.level == 0) { if (sc->nbuttons > MOUSE_MSC_MAXBUTTON) sc->hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->hw.buttons = sc->nbuttons; sc->mode.protocol = MOUSE_PROTO_MSC; sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->mode.syncmask[1] = MOUSE_MSC_SYNC; } else if (sc->mode.level == 1) { if (sc->nbuttons > MOUSE_SYS_MAXBUTTON) sc->hw.buttons = MOUSE_SYS_MAXBUTTON; else sc->hw.buttons = sc->nbuttons; sc->mode.protocol = MOUSE_PROTO_SYSMOUSE; sc->mode.packetsize = MOUSE_SYS_PACKETSIZE; sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK; sc->mode.syncmask[1] = MOUSE_SYS_SYNC; } bzero(sc->qbuf, sizeof(sc->qbuf)); sc->qhead = sc->qtail = sc->qcount = 0; splx(s); break; case MOUSE_GETLEVEL: *(int *)addr = sc->mode.level; break; case MOUSE_SETLEVEL: if (*(int *)addr < 0 || *(int *)addr > 1) return (EINVAL); s = splusb(); sc->mode.level = *(int *)addr; if (sc->mode.level == 0) { if (sc->nbuttons > MOUSE_MSC_MAXBUTTON) sc->hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->hw.buttons = sc->nbuttons; sc->mode.protocol = MOUSE_PROTO_MSC; sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->mode.syncmask[1] = MOUSE_MSC_SYNC; } else if (sc->mode.level == 1) { if (sc->nbuttons > MOUSE_SYS_MAXBUTTON) sc->hw.buttons = MOUSE_SYS_MAXBUTTON; else sc->hw.buttons = sc->nbuttons; sc->mode.protocol = MOUSE_PROTO_SYSMOUSE; sc->mode.packetsize = MOUSE_SYS_PACKETSIZE; sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK; sc->mode.syncmask[1] = MOUSE_SYS_SYNC; } bzero(sc->qbuf, sizeof(sc->qbuf)); sc->qhead = sc->qtail = sc->qcount = 0; splx(s); break; case MOUSE_GETSTATUS: { mousestatus_t *status = (mousestatus_t *) addr; s = splusb(); *status = sc->status; sc->status.obutton = sc->status.button; sc->status.button = 0; sc->status.dx = sc->status.dy = sc->status.dz = /* sc->status.dt = */ 0; splx(s); if (status->dx || status->dy || status->dz /* || status->dt */) status->flags |= MOUSE_POSCHANGED; if (status->button != status->obutton) status->flags |= MOUSE_BUTTONSCHANGED; break; } default: error = ENOTTY; } return error; } DRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, usbd_driver_load, 0); Index: head/sys/dev/usb/usb.c =================================================================== --- head/sys/dev/usb/usb.c (revision 169488) +++ head/sys/dev/usb/usb.c (revision 169489) @@ -1,1022 +1,1016 @@ /* $NetBSD: usb.c,v 1.68 2002/02/20 20:30:12 christos Exp $ */ /* Also already merged from NetBSD: * $NetBSD: usb.c,v 1.70 2002/05/09 21:54:32 augustss Exp $ * $NetBSD: usb.c,v 1.71 2002/06/01 23:51:04 lukem Exp $ * $NetBSD: usb.c,v 1.73 2002/09/23 05:51:19 simonb Exp $ * $NetBSD: usb.c,v 1.80 2003/11/07 17:03:25 wiz Exp $ */ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * USB specifications and other documentation can be found at * http://www.usb.org/developers/docs/ and * http://www.usb.org/developers/devclass_docs/ */ #include #include #include #include #include -#if __FreeBSD_version >= 500000 #include -#endif #if defined(__NetBSD__) || defined(__OpenBSD__) #include #elif defined(__FreeBSD__) #include #include #include #include #include #include #endif #include #include #include #include #if __FreeBSD_version >= 500014 #include #else #include #endif #include #include #include #include #include #include #define USBUNIT(d) (minor(d)) /* usb_discover device nodes, kthread */ #define USB_DEV_MINOR 255 /* event queue device */ #if defined(__FreeBSD__) MALLOC_DEFINE(M_USB, "USB", "USB"); MALLOC_DEFINE(M_USBDEV, "USBdev", "USB device"); MALLOC_DEFINE(M_USBHC, "USBHC", "USB host controller"); #include "usb_if.h" #endif /* defined(__FreeBSD__) */ #include #include #include /* Define this unconditionally in case a kernel module is loaded that * has been compiled with debugging options. */ SYSCTL_NODE(_hw, OID_AUTO, usb, CTLFLAG_RW, 0, "USB debugging"); #ifdef USB_DEBUG #define DPRINTF(x) if (usbdebug) logprintf x #define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x int usbdebug = 0; SYSCTL_INT(_hw_usb, OID_AUTO, debug, CTLFLAG_RW, &usbdebug, 0, "usb debug level"); /* * 0 - do usual exploration * 1 - do not use timeout exploration * >1 - do no exploration */ int usb_noexplore = 0; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif struct usb_softc { device_t sc_dev; /* base device */ #ifdef __FreeBSD__ struct cdev *sc_usbdev; /* /dev/usbN device */ TAILQ_ENTRY(usb_softc) sc_coldexplist; /* cold needs-explore list */ #endif usbd_bus_handle sc_bus; /* USB controller */ struct usbd_port sc_port; /* dummy port for root hub */ struct proc *sc_event_thread; char sc_dying; }; struct usb_taskq { TAILQ_HEAD(, usb_task) tasks; struct proc *task_thread_proc; const char *name; int taskcreated; /* task thread exists. */ }; static struct usb_taskq usb_taskq[USB_NUM_TASKQS]; #if defined(__NetBSD__) || defined(__OpenBSD__) cdev_decl(usb); #elif defined(__FreeBSD__) d_open_t usbopen; d_close_t usbclose; d_read_t usbread; d_ioctl_t usbioctl; d_poll_t usbpoll; struct cdevsw usb_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = usbopen, .d_close = usbclose, .d_read = usbread, .d_ioctl = usbioctl, .d_poll = usbpoll, .d_name = "usb", #if __FreeBSD_version < 500014 .d_bmaj = -1 #endif }; #endif static void usb_discover(void *); #ifdef __FreeBSD__ static bus_child_detached_t usb_child_detached; #endif static void usb_create_event_thread(void *); static void usb_event_thread(void *); static void usb_task_thread(void *); #ifdef __FreeBSD__ static struct cdev *usb_dev; /* The /dev/usb device. */ static int usb_ndevs; /* Number of /dev/usbN devices. */ /* Busses to explore at the end of boot-time device configuration. */ static TAILQ_HEAD(, usb_softc) usb_coldexplist = TAILQ_HEAD_INITIALIZER(usb_coldexplist); #endif #define USB_MAX_EVENTS 100 struct usb_event_q { struct usb_event ue; TAILQ_ENTRY(usb_event_q) next; }; static TAILQ_HEAD(, usb_event_q) usb_events = TAILQ_HEAD_INITIALIZER(usb_events); static int usb_nevents = 0; static struct selinfo usb_selevent; static struct proc *usb_async_proc; /* process that wants USB SIGIO */ static int usb_dev_open = 0; static void usb_add_event(int, struct usb_event *); static int usb_get_next_event(struct usb_event *); static const char *usbrev_str[] = USBREV_STR; USB_DECLARE_DRIVER_INIT(usb, DEVMETHOD(bus_child_detached, usb_child_detached), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown) ); #if defined(__FreeBSD__) MODULE_VERSION(usb, 1); #endif USB_MATCH(usb) { DPRINTF(("usbd_match\n")); return (UMATCH_GENERIC); } USB_ATTACH(usb) { #if defined(__NetBSD__) || defined(__OpenBSD__) struct usb_softc *sc = (struct usb_softc *)self; #elif defined(__FreeBSD__) struct usb_softc *sc = device_get_softc(self); void *aux = device_get_ivars(self); #endif usbd_device_handle dev; usbd_status err; int usbrev; int speed; struct usb_event ue; sc->sc_dev = self; DPRINTF(("usbd_attach\n")); usbd_init(); sc->sc_bus = aux; sc->sc_bus->usbctl = sc; sc->sc_port.power = USB_MAX_POWER; #if defined(__FreeBSD__) printf("%s", device_get_nameunit(sc->sc_dev)); #endif usbrev = sc->sc_bus->usbrev; printf(": USB revision %s", usbrev_str[usbrev]); switch (usbrev) { case USBREV_1_0: case USBREV_1_1: speed = USB_SPEED_FULL; break; case USBREV_2_0: speed = USB_SPEED_HIGH; break; default: printf(", not supported\n"); sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; } printf("\n"); /* Make sure not to use tsleep() if we are cold booting. */ if (cold) sc->sc_bus->use_polling++; ue.u.ue_ctrlr.ue_bus = device_get_unit(sc->sc_dev); usb_add_event(USB_EVENT_CTRLR_ATTACH, &ue); #ifdef USB_USE_SOFTINTR #ifdef __HAVE_GENERIC_SOFT_INTERRUPTS /* XXX we should have our own level */ sc->sc_bus->soft = softintr_establish(IPL_SOFTNET, sc->sc_bus->methods->soft_intr, sc->sc_bus); if (sc->sc_bus->soft == NULL) { printf("%s: can't register softintr\n", device_get_nameunit(sc->sc_dev)); sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; } #else usb_callout_init(sc->sc_bus->softi); #endif #endif err = usbd_new_device(USBDEV(sc->sc_dev), sc->sc_bus, 0, speed, 0, &sc->sc_port); if (!err) { dev = sc->sc_port.device; if (dev->hub == NULL) { sc->sc_dying = 1; printf("%s: root device is not a hub\n", device_get_nameunit(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } sc->sc_bus->root_hub = dev; #if 1 /* * Turning this code off will delay attachment of USB devices * until the USB event thread is running, which means that * the keyboard will not work until after cold boot. */ #if defined(__FreeBSD__) if (cold) { /* Explore high-speed busses before others. */ if (speed == USB_SPEED_HIGH) dev->hub->explore(sc->sc_bus->root_hub); else TAILQ_INSERT_TAIL(&usb_coldexplist, sc, sc_coldexplist); } #else if (cold && (sc->sc_dev.dv_cfdata->cf_flags & 1)) dev->hub->explore(sc->sc_bus->root_hub); #endif #endif } else { printf("%s: root hub problem, error=%d\n", device_get_nameunit(sc->sc_dev), err); sc->sc_dying = 1; } if (cold) sc->sc_bus->use_polling--; config_pending_incr(); #if defined(__NetBSD__) || defined(__OpenBSD__) usb_kthread_create(usb_create_event_thread, sc); #endif #if defined(__FreeBSD__) usb_create_event_thread(sc); /* The per controller devices (used for usb_discover) */ /* XXX This is redundant now, but old usbd's will want it */ sc->sc_usbdev = make_dev(&usb_cdevsw, device_get_unit(self), UID_ROOT, GID_OPERATOR, 0660, "usb%d", device_get_unit(self)); if (usb_ndevs++ == 0) { /* The device spitting out events */ usb_dev = make_dev(&usb_cdevsw, USB_DEV_MINOR, UID_ROOT, GID_OPERATOR, 0660, "usb"); } #endif USB_ATTACH_SUCCESS_RETURN; } static const char *taskq_names[] = USB_TASKQ_NAMES; void usb_create_event_thread(void *arg) { struct usb_softc *sc = arg; struct usb_taskq *taskq; int i; if (usb_kthread_create1(usb_event_thread, sc, &sc->sc_event_thread, "%s", device_get_nameunit(sc->sc_dev))) { printf("%s: unable to create event thread for\n", device_get_nameunit(sc->sc_dev)); panic("usb_create_event_thread"); } for (i = 0; i < USB_NUM_TASKQS; i++) { taskq = &usb_taskq[i]; if (taskq->taskcreated == 0) { taskq->taskcreated = 1; taskq->name = taskq_names[i]; TAILQ_INIT(&taskq->tasks); if (usb_kthread_create2(usb_task_thread, taskq, &taskq->task_thread_proc, taskq->name)) { printf("unable to create task thread\n"); panic("usb_create_event_thread task"); } } } } /* * Add a task to be performed by the task thread. This function can be * called from any context and the task will be executed in a process * context ASAP. */ void usb_add_task(usbd_device_handle dev, struct usb_task *task, int queue) { struct usb_taskq *taskq; int s; s = splusb(); taskq = &usb_taskq[queue]; if (task->queue == -1) { DPRINTFN(2,("usb_add_task: task=%p\n", task)); TAILQ_INSERT_TAIL(&taskq->tasks, task, next); task->queue = queue; } else { DPRINTFN(3,("usb_add_task: task=%p on q\n", task)); } wakeup(&taskq->tasks); splx(s); } void usb_rem_task(usbd_device_handle dev, struct usb_task *task) { struct usb_taskq *taskq; int s; s = splusb(); if (task->queue != -1) { taskq = &usb_taskq[task->queue]; TAILQ_REMOVE(&taskq->tasks, task, next); task->queue = -1; } splx(s); } void usb_event_thread(void *arg) { static int newthread_wchan; struct usb_softc *sc = arg; -#if defined(__FreeBSD__) && __FreeBSD_version >= 500000 +#if defined(__FreeBSD__) mtx_lock(&Giant); #endif DPRINTF(("usb_event_thread: start\n")); /* * In case this controller is a companion controller to an * EHCI controller we need to wait until the EHCI controller * has grabbed the port. What we do here is wait until no new * USB threads have been created in a while. XXX we actually * just want to wait for the PCI slot to be fully scanned. * * Note that when you `kldload usb' it actually attaches the * devices in order that the drivers appear in the kld, not the * normal PCI order, since the addition of each driver within * usb.ko (ohci, ehci etc.) causes a separate PCI bus re-scan. */ wakeup(&newthread_wchan); for (;;) { if (tsleep(&newthread_wchan , PWAIT, "usbets", hz * 4) != 0) break; } /* Make sure first discover does something. */ sc->sc_bus->needs_explore = 1; usb_discover(sc); config_pending_decr(); while (!sc->sc_dying) { #ifdef USB_DEBUG if (usb_noexplore < 2) #endif usb_discover(sc); #ifdef USB_DEBUG (void)tsleep(&sc->sc_bus->needs_explore, PWAIT, "usbevt", usb_noexplore ? 0 : hz * 60); #else (void)tsleep(&sc->sc_bus->needs_explore, PWAIT, "usbevt", hz * 60); #endif DPRINTFN(2,("usb_event_thread: woke up\n")); } sc->sc_event_thread = NULL; /* In case parent is waiting for us to exit. */ wakeup(sc); DPRINTF(("usb_event_thread: exit\n")); kthread_exit(0); } void usb_task_thread(void *arg) { struct usb_task *task; struct usb_taskq *taskq; int s; -#if defined(__FreeBSD__) && __FreeBSD_version >= 500000 +#if defined(__FreeBSD__) mtx_lock(&Giant); #endif taskq = arg; DPRINTF(("usb_task_thread: start taskq %s\n", taskq->name)); s = splusb(); while (usb_ndevs > 0) { task = TAILQ_FIRST(&taskq->tasks); if (task == NULL) { tsleep(&taskq->tasks, PWAIT, "usbtsk", 0); task = TAILQ_FIRST(&taskq->tasks); } DPRINTFN(2,("usb_task_thread: woke up task=%p\n", task)); if (task != NULL) { TAILQ_REMOVE(&taskq->tasks, task, next); task->queue = -1; splx(s); task->fun(task->arg); s = splusb(); } } splx(s); taskq->taskcreated = 0; wakeup(&taskq->taskcreated); DPRINTF(("usb_event_thread: exit\n")); kthread_exit(0); } #if defined(__NetBSD__) || defined(__OpenBSD__) int usbctlprint(void *aux, const char *pnp) { /* only "usb"es can attach to host controllers */ if (pnp) printf("usb at %s", pnp); return (UNCONF); } #endif /* defined(__NetBSD__) || defined(__OpenBSD__) */ int usbopen(struct cdev *dev, int flag, int mode, usb_proc_ptr p) { int unit = USBUNIT(dev); struct usb_softc *sc; if (unit == USB_DEV_MINOR) { if (usb_dev_open) return (EBUSY); usb_dev_open = 1; usb_async_proc = 0; return (0); } USB_GET_SC_OPEN(usb, unit, sc); if (sc->sc_dying) return (EIO); return (0); } int usbread(struct cdev *dev, struct uio *uio, int flag) { struct usb_event ue; int unit = USBUNIT(dev); int s, error, n; if (unit != USB_DEV_MINOR) return (ENODEV); if (uio->uio_resid != sizeof(struct usb_event)) return (EINVAL); error = 0; s = splusb(); for (;;) { n = usb_get_next_event(&ue); if (n != 0) break; if (flag & O_NONBLOCK) { error = EWOULDBLOCK; break; } error = tsleep(&usb_events, PZERO | PCATCH, "usbrea", 0); if (error) break; } splx(s); if (!error) error = uiomove((void *)&ue, uio->uio_resid, uio); return (error); } int usbclose(struct cdev *dev, int flag, int mode, usb_proc_ptr p) { int unit = USBUNIT(dev); if (unit == USB_DEV_MINOR) { usb_async_proc = 0; usb_dev_open = 0; } return (0); } int usbioctl(struct cdev *devt, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) { struct usb_softc *sc; int unit = USBUNIT(devt); if (unit == USB_DEV_MINOR) { switch (cmd) { case FIONBIO: /* All handled in the upper FS layer. */ return (0); case FIOASYNC: if (*(int *)data) -#if __FreeBSD_version >= 500000 usb_async_proc = p->td_proc; -#else - usb_async_proc = p; -#endif else usb_async_proc = 0; return (0); default: return (EINVAL); } } USB_GET_SC(usb, unit, sc); if (sc->sc_dying) return (EIO); switch (cmd) { #if defined(__FreeBSD__) /* This part should be deleted */ case USB_DISCOVER: break; #endif case USB_REQUEST: { struct usb_ctl_request *ur = (void *)data; int len = UGETW(ur->ucr_request.wLength); struct iovec iov; struct uio uio; void *ptr = 0; int addr = ur->ucr_addr; usbd_status err; int error = 0; DPRINTF(("usbioctl: USB_REQUEST addr=%d len=%d\n", addr, len)); if (len < 0 || len > 32768) return (EINVAL); if (addr < 0 || addr >= USB_MAX_DEVICES || sc->sc_bus->devices[addr] == 0) return (EINVAL); if (len != 0) { iov.iov_base = (caddr_t)ur->ucr_data; iov.iov_len = len; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_resid = len; uio.uio_offset = 0; uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = ur->ucr_request.bmRequestType & UT_READ ? UIO_READ : UIO_WRITE; uio.uio_td = p; ptr = malloc(len, M_TEMP, M_WAITOK); if (uio.uio_rw == UIO_WRITE) { error = uiomove(ptr, len, &uio); if (error) goto ret; } } err = usbd_do_request_flags(sc->sc_bus->devices[addr], &ur->ucr_request, ptr, ur->ucr_flags, &ur->ucr_actlen, USBD_DEFAULT_TIMEOUT); if (err) { error = EIO; goto ret; } if (len != 0) { if (uio.uio_rw == UIO_READ) { error = uiomove(ptr, len, &uio); if (error) goto ret; } } ret: if (ptr) free(ptr, M_TEMP); return (error); } case USB_DEVICEINFO: { struct usb_device_info *di = (void *)data; int addr = di->udi_addr; usbd_device_handle dev; if (addr < 1 || addr >= USB_MAX_DEVICES) return (EINVAL); dev = sc->sc_bus->devices[addr]; if (dev == NULL) return (ENXIO); usbd_fill_deviceinfo(dev, di, 1); break; } case USB_DEVICESTATS: *(struct usb_device_stats *)data = sc->sc_bus->stats; break; default: return (EINVAL); } return (0); } int usbpoll(struct cdev *dev, int events, usb_proc_ptr p) { int revents, mask, s; int unit = USBUNIT(dev); if (unit == USB_DEV_MINOR) { revents = 0; mask = POLLIN | POLLRDNORM; s = splusb(); if (events & mask && usb_nevents > 0) revents |= events & mask; if (revents == 0 && events & mask) selrecord(p, &usb_selevent); splx(s); return (revents); } else { #if defined(__FreeBSD__) return (0); /* select/poll never wakes up - back compat */ #else return (ENXIO); #endif } } /* Explore device tree from the root. */ static void usb_discover(void *v) { struct usb_softc *sc = v; #if defined(__FreeBSD__) /* splxxx should be changed to mutexes for preemption safety some day */ int s; #endif DPRINTFN(2,("usb_discover\n")); #ifdef USB_DEBUG if (usb_noexplore > 1) return; #endif /* * We need mutual exclusion while traversing the device tree, * but this is guaranteed since this function is only called * from the event thread for the controller. */ #if defined(__FreeBSD__) s = splusb(); #endif while (sc->sc_bus->needs_explore && !sc->sc_dying) { sc->sc_bus->needs_explore = 0; #if defined(__FreeBSD__) splx(s); #endif sc->sc_bus->root_hub->hub->explore(sc->sc_bus->root_hub); #if defined(__FreeBSD__) s = splusb(); #endif } #if defined(__FreeBSD__) splx(s); #endif } void usb_needs_explore(usbd_device_handle dev) { DPRINTFN(2,("usb_needs_explore\n")); dev->bus->needs_explore = 1; wakeup(&dev->bus->needs_explore); } /* Called at splusb() */ int usb_get_next_event(struct usb_event *ue) { struct usb_event_q *ueq; if (usb_nevents <= 0) return (0); ueq = TAILQ_FIRST(&usb_events); #ifdef DIAGNOSTIC if (ueq == NULL) { printf("usb: usb_nevents got out of sync! %d\n", usb_nevents); usb_nevents = 0; return (0); } #endif *ue = ueq->ue; TAILQ_REMOVE(&usb_events, ueq, next); free(ueq, M_USBDEV); usb_nevents--; return (1); } void usbd_add_dev_event(int type, usbd_device_handle udev) { struct usb_event ue; usbd_fill_deviceinfo(udev, &ue.u.ue_device, USB_EVENT_IS_ATTACH(type)); usb_add_event(type, &ue); } void usbd_add_drv_event(int type, usbd_device_handle udev, device_t dev) { struct usb_event ue; ue.u.ue_driver.ue_cookie = udev->cookie; strncpy(ue.u.ue_driver.ue_devname, device_get_nameunit(dev), sizeof ue.u.ue_driver.ue_devname); usb_add_event(type, &ue); } void usb_add_event(int type, struct usb_event *uep) { struct usb_event_q *ueq; struct usb_event ue; struct timeval thetime; int s; ueq = malloc(sizeof *ueq, M_USBDEV, M_WAITOK); ueq->ue = *uep; ueq->ue.ue_type = type; microtime(&thetime); TIMEVAL_TO_TIMESPEC(&thetime, &ueq->ue.ue_time); s = splusb(); if (USB_EVENT_IS_DETACH(type)) { struct usb_event_q *ueqi, *ueqi_next; for (ueqi = TAILQ_FIRST(&usb_events); ueqi; ueqi = ueqi_next) { ueqi_next = TAILQ_NEXT(ueqi, next); if (ueqi->ue.u.ue_driver.ue_cookie.cookie == uep->u.ue_device.udi_cookie.cookie) { TAILQ_REMOVE(&usb_events, ueqi, next); free(ueqi, M_USBDEV); usb_nevents--; ueqi_next = TAILQ_FIRST(&usb_events); } } } if (usb_nevents >= USB_MAX_EVENTS) { /* Too many queued events, drop an old one. */ DPRINTF(("usb: event dropped\n")); (void)usb_get_next_event(&ue); } TAILQ_INSERT_TAIL(&usb_events, ueq, next); usb_nevents++; wakeup(&usb_events); selwakeuppri(&usb_selevent, PZERO); if (usb_async_proc != NULL) { PROC_LOCK(usb_async_proc); psignal(usb_async_proc, SIGIO); PROC_UNLOCK(usb_async_proc); } splx(s); } void usb_schedsoftintr(usbd_bus_handle bus) { DPRINTFN(10,("usb_schedsoftintr: polling=%d\n", bus->use_polling)); #ifdef USB_USE_SOFTINTR if (bus->use_polling) { bus->methods->soft_intr(bus); } else { #ifdef __HAVE_GENERIC_SOFT_INTERRUPTS softintr_schedule(bus->soft); #else if (!callout_pending(&bus->softi)) callout_reset(&bus->softi, 0, bus->methods->soft_intr, bus); #endif /* __HAVE_GENERIC_SOFT_INTERRUPTS */ } #else bus->methods->soft_intr(bus); #endif /* USB_USE_SOFTINTR */ } #if defined(__NetBSD__) || defined(__OpenBSD__) int usb_activate(device_t self, enum devact act) { struct usb_softc *sc = (struct usb_softc *)self; usbd_device_handle dev = sc->sc_port.device; int i, rv = 0; switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); case DVACT_DEACTIVATE: sc->sc_dying = 1; if (dev != NULL && dev->cdesc != NULL && dev->subdevs != NULL) { for (i = 0; dev->subdevs[i]; i++) rv |= config_deactivate(dev->subdevs[i]); } break; } return (rv); } #endif USB_DETACH(usb) { USB_DETACH_START(usb, sc); struct usb_event ue; struct usb_taskq *taskq; int i; DPRINTF(("usb_detach: start\n")); sc->sc_dying = 1; /* Make all devices disconnect. */ if (sc->sc_port.device != NULL) usb_disconnect_port(&sc->sc_port, self); /* Kill off event thread. */ if (sc->sc_event_thread != NULL) { wakeup(&sc->sc_bus->needs_explore); if (tsleep(sc, PWAIT, "usbdet", hz * 60)) printf("%s: event thread didn't die\n", device_get_nameunit(sc->sc_dev)); DPRINTF(("usb_detach: event thread dead\n")); } #ifdef __FreeBSD__ destroy_dev(sc->sc_usbdev); if (--usb_ndevs == 0) { destroy_dev(usb_dev); usb_dev = NULL; for (i = 0; i < USB_NUM_TASKQS; i++) { taskq = &usb_taskq[i]; wakeup(&taskq->tasks); if (tsleep(&taskq->taskcreated, PWAIT, "usbtdt", hz * 60)) { printf("usb task thread %s didn't die\n", taskq->name); } } } #endif usbd_finish(); #ifdef USB_USE_SOFTINTR #ifdef __HAVE_GENERIC_SOFT_INTERRUPTS if (sc->sc_bus->soft != NULL) { softintr_disestablish(sc->sc_bus->soft); sc->sc_bus->soft = NULL; } #else callout_stop(&sc->sc_bus->softi); #endif #endif ue.u.ue_ctrlr.ue_bus = device_get_unit(sc->sc_dev); usb_add_event(USB_EVENT_CTRLR_DETACH, &ue); return (0); } #if defined(__FreeBSD__) static void usb_child_detached(device_t self, device_t child) { struct usb_softc *sc = device_get_softc(self); /* XXX, should check it is the right device. */ sc->sc_port.device = NULL; } /* Explore USB busses at the end of device configuration. */ static void usb_cold_explore(void *arg) { struct usb_softc *sc; KASSERT(cold || TAILQ_EMPTY(&usb_coldexplist), ("usb_cold_explore: busses to explore when !cold")); while (!TAILQ_EMPTY(&usb_coldexplist)) { sc = TAILQ_FIRST(&usb_coldexplist); TAILQ_REMOVE(&usb_coldexplist, sc, sc_coldexplist); sc->sc_bus->use_polling++; sc->sc_port.device->hub->explore(sc->sc_bus->root_hub); sc->sc_bus->use_polling--; } } DRIVER_MODULE(usb, ohci, usb_driver, usb_devclass, 0, 0); DRIVER_MODULE(usb, uhci, usb_driver, usb_devclass, 0, 0); DRIVER_MODULE(usb, ehci, usb_driver, usb_devclass, 0, 0); DRIVER_MODULE(usb, slhci, usb_driver, usb_devclass, 0, 0); SYSINIT(usb_cold_explore, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, usb_cold_explore, NULL); #endif Index: head/sys/dev/usb/usb_mem.c =================================================================== --- head/sys/dev/usb/usb_mem.c (revision 169488) +++ head/sys/dev/usb/usb_mem.c (revision 169489) @@ -1,309 +1,302 @@ /* $NetBSD: usb_mem.c,v 1.26 2003/02/01 06:23:40 thorpej Exp $ */ /* $FreeBSD$ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * USB DMA memory allocation. * We need to allocate a lot of small (many 8 byte, some larger) * memory blocks that can be used for DMA. Using the bus_dma * routines directly would incur large overheads in space and time. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #if defined(__NetBSD__) || defined(__OpenBSD__) #include /* for usbdivar.h */ #include #elif defined(__FreeBSD__) #include #include #include #endif #include #include #include #ifdef DIAGNOSTIC #include #endif #include #include #include /* just for usb_dma_t */ #include #ifdef USB_DEBUG #define DPRINTF(x) if (usbdebug) logprintf x #define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x extern int usbdebug; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define USB_MEM_SMALL 64 #define USB_MEM_CHUNKS (PAGE_SIZE / USB_MEM_SMALL) #define USB_MEM_BLOCK (USB_MEM_SMALL * USB_MEM_CHUNKS) /* This struct is overlayed on free fragments. */ struct usb_frag_dma { usb_dma_block_t *block; u_int offs; LIST_ENTRY(usb_frag_dma) next; }; static bus_dmamap_callback_t usbmem_callback; static usbd_status usb_block_allocmem(bus_dma_tag_t, size_t, size_t, usb_dma_block_t **); static void usb_block_freemem(usb_dma_block_t *); static LIST_HEAD(, usb_dma_block) usb_blk_freelist = LIST_HEAD_INITIALIZER(usb_blk_freelist); static int usb_blk_nfree = 0; /* XXX should have different free list for different tags (for speed) */ static LIST_HEAD(, usb_frag_dma) usb_frag_freelist = LIST_HEAD_INITIALIZER(usb_frag_freelist); static void usbmem_callback(void *arg, bus_dma_segment_t *segs, int nseg, int error) { int i; usb_dma_block_t *p = arg; if (error == EFBIG) { printf("usb: mapping to large\n"); return; } p->nsegs = nseg; for (i = 0; i < nseg && i < sizeof p->segs / sizeof *p->segs; i++) p->segs[i] = segs[i]; } static usbd_status usb_block_allocmem(bus_dma_tag_t tag, size_t size, size_t align, usb_dma_block_t **dmap) { usb_dma_block_t *p; int s; DPRINTFN(5, ("usb_block_allocmem: size=%lu align=%lu\n", (u_long)size, (u_long)align)); #ifdef DIAGNOSTIC if (!curproc) { printf("usb_block_allocmem: in interrupt context, size=%lu\n", (unsigned long) size); } #endif s = splusb(); /* First check the free list. */ for (p = LIST_FIRST(&usb_blk_freelist); p; p = LIST_NEXT(p, next)) { if (p->tag == tag && p->size >= size && p->size < size * 2 && p->align >= align) { LIST_REMOVE(p, next); usb_blk_nfree--; splx(s); *dmap = p; DPRINTFN(6,("usb_block_allocmem: free list size=%lu\n", (u_long)p->size)); return (USBD_NORMAL_COMPLETION); } } splx(s); #ifdef DIAGNOSTIC if (!curproc) { printf("usb_block_allocmem: in interrupt context, failed\n"); return (USBD_NOMEM); } #endif DPRINTFN(6, ("usb_block_allocmem: no free\n")); p = malloc(sizeof *p, M_USB, M_NOWAIT); if (p == NULL) return (USBD_NOMEM); -#if __FreeBSD_version >= 500000 if (bus_dma_tag_create(tag, align, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, sizeof(p->segs) / sizeof(p->segs[0]), size, 0, NULL, NULL, &p->tag) == ENOMEM) -#else - if (bus_dma_tag_create(tag, align, 0, - BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, - size, sizeof(p->segs) / sizeof(p->segs[0]), size, - 0, &p->tag) == ENOMEM) -#endif { goto free; } p->size = size; p->align = align; if (bus_dmamem_alloc(p->tag, &p->kaddr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT, &p->map)) goto tagfree; if (bus_dmamap_load(p->tag, p->map, p->kaddr, p->size, usbmem_callback, p, 0)) goto memfree; /* XXX - override the tag, ok since we never free it */ p->tag = tag; *dmap = p; return (USBD_NORMAL_COMPLETION); /* * XXX - do we need to _unload? is the order of _free and _destroy * correct? */ memfree: bus_dmamem_free(p->tag, p->kaddr, p->map); tagfree: bus_dma_tag_destroy(p->tag); free: free(p, M_USB); return (USBD_NOMEM); } /* * Do not free the memory unconditionally since we might be called * from an interrupt context and that is BAD. * XXX when should we really free? */ static void usb_block_freemem(usb_dma_block_t *p) { int s; DPRINTFN(6, ("usb_block_freemem: size=%lu\n", (u_long)p->size)); s = splusb(); LIST_INSERT_HEAD(&usb_blk_freelist, p, next); usb_blk_nfree++; splx(s); } usbd_status usb_allocmem(usbd_bus_handle bus, size_t size, size_t align, usb_dma_t *p) { bus_dma_tag_t tag = bus->parent_dmatag; usbd_status err; struct usb_frag_dma *f; usb_dma_block_t *b; int i; int s; /* compat w/ Net/OpenBSD */ if (align == 0) align = 1; /* If the request is large then just use a full block. */ if (size > USB_MEM_SMALL || align > USB_MEM_SMALL) { DPRINTFN(1, ("usb_allocmem: large alloc %d\n", (int)size)); size = (size + USB_MEM_BLOCK - 1) & ~(USB_MEM_BLOCK - 1); err = usb_block_allocmem(tag, size, align, &p->block); if (!err) { p->block->fullblock = 1; p->offs = 0; p->len = size; } return (err); } s = splusb(); /* Check for free fragments. */ for (f = LIST_FIRST(&usb_frag_freelist); f; f = LIST_NEXT(f, next)) if (f->block->tag == tag) break; if (f == NULL) { DPRINTFN(1, ("usb_allocmem: adding fragments\n")); err = usb_block_allocmem(tag, USB_MEM_BLOCK, USB_MEM_SMALL,&b); if (err) { splx(s); return (err); } b->fullblock = 0; /* XXX - override the tag, ok since we never free it */ b->tag = tag; KASSERT(sizeof *f <= USB_MEM_SMALL, ("USB_MEM_SMALL(%d) is too small for struct usb_frag_dma(%zd)\n", USB_MEM_SMALL, sizeof *f)); for (i = 0; i < USB_MEM_BLOCK; i += USB_MEM_SMALL) { f = (struct usb_frag_dma *)((char *)b->kaddr + i); f->block = b; f->offs = i; LIST_INSERT_HEAD(&usb_frag_freelist, f, next); } f = LIST_FIRST(&usb_frag_freelist); } p->block = f->block; p->offs = f->offs; p->len = USB_MEM_SMALL; LIST_REMOVE(f, next); splx(s); DPRINTFN(5, ("usb_allocmem: use frag=%p size=%d\n", f, (int)size)); return (USBD_NORMAL_COMPLETION); } void usb_freemem(usbd_bus_handle bus, usb_dma_t *p) { struct usb_frag_dma *f; int s; if (p->block->fullblock) { DPRINTFN(1, ("usb_freemem: large free\n")); usb_block_freemem(p->block); return; } f = KERNADDR(p, 0); f->block = p->block; f->offs = p->offs; s = splusb(); LIST_INSERT_HEAD(&usb_frag_freelist, f, next); splx(s); DPRINTFN(5, ("usb_freemem: frag=%p\n", f)); }