From owner-freebsd-usb@FreeBSD.ORG Sun Oct 31 22:43:09 2010 Return-Path: Delivered-To: freebsd-usb@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id B587A106564A; Sun, 31 Oct 2010 22:43:09 +0000 (UTC) (envelope-from weongyo.jeong@gmail.com) Received: from mail-vw0-f54.google.com (mail-vw0-f54.google.com [209.85.212.54]) by mx1.freebsd.org (Postfix) with ESMTP id 4C7398FC08; Sun, 31 Oct 2010 22:43:09 +0000 (UTC) Received: by vws12 with SMTP id 12so3002173vws.13 for ; Sun, 31 Oct 2010 15:43:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:received:from:date:to:cc :subject:message-id:reply-to:mime-version:content-type :content-disposition:user-agent:organization:x-operation-sytem; bh=p0SKQsPHZHPMDrD+yI/baAokqVVA0a43FmCNR+If4PI=; b=kN3NIDHZJwWlfM3kvGT69bt3+TX/SutFd56IylF92/2EucsYZ7wBfalsvadPrzRD8w u6BdOvViyfeLDjZmmFFpWCGv2yUmKqFwuRLGXmRsztpOBOamo0UOJv1Xtwq1S9ndRi/5 yuBa37dPc+ntc75zobM5uSSkQnX2adubtlWrE= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:date:to:cc:subject:message-id:reply-to:mime-version :content-type:content-disposition:user-agent:organization :x-operation-sytem; b=UYKff4VG91EleSqkhhQs2EzL2kEDgb/QiJ8/SUtzSqtmM21sZCrpGFdEqrRE3DmVKC jzYSURlu327SIL01D7gP+BxzAimp9CKx2pbb/c0HXb/MQmTb2DAbLyGZY6/nJljeySdh t9QCuIJatgZ3m4/sqlLQ7BpEBc1kTMzsHEBko= Received: by 10.229.224.82 with SMTP id in18mr498406qcb.262.1288564988705; Sun, 31 Oct 2010 15:43:08 -0700 (PDT) Received: from weongyo ([174.35.1.224]) by mx.google.com with ESMTPS id s28sm4386950qcp.21.2010.10.31.15.43.05 (version=SSLv3 cipher=RC4-MD5); Sun, 31 Oct 2010 15:43:07 -0700 (PDT) Received: by weongyo (sSMTP sendmail emulation); Sun, 31 Oct 2010 15:43:04 -0700 From: Weongyo Jeong Date: Sun, 31 Oct 2010 15:43:04 -0700 To: freebsd-usb@freebsd.org Message-ID: <20101031224304.GB3918@weongyo> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.4.2.3i Organization: CDNetworks. X-Operation-Sytem: FreeBSD Cc: Subject: [CFR 3/n] removes uther dependency of axe(4) X-BeenThere: freebsd-usb@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: Weongyo Jeong List-Id: FreeBSD support for USB List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 31 Oct 2010 22:43:09 -0000 Hello, As one of patch series it's for patching axe(4) without dependency of uether module. The change log would be almost same like the previous patch log. regards, Weongyo Jeong Index: if_axereg.h =================================================================== --- if_axereg.h (revision 214604) +++ if_axereg.h (working copy) @@ -185,7 +185,7 @@ struct axe_sframe_hdr { uint16_t ilen; } __packed; -#define GET_MII(sc) uether_getmii(&(sc)->sc_ue) +#define GET_MII(sc) (device_get_softc(sc->sc_miibus)) /* The interrupt endpoint is currently unused by the ASIX part. */ enum { @@ -195,20 +195,35 @@ enum { }; struct axe_softc { - struct usb_ether sc_ue; + struct ifnet *sc_ifp; + device_t sc_dev; + device_t sc_miibus; + struct usb_device *sc_udev; /* used by uether_do_request() */ struct mtx sc_mtx; - struct usb_xfer *sc_xfer[AXE_N_TRANSFER]; + struct sx sc_sx; + struct usb_xfer *sc_xfer[AXE_N_TRANSFER]; + + /* ethernet address from eeprom */ + uint8_t sc_eaddr[ETHER_ADDR_LEN]; + struct ifqueue sc_rxq; + + struct sleepout sc_sleepout; + struct sleepout_task sc_watchdog; + struct task sc_setmulti; + struct task sc_setpromisc; + + int sc_unit; int sc_phyno; - int sc_flags; #define AXE_FLAG_LINK 0x0001 #define AXE_FLAG_772 0x1000 /* AX88772 */ #define AXE_FLAG_178 0x2000 /* AX88178 */ - uint8_t sc_ipgs[3]; uint8_t sc_phyaddrs[2]; }; +#define AXE_SXLOCK(_sc) sx_xlock(&(_sc)->sc_sx) +#define AXE_SXUNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx) #define AXE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define AXE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define AXE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) Index: if_axe.c =================================================================== --- if_axe.c (revision 214604) +++ if_axe.c (working copy) @@ -89,6 +89,8 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include #include #include #include @@ -96,6 +98,19 @@ __FBSDID("$FreeBSD$"); #include #include +#include +#include +#include +#include +#include +#include +#include + +#include "miibus_if.h" + +#include +#include + #include #include #include @@ -104,6 +119,7 @@ __FBSDID("$FreeBSD$"); #define USB_DEBUG_VAR axe_debug #include #include +#include #include #include @@ -178,23 +194,26 @@ static miibus_readreg_t axe_miibus_readreg; static miibus_writereg_t axe_miibus_writereg; static miibus_statchg_t axe_miibus_statchg; -static uether_fn_t axe_attach_post; -static uether_fn_t axe_init; -static uether_fn_t axe_stop; -static uether_fn_t axe_start; -static uether_fn_t axe_tick; -static uether_fn_t axe_setmulti; -static uether_fn_t axe_setpromisc; - static int axe_ifmedia_upd(struct ifnet *); static void axe_ifmedia_sts(struct ifnet *, struct ifmediareq *); static int axe_cmd(struct axe_softc *, int, int, int, void *); static void axe_ax88178_init(struct axe_softc *); static void axe_ax88772_init(struct axe_softc *); static int axe_get_phyno(struct axe_softc *, int); +static void axe_watchdog(void *); +static void axe_init(void *); +static void axe_init_locked(struct axe_softc *); +static int axe_ioctl(struct ifnet *, u_long, caddr_t); +static void axe_start(struct ifnet *); +static void axe_start_locked(struct ifnet *); +static void axe_tick(struct axe_softc *); +static void axe_stop(struct axe_softc *); +static void axe_stop_locked(struct axe_softc *); +static void axe_setmulti_locked(struct axe_softc *); +static void axe_setpromisc(void *, int); +static void axe_setpromisc_locked(struct axe_softc *); static const struct usb_config axe_config[AXE_N_TRANSFER] = { - [AXE_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, @@ -250,18 +269,14 @@ MODULE_DEPEND(axe, ether, 1, 1, 1); MODULE_DEPEND(axe, miibus, 1, 1, 1); MODULE_VERSION(axe, 1); -static const struct usb_ether_methods axe_ue_methods = { - .ue_attach_post = axe_attach_post, - .ue_start = axe_start, - .ue_init = axe_init, - .ue_stop = axe_stop, - .ue_tick = axe_tick, - .ue_setmulti = axe_setmulti, - .ue_setpromisc = axe_setpromisc, - .ue_mii_upd = axe_ifmedia_upd, - .ue_mii_sts = axe_ifmedia_sts, -}; +static uint8_t +axe_pause(struct axe_softc *sc, unsigned int _ticks) +{ + usb_pause_mtx(&sc->sc_mtx, _ticks); + return (0); +} + static int axe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf) { @@ -278,7 +293,8 @@ axe_cmd(struct axe_softc *sc, int cmd, int index, USETW(req.wIndex, index); USETW(req.wLength, AXE_CMD_LEN(cmd)); - err = uether_do_request(&sc->sc_ue, &req, buf, 1000); + err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, buf, 0, + NULL, 1000); return (err); } @@ -354,7 +370,7 @@ axe_miibus_statchg(device_t dev) if (!locked) AXE_LOCK(sc); - ifp = uether_getifp(&sc->sc_ue); + ifp = sc->sc_ifp; if (mii == NULL || ifp == NULL || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) goto done; @@ -418,8 +434,7 @@ axe_ifmedia_upd(struct ifnet *ifp) struct mii_data *mii = GET_MII(sc); int error; - AXE_LOCK_ASSERT(sc, MA_OWNED); - + AXE_LOCK(sc); if (mii->mii_instance) { struct mii_softc *miisc; @@ -427,6 +442,7 @@ axe_ifmedia_upd(struct ifnet *ifp) mii_phy_reset(miisc); } error = mii_mediachg(mii); + AXE_UNLOCK(sc); return (error); } @@ -447,10 +463,19 @@ axe_ifmedia_sts(struct ifnet *ifp, struct ifmediar } static void -axe_setmulti(struct usb_ether *ue) +axe_setmulti(void *arg, int npending) { - struct axe_softc *sc = uether_getsc(ue); - struct ifnet *ifp = uether_getifp(ue); + struct axe_softc *sc = arg; + + AXE_LOCK(sc); + axe_setmulti_locked(sc); + AXE_UNLOCK(sc); +} + +static void +axe_setmulti_locked(struct axe_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; struct ifmultiaddr *ifma; uint32_t h = 0; uint16_t rxmode; @@ -509,17 +534,15 @@ axe_get_phyno(struct axe_softc *sc, int sel) #define AXE_GPIO_WRITE(x, y) do { \ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, (x), NULL); \ - uether_pause(ue, (y)); \ + axe_pause(sc, (y)); \ } while (0) static void axe_ax88178_init(struct axe_softc *sc) { - struct usb_ether *ue; int gpio0, phymode; uint16_t eeprom, val; - ue = &sc->sc_ue; axe_cmd(sc, AXE_CMD_SROM_WR_ENABLE, 0, 0, NULL); /* XXX magic */ axe_cmd(sc, AXE_CMD_SROM_READ, 0, 0x0017, &eeprom); @@ -536,7 +559,7 @@ axe_ax88178_init(struct axe_softc *sc) } if (bootverbose) - device_printf(sc->sc_ue.ue_dev, "EEPROM data : 0x%04x\n", + device_printf(sc->sc_dev, "EEPROM data : 0x%04x\n", eeprom); /* Program GPIOs depending on PHY hardware. */ switch (phymode) { @@ -580,15 +603,15 @@ axe_ax88178_init(struct axe_softc *sc) AXE_GPIO_WRITE(val | AXE_GPIO2_EN, hz / 4); AXE_GPIO_WRITE(val | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); if (phymode == AXE_PHY_MODE_REALTEK_8211CL) { - axe_miibus_writereg(ue->ue_dev, sc->sc_phyno, + axe_miibus_writereg(sc->sc_dev, sc->sc_phyno, 0x1F, 0x0005); - axe_miibus_writereg(ue->ue_dev, sc->sc_phyno, + axe_miibus_writereg(sc->sc_dev, sc->sc_phyno, 0x0C, 0x0000); - val = axe_miibus_readreg(ue->ue_dev, sc->sc_phyno, + val = axe_miibus_readreg(sc->sc_dev, sc->sc_phyno, 0x0001); - axe_miibus_writereg(ue->ue_dev, sc->sc_phyno, + axe_miibus_writereg(sc->sc_dev, sc->sc_phyno, 0x01, val | 0x0080); - axe_miibus_writereg(ue->ue_dev, sc->sc_phyno, + axe_miibus_writereg(sc->sc_dev, sc->sc_phyno, 0x1F, 0x0000); } break; @@ -599,14 +622,14 @@ axe_ax88178_init(struct axe_softc *sc) /* soft reset */ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); - uether_pause(ue, hz / 4); + axe_pause(sc, hz / 4); axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL); - uether_pause(ue, hz / 4); + axe_pause(sc, hz / 4); /* Enable MII/GMII/RGMII interface to work with external PHY. */ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0, NULL); - uether_pause(ue, hz / 4); + axe_pause(sc, hz / 4); axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); } @@ -615,23 +638,24 @@ axe_ax88178_init(struct axe_softc *sc) static void axe_ax88772_init(struct axe_softc *sc) { + axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL); - uether_pause(&sc->sc_ue, hz / 16); + axe_pause(sc, hz / 16); if (sc->sc_phyno == AXE_772_PHY_NO_EPHY) { /* ask for the embedded PHY */ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x01, NULL); - uether_pause(&sc->sc_ue, hz / 64); + axe_pause(sc, hz / 64); /* power down and reset state, pin reset state */ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); - uether_pause(&sc->sc_ue, hz / 16); + axe_pause(sc, hz / 16); /* power down/reset state, pin operating state */ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); - uether_pause(&sc->sc_ue, hz / 4); + axe_pause(sc, hz / 4); /* power up, reset */ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL, NULL); @@ -642,14 +666,14 @@ axe_ax88772_init(struct axe_softc *sc) } else { /* ask for external PHY */ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x00, NULL); - uether_pause(&sc->sc_ue, hz / 64); + axe_pause(sc, hz / 64); /* power down internal PHY */ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); } - uether_pause(&sc->sc_ue, hz / 4); + axe_pause(sc, hz / 4); axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); } @@ -659,34 +683,33 @@ axe_reset(struct axe_softc *sc) struct usb_config_descriptor *cd; usb_error_t err; - cd = usbd_get_config_descriptor(sc->sc_ue.ue_udev); + cd = usbd_get_config_descriptor(sc->sc_udev); - err = usbd_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx, + err = usbd_req_set_config(sc->sc_udev, &sc->sc_mtx, cd->bConfigurationValue); if (err) DPRINTF("reset failed (ignored)\n"); /* Wait a little while for the chip to get its brains in order. */ - uether_pause(&sc->sc_ue, hz / 100); + axe_pause(sc, hz / 100); } static void -axe_attach_post(struct usb_ether *ue) +axe_attach_post(struct axe_softc *sc) { - struct axe_softc *sc = uether_getsc(ue); /* * Load PHY indexes first. Needed by axe_xxx_init(). */ axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, sc->sc_phyaddrs); if (bootverbose) - device_printf(sc->sc_ue.ue_dev, "PHYADDR 0x%02x:0x%02x\n", + device_printf(sc->sc_dev, "PHYADDR 0x%02x:0x%02x\n", sc->sc_phyaddrs[0], sc->sc_phyaddrs[1]); sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_PRI); if (sc->sc_phyno == -1) sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_SEC); if (sc->sc_phyno == -1) { - device_printf(sc->sc_ue.ue_dev, + device_printf(sc->sc_dev, "no valid PHY address found, assuming PHY address 0\n"); sc->sc_phyno = 0; } @@ -700,9 +723,9 @@ static void * Get station address. */ if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) - axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, ue->ue_eaddr); + axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, sc->sc_eaddr); else - axe_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, ue->ue_eaddr); + axe_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, sc->sc_eaddr); /* * Fetch IPG values. @@ -728,6 +751,19 @@ axe_probe(device_t dev) return (usbd_lookup_id_by_uaa(axe_devs, sizeof(axe_devs), uaa)); } +static void +axe_watchdog(void *arg) +{ + struct axe_softc *sc = arg; + struct ifnet *ifp = sc->sc_ifp; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return; + + axe_tick(sc); + sleepout_reset(&sc->sc_watchdog, hz, axe_watchdog, sc); +} + /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. @@ -737,15 +773,23 @@ axe_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct axe_softc *sc = device_get_softc(dev); - struct usb_ether *ue = &sc->sc_ue; + struct ifnet *ifp; uint8_t iface_index; int error; sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + sc->sc_dev = dev; + sc->sc_udev = uaa->device; + sc->sc_unit = uether_alloc_unr(); device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + sx_init(&sc->sc_sx, "axe sxlock"); + sleepout_create(&sc->sc_sleepout, "axe sleepout"); + sleepout_init_mtx(&sc->sc_sleepout, &sc->sc_watchdog, &sc->sc_mtx, 0); + TASK_INIT(&sc->sc_setmulti, 0, axe_setmulti, sc); + TASK_INIT(&sc->sc_setpromisc, 0, axe_setpromisc, sc); iface_index = AXE_IFACE_IDX; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, @@ -755,19 +799,39 @@ axe_attach(device_t dev) goto detach; } - ue->ue_sc = sc; - ue->ue_dev = dev; - ue->ue_udev = uaa->device; - ue->ue_mtx = &sc->sc_mtx; - ue->ue_methods = &axe_ue_methods; + AXE_LOCK(sc); + axe_attach_post(sc); + AXE_UNLOCK(sc); - error = uether_ifattach(ue); - if (error) { - device_printf(dev, "could not attach interface\n"); + sc->sc_ifp = ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(sc->sc_dev, "could not allocate ifnet\n"); goto detach; } - return (0); /* success */ + ifp->if_softc = sc; + if_initname(ifp, "ue", sc->sc_unit); + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = axe_ioctl; + ifp->if_start = axe_start; + ifp->if_init = axe_init; + IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); + ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; + IFQ_SET_READY(&ifp->if_snd); + + error = mii_attach(sc->sc_dev, &sc->sc_miibus, ifp, + axe_ifmedia_upd, axe_ifmedia_sts, + BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0); + if (error) { + device_printf(sc->sc_dev, "MII without any PHY\n"); + goto error2; + } + + if_printf(ifp, " on %s\n", + device_get_nameunit(sc->sc_dev)); + ether_ifattach(ifp, sc->sc_eaddr); + return (0); detach: axe_detach(dev); return (ENXIO); /* failure */ @@ -777,15 +841,96 @@ static int axe_detach(device_t dev) { struct axe_softc *sc = device_get_softc(dev); - struct usb_ether *ue = &sc->sc_ue; + struct ifnet *ifp = sc->sc_ifp; + sleepout_drain(&sc->sc_watchdog); + SLEEPOUT_DRAINTASK(&sc->sc_sleepout, &sc->sc_setpromisc); + SLEEPOUT_DRAINTASK(&sc->sc_sleepout, &sc->sc_setmulti); usbd_transfer_unsetup(sc->sc_xfer, AXE_N_TRANSFER); - uether_ifdetach(ue); + + if (sc->sc_miibus != NULL) + device_delete_child(sc->sc_dev, sc->sc_miibus); + if (ifp != NULL) { + AXE_LOCK(sc); + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + AXE_UNLOCK(sc); + ether_ifdetach(ifp); + if_free(ifp); + } + sleepout_free(&sc->sc_sleepout); + sx_destroy(&sc->sc_sx); mtx_destroy(&sc->sc_mtx); + uether_free_unr(sc->sc_unit); return (0); } +static void +axe_rxflush(struct axe_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + + AXE_LOCK_ASSERT(sc, MA_OWNED); + + for (;;) { + _IF_DEQUEUE(&sc->sc_rxq, m); + if (m == NULL) + break; + + /* + * The USB xfer has been resubmitted so its safe to unlock now. + */ + AXE_UNLOCK(sc); + ifp->if_input(ifp, m); + AXE_LOCK(sc); + } +} + +static struct mbuf * +axe_newbuf(void) +{ + struct mbuf *m_new; + + m_new = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (m_new == NULL) + return (NULL); + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + + m_adj(m_new, ETHER_ALIGN); + return (m_new); +} + +static int +axe_rxbuf(struct axe_softc *sc, struct usb_page_cache *pc, + unsigned int offset, unsigned int len) +{ + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + + AXE_LOCK_ASSERT(sc, MA_OWNED); + + if (len < ETHER_HDR_LEN || len > MCLBYTES - ETHER_ALIGN) + return (1); + + m = axe_newbuf(); + if (m == NULL) { + ifp->if_ierrors++; + return (ENOMEM); + } + + usbd_copy_out(pc, offset, mtod(m, uint8_t *), len); + + /* finalize mbuf */ + ifp->if_ipackets++; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = len; + + /* enqueue for later when the lock can be released */ + _IF_ENQUEUE(&sc->sc_rxq, m); + return (0); +} + #if (AXE_BULK_BUF_SIZE >= 0x10000) #error "Please update axe_bulk_read_callback()!" #endif @@ -794,8 +939,7 @@ static void axe_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct axe_softc *sc = usbd_xfer_softc(xfer); - struct usb_ether *ue = &sc->sc_ue; - struct ifnet *ifp = uether_getifp(ue); + struct ifnet *ifp = sc->sc_ifp; struct axe_sframe_hdr hdr; struct usb_page_cache *pc; int err, pos, len; @@ -832,24 +976,22 @@ axe_bulk_read_callback(struct usb_xfer *xfer, usb_ err = EINVAL; break; } - uether_rxbuf(ue, pc, pos, len); + err = axe_rxbuf(sc, pc, pos, len); pos += len + (len % 2); } } else - uether_rxbuf(ue, pc, 0, actlen); + err = axe_rxbuf(sc, pc, 0, actlen); if (err != 0) ifp->if_ierrors++; - /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); - uether_rxflush(ue); + axe_rxflush(sc); return; - default: /* Error */ DPRINTF("bulk read error, %s\n", usbd_errstr(error)); @@ -859,7 +1001,6 @@ tr_setup: goto tr_setup; } return; - } } @@ -872,7 +1013,7 @@ axe_bulk_write_callback(struct usb_xfer *xfer, usb { struct axe_softc *sc = usbd_xfer_softc(xfer); struct axe_sframe_hdr hdr; - struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct ifnet *ifp = sc->sc_ifp; struct usb_page_cache *pc; struct mbuf *m; int pos; @@ -896,7 +1037,6 @@ tr_setup: pc = usbd_xfer_get_frame(xfer, 0); while (1) { - IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) { @@ -904,11 +1044,9 @@ tr_setup: break; /* send out data */ return; } - if (m->m_pkthdr.len > MCLBYTES) { + if (m->m_pkthdr.len > MCLBYTES) m->m_pkthdr.len = MCLBYTES; - } if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) { - hdr.len = htole16(m->m_pkthdr.len); hdr.ilen = ~hdr.len; @@ -961,7 +1099,6 @@ tr_setup: usbd_transfer_submit(xfer); ifp->if_drv_flags |= IFF_DRV_OACTIVE; return; - default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usbd_errstr(error)); @@ -975,31 +1112,41 @@ tr_setup: goto tr_setup; } return; - } } static void -axe_tick(struct usb_ether *ue) +axe_tick(struct axe_softc *sc) { - struct axe_softc *sc = uether_getsc(ue); struct mii_data *mii = GET_MII(sc); AXE_LOCK_ASSERT(sc, MA_OWNED); mii_tick(mii); if ((sc->sc_flags & AXE_FLAG_LINK) == 0) { - axe_miibus_statchg(ue->ue_dev); + axe_miibus_statchg(sc->sc_dev); if ((sc->sc_flags & AXE_FLAG_LINK) != 0) - axe_start(ue); + axe_start_locked(sc->sc_ifp); } } static void -axe_start(struct usb_ether *ue) +axe_start(struct ifnet *ifp) { - struct axe_softc *sc = uether_getsc(ue); + struct axe_softc *sc = ifp->if_softc; + AXE_LOCK(sc); + axe_start_locked(ifp); + AXE_UNLOCK(sc); +} + +static void +axe_start_locked(struct ifnet *ifp) +{ + struct axe_softc *sc = ifp->if_softc; + + AXE_LOCK_ASSERT(sc, MA_OWNED); + /* * start the USB transfers, if not already started: */ @@ -1008,16 +1155,27 @@ static void } static void -axe_init(struct usb_ether *ue) +axe_init(void *arg) { - struct axe_softc *sc = uether_getsc(ue); - struct ifnet *ifp = uether_getifp(ue); + struct axe_softc *sc = arg; + + AXE_SXLOCK(sc); + AXE_LOCK(sc); + axe_init_locked(sc); + AXE_UNLOCK(sc); + AXE_SXUNLOCK(sc); +} + +static void +axe_init_locked(struct axe_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; uint16_t rxmode; AXE_LOCK_ASSERT(sc, MA_OWNED); /* Cancel pending I/O */ - axe_stop(ue); + axe_stop_locked(sc); /* Set MAC address. */ if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) @@ -1047,9 +1205,8 @@ static void */ rxmode |= AXE_178_RXCMD_MFB_16384; #endif - } else { + } else rxmode |= AXE_172_RXCMD_UNICAST; - } /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) @@ -1061,44 +1218,65 @@ static void axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); /* Load the multicast filter. */ - axe_setmulti(ue); + axe_setmulti_locked(sc); usbd_xfer_set_stall(sc->sc_xfer[AXE_BULK_DT_WR]); ifp->if_drv_flags |= IFF_DRV_RUNNING; - axe_start(ue); + sleepout_reset(&sc->sc_watchdog, hz, axe_watchdog, sc); + axe_start_locked(sc->sc_ifp); } static void -axe_setpromisc(struct usb_ether *ue) +axe_setpromisc(void *arg, int npending) { - struct axe_softc *sc = uether_getsc(ue); - struct ifnet *ifp = uether_getifp(ue); + struct axe_softc *sc = arg; + + AXE_LOCK(sc); + axe_setpromisc_locked(sc); + AXE_UNLOCK(sc); +} + +static void +axe_setpromisc_locked(struct axe_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; uint16_t rxmode; axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode); rxmode = le16toh(rxmode); - if (ifp->if_flags & IFF_PROMISC) { + if (ifp->if_flags & IFF_PROMISC) rxmode |= AXE_RXCMD_PROMISC; - } else { + else rxmode &= ~AXE_RXCMD_PROMISC; - } axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); - axe_setmulti(ue); + axe_setmulti_locked(sc); } static void -axe_stop(struct usb_ether *ue) +axe_stop(struct axe_softc *sc) { - struct axe_softc *sc = uether_getsc(ue); - struct ifnet *ifp = uether_getifp(ue); + AXE_SXLOCK(sc); + AXE_LOCK(sc); + axe_stop_locked(sc); + AXE_UNLOCK(sc); + AXE_SXUNLOCK(sc); +} + +static void +axe_stop_locked(struct axe_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + AXE_LOCK_ASSERT(sc, MA_OWNED); + sleepout_stop(&sc->sc_watchdog); + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); sc->sc_flags &= ~AXE_FLAG_LINK; @@ -1110,3 +1288,46 @@ static void axe_reset(sc); } + +static int +axe_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct axe_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + struct mii_data *mii = GET_MII(sc); + int error = 0, drv_flags, flags; + + switch (command) { + case SIOCSIFFLAGS: + /* Avoids race and LOR between mutex and sx lock. */ + AXE_LOCK(sc); + flags = ifp->if_flags; + drv_flags = ifp->if_drv_flags; + AXE_UNLOCK(sc); + /* device up and down is synchronous using sx(9) lock */ + if (flags & IFF_UP) { + if (drv_flags & IFF_DRV_RUNNING) + SLEEPOUT_RUNTASK(&sc->sc_sleepout, + &sc->sc_setpromisc); + else + axe_init(sc); + } else + axe_stop(sc); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + /* To avoid LOR by in_multi_mtx (netinet/in_mcast.c) */ + if (ifp->if_flags & IFF_UP && + ifp->if_drv_flags & IFF_DRV_RUNNING) + SLEEPOUT_RUNTASK(&sc->sc_sleepout, &sc->sc_setmulti); + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + break; + default: + error = ether_ioctl(ifp, command, data); + break; + } + return (error); +}