예제 #1
0
/* set ISOC configuration */
int
ubt_set_isoc_config(struct ubt_softc *sc)
{
	usb_endpoint_descriptor_t *ed;
	int rd_addr, wr_addr, rd_size, wr_size;
	uint8_t count, i;
	int err;

	err = usbd_set_interface(sc->sc_iface1, sc->sc_config);
	if (err != USBD_NORMAL_COMPLETION) {
		kprintf(
		    "%s: Could not set config %d on ISOC interface. %s (%d)\n",
		    device_get_nameunit(sc->sc_dev), sc->sc_config, usbd_errstr(err), err);

		return err == USBD_IN_USE ? EBUSY : EIO;
	}

	/*
	 * We wont get past the above if there are any pipes open, so no
	 * need to worry about buf/xfer/pipe deallocation. If we get an
	 * error after this, the frame quantities will be 0 and no SCO
	 * data will be possible.
	 */

	sc->sc_scord_size = rd_size = 0;
	sc->sc_scord_addr = rd_addr = -1;

	sc->sc_scowr_size = wr_size = 0;
	sc->sc_scowr_addr = wr_addr = -1;

	count = 0;
	(void)usbd_endpoint_count(sc->sc_iface1, &count);

	for (i = 0 ; i < count ; i++) {
		ed = usbd_interface2endpoint_descriptor(sc->sc_iface1, i);
		if (ed == NULL) {
			kprintf("%s: could not read endpoint descriptor %d\n",
			    device_get_nameunit(sc->sc_dev), i);

			return EIO;
		}

		DPRINTFN(5, "%s: endpoint type %02x (%02x) addr %02x (%s)\n",
			device_get_nameunit(sc->sc_dev),
			UE_GET_XFERTYPE(ed->bmAttributes),
			UE_GET_ISO_TYPE(ed->bmAttributes),
			ed->bEndpointAddress,
			UE_GET_DIR(ed->bEndpointAddress) ? "in" : "out");

		if (UE_GET_XFERTYPE(ed->bmAttributes) != UE_ISOCHRONOUS)
			continue;

		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) {
			rd_addr = ed->bEndpointAddress;
			rd_size = UGETW(ed->wMaxPacketSize);
		} else {
			wr_addr = ed->bEndpointAddress;
			wr_size = UGETW(ed->wMaxPacketSize);
		}
	}

	if (rd_addr == -1) {
		kprintf(
		    "%s: missing ISOC IN endpoint on interface config %d\n",
		    device_get_nameunit(sc->sc_dev), sc->sc_config);

		return ENOENT;
	}
	if (wr_addr == -1) {
		kprintf(
		    "%s: missing ISOC OUT endpoint on interface config %d\n",
		    device_get_nameunit(sc->sc_dev), sc->sc_config);

		return ENOENT;
	}

#ifdef DIAGNOSTIC
	if (rd_size > MLEN) {
		kprintf("%s: rd_size=%d exceeds MLEN\n",
		    device_get_nameunit(sc->sc_dev), rd_size);

		return EOVERFLOW;
	}

	if (wr_size > MLEN) {
		kprintf("%s: wr_size=%d exceeds MLEN\n",
		    device_get_nameunit(sc->sc_dev), wr_size);

		return EOVERFLOW;
	}
#endif

	sc->sc_scord_size = rd_size;
	sc->sc_scord_addr = rd_addr;

	sc->sc_scowr_size = wr_size;
	sc->sc_scowr_addr = wr_addr;

	return 0;
}
/*------------------------------------------------------------------------*
 *	usbd_req_reset_port
 *
 * This function will instruct an USB HUB to perform a reset sequence
 * on the specified port number.
 *
 * Returns:
 *    0: Success. The USB device should now be at address zero.
 * Else: Failure. No USB device is present and the USB port should be
 *       disabled.
 *------------------------------------------------------------------------*/
usb_error_t
usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
{
	struct usb_port_status ps;
	usb_error_t err;
	uint16_t n;

#if USB_DEBUG
	uint16_t pr_poll_delay;
	uint16_t pr_recovery_delay;

#endif
	err = usbd_req_set_port_feature(udev, mtx, port, UHF_PORT_RESET);
	if (err) {
		goto done;
	}
#if USB_DEBUG
	/* range check input parameters */
	pr_poll_delay = usb_pr_poll_delay;
	if (pr_poll_delay < 1) {
		pr_poll_delay = 1;
	} else if (pr_poll_delay > 1000) {
		pr_poll_delay = 1000;
	}
	pr_recovery_delay = usb_pr_recovery_delay;
	if (pr_recovery_delay > 1000) {
		pr_recovery_delay = 1000;
	}
#endif
	n = 0;
	while (1) {
#if USB_DEBUG
		/* wait for the device to recover from reset */
		usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_poll_delay));
		n += pr_poll_delay;
#else
		/* wait for the device to recover from reset */
		usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_DELAY));
		n += USB_PORT_RESET_DELAY;
#endif
		err = usbd_req_get_port_status(udev, mtx, &ps, port);
		if (err) {
			goto done;
		}
		/* if the device disappeared, just give up */
		if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) {
			goto done;
		}
		/* check if reset is complete */
		if (UGETW(ps.wPortChange) & UPS_C_PORT_RESET) {
			break;
		}
		/* check for timeout */
		if (n > 1000) {
			n = 0;
			break;
		}
	}

	/* clear port reset first */
	err = usbd_req_clear_port_feature(
	    udev, mtx, port, UHF_C_PORT_RESET);
	if (err) {
		goto done;
	}
	/* check for timeout */
	if (n == 0) {
		err = USB_ERR_TIMEOUT;
		goto done;
	}
#if USB_DEBUG
	/* wait for the device to recover from reset */
	usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_recovery_delay));
#else
	/* wait for the device to recover from reset */
	usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_RECOVERY));
#endif

done:
	DPRINTFN(2, "port %d reset returning error=%s\n",
	    port, usbd_errstr(err));
	return (err);
}
예제 #3
0
/*
 * A frame has been uploaded: pass the resulting mbuf chain up to
 * the higher level protocols.
 */
void
ugl_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
{
	struct ugl_chain	*c = priv;
	struct ugl_softc	*sc = c->ugl_sc;
	struct ifnet		*ifp = GET_IFP(sc);
	struct mbuf		*m;
	int			total_len = 0;
	unsigned int		packet_len, packet_count;
	int			s;

	if (usbd_is_dying(sc->sc_udev))
		return;

	if (!(ifp->if_flags & IFF_RUNNING))
		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",
			    sc->sc_dev.dv_xname, sc->sc_rx_errs,
			    usbd_errstr(status));
			sc->sc_rx_errs = 0;
		}
		if (status == USBD_STALLED)
			usbd_clear_endpoint_stall_async(sc->sc_ep[UGL_ENDPT_RX]);
		goto done;
	}

	usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);

	DPRINTFN(9,("%s: %s: enter status=%d length=%d\n",
		    sc->sc_dev.dv_xname, __func__, status, total_len));

	if (total_len < offsetof(struct ugl_packet, pkt_data)) {
		printf("%s: bad header (length=%d)\n",
		    sc->sc_dev.dv_xname, total_len);

		goto done;
	}

	packet_count = UGETDW(c->ugl_buf->pkt_count);
	if (packet_count != UGL_RX_FRAMES) {
		printf("%s: bad packet count (%d)\n",
		    sc->sc_dev.dv_xname, packet_count);

		if (packet_count == 0)
			goto done;
	}

	packet_len = UGETDW(c->ugl_buf->pkt_length);
	if (total_len < packet_len) {
		printf("%s: bad packet size(%d), length=%d\n",
		    sc->sc_dev.dv_xname, packet_len, total_len);

		if (packet_len == 0)
			goto done;
	}

	m = c->ugl_mbuf;
	memcpy(mtod(c->ugl_mbuf, char *), c->ugl_buf->pkt_data, packet_len);

	ifp->if_ipackets++;
	m->m_pkthdr.len = m->m_len = packet_len;

	m->m_pkthdr.rcvif = ifp;

	s = splnet();

	/* XXX ugly */
	if (ugl_newbuf(sc, c, NULL) == ENOBUFS) {
		ifp->if_ierrors++;
		goto done1;
	}

#if NBPFILTER > 0
	/*
	 * Handle BPF listeners. Let the BPF user see the packet, but
	 * don't pass it up to the ether_input() layer unless it's
	 * a broadcast packet, multicast packet, matches our ethernet
	 * address or the interface is in promiscuous mode.
	 */
	if (ifp->if_bpf) {
		bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
	}
#endif

	DPRINTFN(10,("%s: %s: deliver %d\n", sc->sc_dev.dv_xname,
		    __func__, m->m_len));

	ether_input_mbuf(ifp, m);

 done1:
	splx(s);

 done:
	/* Setup new transfer. */
	usbd_setup_xfer(c->ugl_xfer, sc->sc_ep[UGL_ENDPT_RX],
	    c, c->ugl_buf, UGL_BUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
	    USBD_NO_TIMEOUT, ugl_rxeof);
	usbd_transfer(c->ugl_xfer);

	DPRINTFN(10,("%s: %s: start rx\n", sc->sc_dev.dv_xname,
		    __func__));
}
예제 #4
0
int
ucomopen(dev_t dev, int flag, int mode, struct lwp *l)
{
	int unit = UCOMUNIT(dev);
	usbd_status err;
	struct ucom_softc *sc = device_lookup_private(&ucom_cd, unit);
	struct ucom_buffer *ub;
	struct tty *tp;
	int s, i;
	int error;

	if (sc == NULL)
		return (ENXIO);

	if (sc->sc_dying)
		return (EIO);

	if (!device_is_active(sc->sc_dev))
		return (ENXIO);

	tp = sc->sc_tty;

	DPRINTF(("ucomopen: unit=%d, tp=%p\n", unit, tp));

	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
		return (EBUSY);

	s = spltty();

	/*
	 * Do the following iff this is a first open.
	 */
	while (sc->sc_opening)
		tsleep(&sc->sc_opening, PRIBIO, "ucomop", 0);

	if (sc->sc_dying) {
		splx(s);
		return (EIO);
	}
	sc->sc_opening = 1;

	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
		struct termios t;

		tp->t_dev = dev;

		if (sc->sc_methods->ucom_open != NULL) {
			error = sc->sc_methods->ucom_open(sc->sc_parent,
							  sc->sc_portno);
			if (error) {
				ucom_cleanup(sc);
				sc->sc_opening = 0;
				wakeup(&sc->sc_opening);
				splx(s);
				return (error);
			}
		}

		ucom_status_change(sc);

		/* Clear PPS capture state on first open. */
		mutex_spin_enter(&timecounter_lock);
		memset(&sc->sc_pps_state, 0, sizeof(sc->sc_pps_state));
		sc->sc_pps_state.ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR;
		pps_init(&sc->sc_pps_state);
		mutex_spin_exit(&timecounter_lock);

		/*
		 * Initialize the termios status to the defaults.  Add in the
		 * sticky bits from TIOCSFLAGS.
		 */
		t.c_ispeed = 0;
		t.c_ospeed = TTYDEF_SPEED;
		t.c_cflag = TTYDEF_CFLAG;
		if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL))
			SET(t.c_cflag, CLOCAL);
		if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS))
			SET(t.c_cflag, CRTSCTS);
		if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF))
			SET(t.c_cflag, MDMBUF);
		/* Make sure ucomparam() will do something. */
		tp->t_ospeed = 0;
		(void) ucomparam(tp, &t);
		tp->t_iflag = TTYDEF_IFLAG;
		tp->t_oflag = TTYDEF_OFLAG;
		tp->t_lflag = TTYDEF_LFLAG;
		ttychars(tp);
		ttsetwater(tp);

		/*
		 * Turn on DTR.  We must always do this, even if carrier is not
		 * present, because otherwise we'd have to use TIOCSDTR
		 * immediately after setting CLOCAL, which applications do not
		 * expect.  We always assert DTR while the device is open
		 * unless explicitly requested to deassert it.  Ditto RTS.
		 */
		ucom_dtr(sc, 1);
		ucom_rts(sc, 1);

		DPRINTF(("ucomopen: open pipes in=%d out=%d\n",
			 sc->sc_bulkin_no, sc->sc_bulkout_no));

		/* Open the bulk pipes */
		err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no,
				     USBD_EXCLUSIVE_USE, &sc->sc_bulkin_pipe);
		if (err) {
			DPRINTF(("%s: open bulk in error (addr %d), err=%s\n",
				 device_xname(sc->sc_dev), sc->sc_bulkin_no,
				 usbd_errstr(err)));
			error = EIO;
			goto fail_0;
		}
		err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no,
				     USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
		if (err) {
			DPRINTF(("%s: open bulk out error (addr %d), err=%s\n",
				 device_xname(sc->sc_dev), sc->sc_bulkout_no,
				 usbd_errstr(err)));
			error = EIO;
			goto fail_1;
		}

		sc->sc_rx_unblock = 0;
		sc->sc_rx_stopped = 0;
		sc->sc_tx_stopped = 0;

		memset(sc->sc_ibuff, 0, sizeof(sc->sc_ibuff));
		memset(sc->sc_obuff, 0, sizeof(sc->sc_obuff));

		SIMPLEQ_INIT(&sc->sc_ibuff_empty);
		SIMPLEQ_INIT(&sc->sc_ibuff_full);
		SIMPLEQ_INIT(&sc->sc_obuff_free);
		SIMPLEQ_INIT(&sc->sc_obuff_full);

		/* Allocate input buffers */
		for (ub = &sc->sc_ibuff[0]; ub != &sc->sc_ibuff[UCOM_IN_BUFFS];
		    ub++) {
			ub->ub_xfer = usbd_alloc_xfer(sc->sc_udev);
			if (ub->ub_xfer == NULL) {
				error = ENOMEM;
				goto fail_2;
			}
			ub->ub_data = usbd_alloc_buffer(ub->ub_xfer,
			    sc->sc_ibufsizepad);
			if (ub->ub_data == NULL) {
				error = ENOMEM;
				goto fail_2;
			}

			if (ucomsubmitread(sc, ub) != USBD_NORMAL_COMPLETION) {
				error = EIO;
				goto fail_2;
			}
		}

		for (ub = &sc->sc_obuff[0]; ub != &sc->sc_obuff[UCOM_OUT_BUFFS];
		    ub++) {
			ub->ub_xfer = usbd_alloc_xfer(sc->sc_udev);
			if (ub->ub_xfer == NULL) {
				error = ENOMEM;
				goto fail_2;
			}
			ub->ub_data = usbd_alloc_buffer(ub->ub_xfer,
			    sc->sc_obufsize);
			if (ub->ub_data == NULL) {
				error = ENOMEM;
				goto fail_2;
			}

			SIMPLEQ_INSERT_TAIL(&sc->sc_obuff_free, ub, ub_link);
		}

	}
	sc->sc_opening = 0;
	wakeup(&sc->sc_opening);
	splx(s);

	error = ttyopen(tp, UCOMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
	if (error)
		goto bad;

	error = (*tp->t_linesw->l_open)(dev, tp);
	if (error)
		goto bad;

	return (0);

fail_2:
	usbd_abort_pipe(sc->sc_bulkin_pipe);
	for (i = 0; i < UCOM_IN_BUFFS; i++) {
		if (sc->sc_ibuff[i].ub_xfer != NULL) {
			usbd_free_xfer(sc->sc_ibuff[i].ub_xfer);
			sc->sc_ibuff[i].ub_xfer = NULL;
			sc->sc_ibuff[i].ub_data = NULL;
		}
	}
	usbd_abort_pipe(sc->sc_bulkout_pipe);
	for (i = 0; i < UCOM_OUT_BUFFS; i++) {
		if (sc->sc_obuff[i].ub_xfer != NULL) {
			usbd_free_xfer(sc->sc_obuff[i].ub_xfer);
			sc->sc_obuff[i].ub_xfer = NULL;
			sc->sc_obuff[i].ub_data = NULL;
		}
	}

	usbd_close_pipe(sc->sc_bulkout_pipe);
	sc->sc_bulkout_pipe = NULL;
fail_1:
	usbd_close_pipe(sc->sc_bulkin_pipe);
	sc->sc_bulkin_pipe = NULL;
fail_0:
	sc->sc_opening = 0;
	wakeup(&sc->sc_opening);
	splx(s);
	return (error);

bad:
	s = spltty();
	CLR(tp->t_state, TS_BUSY);
	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
		/*
		 * We failed to open the device, and nobody else had it opened.
		 * Clean up the state as appropriate.
		 */
		ucom_cleanup(sc);
	}
	splx(s);

	return (error);
}
예제 #5
0
static void
ubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
{
	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
	struct mbuf		*m;
	ng_hci_acldata_pkt_t	*hdr;
	struct usb_page_cache	*pc;
	int len;
	int actlen;

	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);

	m = NULL;

	switch (USB_GET_STATE(xfer)) {
	case USB_ST_TRANSFERRED:
		/* Allocate new mbuf */
		MGETHDR(m, M_NOWAIT, MT_DATA);
		if (m == NULL) {
			UBT_STAT_IERROR(sc);
			goto submit_next;
		}

		MCLGET(m, M_NOWAIT);
		if (!(m->m_flags & M_EXT)) {
			UBT_STAT_IERROR(sc);
			goto submit_next;
		}

		/* Add HCI packet type */
		*mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
		m->m_pkthdr.len = m->m_len = 1;

		if (actlen > MCLBYTES - 1)
			actlen = MCLBYTES - 1;

		pc = usbd_xfer_get_frame(xfer, 0);
		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
		m->m_pkthdr.len += actlen;
		m->m_len += actlen;

		UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
			actlen);

		/* Validate packet and send it up the stack */
		if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
			UBT_INFO(sc, "HCI ACL packet is too short\n");

			UBT_STAT_IERROR(sc);
			goto submit_next;
		}

		hdr = mtod(m, ng_hci_acldata_pkt_t *);
		len = le16toh(hdr->length);
		if (len != (int)(m->m_pkthdr.len - sizeof(*hdr))) {
			UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
				"pktlen=%d\n", len, m->m_pkthdr.len);

			UBT_STAT_IERROR(sc);
			goto submit_next;
		}

		UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
			"length=%d\n", m->m_pkthdr.len, len);

		UBT_STAT_PCKTS_RECV(sc);
		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);

		ubt_fwd_mbuf_up(sc, &m);
		/* m == NULL at this point */
		/* FALLTHOUGH */

	case USB_ST_SETUP:
submit_next:
		NG_FREE_M(m); /* checks for m != NULL */

		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
		usbd_transfer_submit(xfer);
		break;

	default: /* Error */
		if (error != USB_ERR_CANCELLED) {
			UBT_WARN(sc, "bulk-in transfer failed: %s\n",
				usbd_errstr(error));

			/* Try to clear stall first */
			usbd_xfer_set_stall(xfer);
			goto submit_next;
		}
			/* transfer cancelled */
		break;
	}
} /* ubt_bulk_read_callback */
예제 #6
0
void
uhub_attach(struct device *parent, struct device *self, void *aux)
{
    struct uhub_softc *sc = (struct uhub_softc *)self;
    struct usb_attach_arg *uaa = aux;
    usbd_device_handle dev = uaa->device;
    usbd_status err;
    struct usbd_hub *hub = NULL;
    usb_device_request_t req;
    usb_hub_descriptor_t hubdesc;
    int p, port, nports, nremov, pwrdly;
    usbd_interface_handle iface;
    usb_endpoint_descriptor_t *ed;
    struct usbd_tt *tts = NULL;

    DPRINTFN(1,("uhub_attach\n"));
    sc->sc_hub = dev;

    err = usbd_set_config_index(dev, 0, 1);
    if (err) {
        DPRINTF(("%s: configuration failed, error=%s\n",
                 sc->sc_dev.dv_xname, usbd_errstr(err)));
        return;
    }

    if (dev->depth > USB_HUB_MAX_DEPTH) {
        printf("%s: hub depth (%d) exceeded, hub ignored\n",
               sc->sc_dev.dv_xname, USB_HUB_MAX_DEPTH);
        return;
    }

    /* Get hub descriptor. */
    req.bmRequestType = UT_READ_CLASS_DEVICE;
    req.bRequest = UR_GET_DESCRIPTOR;
    USETW2(req.wValue, UDESC_HUB, 0);
    USETW(req.wIndex, 0);
    USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);
    DPRINTFN(1,("usb_init_hub: getting hub descriptor\n"));
    err = usbd_do_request(dev, &req, &hubdesc);
    nports = hubdesc.bNbrPorts;
    if (!err && nports > 7) {
        USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE + (nports+1) / 8);
        err = usbd_do_request(dev, &req, &hubdesc);
    }
    if (err) {
        DPRINTF(("%s: getting hub descriptor failed, error=%s\n",
                 sc->sc_dev.dv_xname, usbd_errstr(err)));
        return;
    }

    for (nremov = 0, port = 1; port <= nports; port++)
        if (!UHD_NOT_REMOV(&hubdesc, port))
            nremov++;

#ifdef UHUB_DEBUG
    printf("%s: %d port%s with %d removable, %s powered",
           sc->sc_dev.dv_xname, nports, nports != 1 ? "s" : "",
           nremov, dev->self_powered ? "self" : "bus");

    if (dev->depth > 0 && UHUB_IS_HIGH_SPEED(sc)) {
        printf(", %s transaction translator%s",
               UHUB_IS_SINGLE_TT(sc) ? "single" : "multiple",
               UHUB_IS_SINGLE_TT(sc) ? "" : "s");
    }
    printf("\n");
#endif

    if (nports == 0) {
        printf("%s: no ports, hub ignored\n", sc->sc_dev.dv_xname);
        goto bad;
    }

    hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port),
                 M_USBDEV, M_NOWAIT);
    if (hub == NULL)
        return;
    dev->hub = hub;
    dev->hub->hubsoftc = sc;
    hub->explore = uhub_explore;
    hub->hubdesc = hubdesc;

    DPRINTFN(1,("usbhub_init_hub: selfpowered=%d, parent=%p, "
                "parent->selfpowered=%d\n",
                dev->self_powered, dev->powersrc->parent,
                dev->powersrc->parent ?
                dev->powersrc->parent->self_powered : 0));

    if (!dev->self_powered && dev->powersrc->parent != NULL &&
            !dev->powersrc->parent->self_powered) {
        printf("%s: bus powered hub connected to bus powered hub, "
               "ignored\n", sc->sc_dev.dv_xname);
        goto bad;
    }

    /* Set up interrupt pipe. */
    err = usbd_device2interface_handle(dev, 0, &iface);
    if (err) {
        printf("%s: no interface handle\n", sc->sc_dev.dv_xname);
        goto bad;
    }
    ed = usbd_interface2endpoint_descriptor(iface, 0);
    if (ed == NULL) {
        printf("%s: no endpoint descriptor\n", sc->sc_dev.dv_xname);
        goto bad;
    }
    if ((ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
        printf("%s: bad interrupt endpoint\n", sc->sc_dev.dv_xname);
        goto bad;
    }

    err = usbd_open_pipe_intr(iface, ed->bEndpointAddress,
                              USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_status,
                              sizeof(sc->sc_status), uhub_intr, UHUB_INTR_INTERVAL);
    if (err) {
        printf("%s: cannot open interrupt pipe\n",
               sc->sc_dev.dv_xname);
        goto bad;
    }

    /* Wait with power off for a while. */
    usbd_delay_ms(dev, USB_POWER_DOWN_TIME);

    usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, dev, &sc->sc_dev);

    /*
     * To have the best chance of success we do things in the exact same
     * order as Windoze98.  This should not be necessary, but some
     * devices do not follow the USB specs to the letter.
     *
     * These are the events on the bus when a hub is attached:
     *  Get device and config descriptors (see attach code)
     *  Get hub descriptor (see above)
     *  For all ports
     *     turn on power
     *     wait for power to become stable
     * (all below happens in explore code)
     *  For all ports
     *     clear C_PORT_CONNECTION
     *  For all ports
     *     get port status
     *     if device connected
     *        wait 100 ms
     *        turn on reset
     *        wait
     *        clear C_PORT_RESET
     *        get port status
     *        proceed with device attachment
     */

    if (UHUB_IS_HIGH_SPEED(sc)) {
        tts = malloc((UHUB_IS_SINGLE_TT(sc) ? 1 : nports) *
                     sizeof (struct usbd_tt), M_USBDEV, M_NOWAIT);
        if (!tts)
            goto bad;
    }
    /* Set up data structures */
    for (p = 0; p < nports; p++) {
        struct usbd_port *up = &hub->ports[p];
        up->device = NULL;
        up->parent = dev;
        up->portno = p+1;
        if (dev->self_powered)
            /* Self powered hub, give ports maximum current. */
            up->power = USB_MAX_POWER;
        else
            up->power = USB_MIN_POWER;
        up->restartcnt = 0;
        up->reattach = 0;
        if (UHUB_IS_HIGH_SPEED(sc)) {
            up->tt = &tts[UHUB_IS_SINGLE_TT(sc) ? 0 : p];
            up->tt->hub = hub;
        } else {
            up->tt = NULL;
        }
    }

    /* XXX should check for none, individual, or ganged power? */

    pwrdly = dev->hub->hubdesc.bPwrOn2PwrGood * UHD_PWRON_FACTOR
             + USB_EXTRA_POWER_UP_TIME;
    for (port = 1; port <= nports; port++) {
        /* Turn the power on. */
        err = usbd_set_port_feature(dev, port, UHF_PORT_POWER);
        if (err)
            printf("%s: port %d power on failed, %s\n",
                   sc->sc_dev.dv_xname, port,
                   usbd_errstr(err));
        DPRINTF(("usb_init_port: turn on port %d power\n", port));
    }

    /* Wait for stable power.  Root hubs delay in their event thread. */
    if (dev->powersrc->parent != NULL)
        usbd_delay_ms(dev, pwrdly);

    /* The usual exploration will finish the setup. */

    sc->sc_running = 1;

    return;

bad:
    if (hub)
        free(hub, M_USBDEV);
    dev->hub = NULL;
}
예제 #7
0
void 
uftdi_attach(device_t parent, device_t self, void *aux)
{
	struct uftdi_softc *sc = device_private(self);
	struct usb_attach_arg *uaa = aux;
	usbd_device_handle dev = uaa->device;
	usbd_interface_handle iface;
	usb_device_descriptor_t *ddesc;
	usb_interface_descriptor_t *id;
	usb_endpoint_descriptor_t *ed;
	char *devinfop;
	const char *devname = device_xname(self);
	int i,idx;
	usbd_status err;
	struct ucom_attach_args uca;

	DPRINTFN(10,("\nuftdi_attach: sc=%p\n", sc));

	aprint_naive("\n");
	aprint_normal("\n");

	devinfop = usbd_devinfo_alloc(dev, 0);
	aprint_normal_dev(self, "%s\n", devinfop);
	usbd_devinfo_free(devinfop);

	/* Move the device into the configured state. */
	err = usbd_set_config_index(dev, UFTDI_CONFIG_INDEX, 1);
	if (err) {
		aprint_error("\n%s: failed to set configuration, err=%s\n",
		       devname, usbd_errstr(err));
		goto bad;
	}

	sc->sc_dev = self;
	sc->sc_udev = dev;
	sc->sc_numports = 1;
	sc->sc_type = UFTDI_TYPE_8U232AM; /* most devices are post-8U232AM */
	sc->sc_hdrlen = 0;
	if (uaa->vendor == USB_VENDOR_FTDI
	    && uaa->product == USB_PRODUCT_FTDI_SERIAL_8U100AX) {
		sc->sc_type = UFTDI_TYPE_SIO;
		sc->sc_hdrlen = 1;
	}

	ddesc = usbd_get_device_descriptor(dev);
	sc->sc_chiptype = UGETW(ddesc->bcdDevice);
	switch (sc->sc_chiptype) {
	case 0x500: /* 2232D */
	case 0x700: /* 2232H */
		sc->sc_numports = 2;
		break;
	case 0x800: /* 4232H */
		sc->sc_numports = 4;
		break;
	case 0x200: /* 232/245AM */
	case 0x400: /* 232/245BL */
	case 0x600: /* 232/245R */
	default:
		break;
	}

	for (idx = UFTDI_IFACE_INDEX; idx < sc->sc_numports; idx++) {
		err = usbd_device2interface_handle(dev, idx, &iface);
		if (err) {
			aprint_error(
			    "\n%s: failed to get interface idx=%d, err=%s\n",
			    devname, idx, usbd_errstr(err));
			goto bad;
		}

		id = usbd_get_interface_descriptor(iface);

		sc->sc_iface[idx] = iface;

		uca.bulkin = uca.bulkout = -1;
		uca.ibufsize = uca.obufsize = 0;
		for (i = 0; i < id->bNumEndpoints; i++) {
			int addr, dir, attr;
			ed = usbd_interface2endpoint_descriptor(iface, i);
			if (ed == NULL) {
				aprint_error_dev(self,
				    "could not read endpoint descriptor: %s\n",
				    usbd_errstr(err));
				goto bad;
			}

			addr = ed->bEndpointAddress;
			dir = UE_GET_DIR(ed->bEndpointAddress);
			attr = ed->bmAttributes & UE_XFERTYPE;
			if (dir == UE_DIR_IN && attr == UE_BULK) {
				uca.bulkin = addr;
				uca.ibufsize = UGETW(ed->wMaxPacketSize);
				if (uca.ibufsize >= UFTDI_MAX_IBUFSIZE)
					uca.ibufsize = UFTDI_MAX_IBUFSIZE;
			} else if (dir == UE_DIR_OUT && attr == UE_BULK) {
				uca.bulkout = addr;
				uca.obufsize = UGETW(ed->wMaxPacketSize)
				    - sc->sc_hdrlen;
				if (uca.obufsize >= UFTDI_MAX_OBUFSIZE)
					uca.obufsize = UFTDI_MAX_OBUFSIZE;
				/* Limit length if we have a 6-bit header.  */
				if ((sc->sc_hdrlen > 0) &&
				    (uca.obufsize > UFTDIOBUFSIZE))
					uca.obufsize = UFTDIOBUFSIZE;
			} else {
				aprint_error_dev(self,
				    "unexpected endpoint\n");
				goto bad;
			}
		}
		if (uca.bulkin == -1) {
			aprint_error_dev(self,
			    "Could not find data bulk in\n");
			goto bad;
		}
		if (uca.bulkout == -1) {
			aprint_error_dev(self,
			    "Could not find data bulk out\n");
			goto bad;
		}

		uca.portno = FTDI_PIT_SIOA + idx;
		/* bulkin, bulkout set above */
		if (uca.ibufsize == 0)
			uca.ibufsize = UFTDIIBUFSIZE;
		uca.ibufsizepad = uca.ibufsize;
		if (uca.obufsize == 0)
			uca.obufsize = UFTDIOBUFSIZE - sc->sc_hdrlen;
		uca.opkthdrlen = sc->sc_hdrlen;
		uca.device = dev;
		uca.iface = iface;
		uca.methods = &uftdi_methods;
		uca.arg = sc;
		uca.info = NULL;

		DPRINTF(("uftdi: in=0x%x out=0x%x isize=0x%x osize=0x%x\n",
			uca.bulkin, uca.bulkout,
			uca.ibufsize, uca.obufsize));
		sc->sc_subdev[idx] = config_found_sm_loc(self, "ucombus", NULL,
		    &uca, ucomprint, ucomsubmatch);
	}

	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
			   sc->sc_dev);

	return;

bad:
	DPRINTF(("uftdi_attach: ATTACH ERROR\n"));
	sc->sc_dying = 1;
	return;
}
예제 #8
0
static void
ipheth_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
{
	struct ipheth_softc *sc = usbd_xfer_softc(xfer);
	struct ifnet *ifp = uether_getifp(&sc->sc_ue);
	struct usb_page_cache *pc;
	struct mbuf *m;
	uint8_t x;
	int actlen;
	int aframes;

	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);

	DPRINTFN(1, "\n");

	switch (USB_GET_STATE(xfer)) {
	case USB_ST_TRANSFERRED:
		DPRINTFN(11, "transfer complete: %u bytes in %u frames\n",
		    actlen, aframes);

		ifp->if_opackets++;

		/* free all previous TX buffers */
		ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);

		/* FALLTHROUGH */
	case USB_ST_SETUP:
tr_setup:
		for (x = 0; x != IPHETH_TX_FRAMES_MAX; x++) {

			IFQ_DRV_DEQUEUE(&ifp->if_snd, m);

			if (m == NULL)
				break;

			usbd_xfer_set_frame_offset(xfer,
			    x * IPHETH_BUF_SIZE, x);

			pc = usbd_xfer_get_frame(xfer, x);

			sc->sc_tx_buf[x] = m;

			if (m->m_pkthdr.len > IPHETH_BUF_SIZE)
				m->m_pkthdr.len = IPHETH_BUF_SIZE;

			usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);

			usbd_xfer_set_frame_len(xfer, x, IPHETH_BUF_SIZE);

			if (IPHETH_BUF_SIZE != m->m_pkthdr.len) {
				usbd_frame_zero(pc, m->m_pkthdr.len,
					IPHETH_BUF_SIZE - m->m_pkthdr.len);
			}

			/*
			 * If there's a BPF listener, bounce a copy of
			 * this frame to him:
			 */
			BPF_MTAP(ifp, m);
		}
		if (x != 0) {
			usbd_xfer_set_frames(xfer, x);

			usbd_transfer_submit(xfer);
		}
		break;

	default:			/* Error */
		DPRINTFN(11, "transfer error, %s\n",
		    usbd_errstr(error));

		/* free all previous TX buffers */
		ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);

		/* count output errors */
		ifp->if_oerrors++;

		if (error != USB_ERR_CANCELLED) {
			/* try to clear stall first */
			usbd_xfer_set_stall(xfer);
			goto tr_setup;
		}
		break;
	}
}
예제 #9
0
static void
ipheth_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
{
	struct ipheth_softc *sc = usbd_xfer_softc(xfer);
	struct mbuf *m;
	uint8_t x;
	int actlen;
	int aframes;
	int len;

	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);

	switch (USB_GET_STATE(xfer)) {
	case USB_ST_TRANSFERRED:

		DPRINTF("received %u bytes in %u frames\n", actlen, aframes);

		for (x = 0; x != aframes; x++) {

			m = sc->sc_rx_buf[x];
			sc->sc_rx_buf[x] = NULL;
			len = usbd_xfer_frame_len(xfer, x);

			if (len < (int)(sizeof(struct ether_header) +
			    IPHETH_RX_ADJ)) {
				m_freem(m);
				continue;
			}

			m_adj(m, IPHETH_RX_ADJ);

			/* queue up mbuf */
			uether_rxmbuf(&sc->sc_ue, m, len - IPHETH_RX_ADJ);
		}

		/* FALLTHROUGH */
	case USB_ST_SETUP:

		for (x = 0; x != IPHETH_RX_FRAMES_MAX; x++) {
			if (sc->sc_rx_buf[x] == NULL) {
				m = uether_newbuf();
				if (m == NULL)
					goto tr_stall;

				/* cancel alignment for ethernet */
				m_adj(m, ETHER_ALIGN);

				sc->sc_rx_buf[x] = m;
			} else {
				m = sc->sc_rx_buf[x];
			}

			usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len);
		}
		/* set number of frames and start hardware */
		usbd_xfer_set_frames(xfer, x);
		usbd_transfer_submit(xfer);
		/* flush any received frames */
		uether_rxflush(&sc->sc_ue);
		break;

	default:			/* Error */
		DPRINTF("error = %s\n", usbd_errstr(error));

		if (error != USB_ERR_CANCELLED) {
	tr_stall:
			/* try to clear stall first */
			usbd_xfer_set_stall(xfer);
			usbd_xfer_set_frames(xfer, 0);
			usbd_transfer_submit(xfer);
			break;
		}
		/* need to free the RX-mbufs when we are cancelled */
		ipheth_free_queue(sc->sc_rx_buf, IPHETH_RX_FRAMES_MAX);
		break;
	}
}
예제 #10
0
파일: if_cdce.c 프로젝트: hlcherub/src
static void
cdce_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
{
    struct cdce_softc *sc = usbd_xfer_softc(xfer);
    struct ifnet *ifp = uether_getifp(&sc->sc_ue);
    struct mbuf *m;
    struct mbuf *mt;
    uint32_t crc;
    uint8_t x;
    int actlen, aframes;

    usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);

    DPRINTFN(1, "\n");

    switch (USB_GET_STATE(xfer)) {
    case USB_ST_TRANSFERRED:
        DPRINTFN(11, "transfer complete: %u bytes in %u frames\n",
                 actlen, aframes);

        ifp->if_opackets++;

        /* free all previous TX buffers */
        cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX);

    /* FALLTHROUGH */
    case USB_ST_SETUP:
tr_setup:
        for (x = 0; x != CDCE_FRAMES_MAX; x++) {

            IFQ_DRV_DEQUEUE(&ifp->if_snd, m);

            if (m == NULL)
                break;

            if (sc->sc_flags & CDCE_FLAG_ZAURUS) {
                /*
                 * Zaurus wants a 32-bit CRC appended
                 * to every frame
                 */

                crc = cdce_m_crc32(m, 0, m->m_pkthdr.len);
                crc = htole32(crc);

                if (!m_append(m, 4, (void *)&crc)) {
                    m_freem(m);
                    ifp->if_oerrors++;
                    continue;
                }
            }
            if (m->m_len != m->m_pkthdr.len) {
                mt = m_defrag(m, M_NOWAIT);
                if (mt == NULL) {
                    m_freem(m);
                    ifp->if_oerrors++;
                    continue;
                }
                m = mt;
            }
            if (m->m_pkthdr.len > MCLBYTES) {
                m->m_pkthdr.len = MCLBYTES;
            }
            sc->sc_tx_buf[x] = m;
            usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len);

            /*
             * If there's a BPF listener, bounce a copy of
             * this frame to him:
             */
            BPF_MTAP(ifp, m);
        }
        if (x != 0) {
            usbd_xfer_set_frames(xfer, x);

            usbd_transfer_submit(xfer);
        }
        break;

    default:			/* Error */
        DPRINTFN(11, "transfer error, %s\n",
                 usbd_errstr(error));

        /* free all previous TX buffers */
        cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX);

        /* count output errors */
        ifp->if_oerrors++;

        if (error != USB_ERR_CANCELLED) {
            /* try to clear stall first */
            usbd_xfer_set_stall(xfer);
            goto tr_setup;
        }
        break;
    }
}
예제 #11
0
파일: if_cdce.c 프로젝트: hlcherub/src
static void
cdce_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
{
    struct cdce_softc *sc = usbd_xfer_softc(xfer);
    struct mbuf *m;
    uint8_t x;
    int actlen;
    int aframes;
    int len;

    usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);

    switch (USB_GET_STATE(xfer)) {
    case USB_ST_TRANSFERRED:

        DPRINTF("received %u bytes in %u frames\n", actlen, aframes);

        for (x = 0; x != aframes; x++) {

            m = sc->sc_rx_buf[x];
            sc->sc_rx_buf[x] = NULL;
            len = usbd_xfer_frame_len(xfer, x);

            /* Strip off CRC added by Zaurus, if any */
            if ((sc->sc_flags & CDCE_FLAG_ZAURUS) && len >= 14)
                len -= 4;

            if (len < (int)sizeof(struct ether_header)) {
                m_freem(m);
                continue;
            }
            /* queue up mbuf */
            uether_rxmbuf(&sc->sc_ue, m, len);
        }

    /* FALLTHROUGH */
    case USB_ST_SETUP:
        /*
         * TODO: Implement support for multi frame transfers,
         * when the USB hardware supports it.
         */
        for (x = 0; x != 1; x++) {
            if (sc->sc_rx_buf[x] == NULL) {
                m = uether_newbuf();
                if (m == NULL)
                    goto tr_stall;
                sc->sc_rx_buf[x] = m;
            } else {
                m = sc->sc_rx_buf[x];
            }

            usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len);
        }
        /* set number of frames and start hardware */
        usbd_xfer_set_frames(xfer, x);
        usbd_transfer_submit(xfer);
        /* flush any received frames */
        uether_rxflush(&sc->sc_ue);
        break;

    default:			/* Error */
        DPRINTF("error = %s\n",
                usbd_errstr(error));

        if (error != USB_ERR_CANCELLED) {
tr_stall:
            /* try to clear stall first */
            usbd_xfer_set_stall(xfer);
            usbd_xfer_set_frames(xfer, 0);
            usbd_transfer_submit(xfer);
            break;
        }

        /* need to free the RX-mbufs when we are cancelled */
        cdce_free_queue(sc->sc_rx_buf, CDCE_FRAMES_MAX);
        break;
    }
}
예제 #12
0
파일: if_cdce.c 프로젝트: hlcherub/src
static void
cdce_ncm_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
{
    struct cdce_softc *sc = usbd_xfer_softc(xfer);
    struct usb_page_cache *pc = usbd_xfer_get_frame(xfer, 0);
    struct ifnet *ifp = uether_getifp(&sc->sc_ue);
    struct mbuf *m;
    int sumdata;
    int sumlen;
    int actlen;
    int aframes;
    int temp;
    int nframes;
    int x;
    int offset;

    switch (USB_GET_STATE(xfer)) {
    case USB_ST_TRANSFERRED:

        usbd_xfer_status(xfer, &actlen, &sumlen, &aframes, NULL);

        DPRINTFN(1, "received %u bytes in %u frames\n",
                 actlen, aframes);

        if (actlen < (int)(sizeof(sc->sc_ncm.hdr) +
                           sizeof(sc->sc_ncm.dpt))) {
            DPRINTFN(1, "frame too short\n");
            goto tr_setup;
        }
        usbd_copy_out(pc, 0, &(sc->sc_ncm.hdr),
                      sizeof(sc->sc_ncm.hdr));

        if ((sc->sc_ncm.hdr.dwSignature[0] != 'N') ||
                (sc->sc_ncm.hdr.dwSignature[1] != 'C') ||
                (sc->sc_ncm.hdr.dwSignature[2] != 'M') ||
                (sc->sc_ncm.hdr.dwSignature[3] != 'H')) {
            DPRINTFN(1, "invalid HDR signature: "
                     "0x%02x:0x%02x:0x%02x:0x%02x\n",
                     sc->sc_ncm.hdr.dwSignature[0],
                     sc->sc_ncm.hdr.dwSignature[1],
                     sc->sc_ncm.hdr.dwSignature[2],
                     sc->sc_ncm.hdr.dwSignature[3]);
            goto tr_stall;
        }
        temp = UGETW(sc->sc_ncm.hdr.wBlockLength);
        if (temp > sumlen) {
            DPRINTFN(1, "unsupported block length %u/%u\n",
                     temp, sumlen);
            goto tr_stall;
        }
        temp = UGETW(sc->sc_ncm.hdr.wDptIndex);
        if ((int)(temp + sizeof(sc->sc_ncm.dpt)) > actlen) {
            DPRINTFN(1, "invalid DPT index: 0x%04x\n", temp);
            goto tr_stall;
        }
        usbd_copy_out(pc, temp, &(sc->sc_ncm.dpt),
                      sizeof(sc->sc_ncm.dpt));

        if ((sc->sc_ncm.dpt.dwSignature[0] != 'N') ||
                (sc->sc_ncm.dpt.dwSignature[1] != 'C') ||
                (sc->sc_ncm.dpt.dwSignature[2] != 'M') ||
                (sc->sc_ncm.dpt.dwSignature[3] != '0')) {
            DPRINTFN(1, "invalid DPT signature"
                     "0x%02x:0x%02x:0x%02x:0x%02x\n",
                     sc->sc_ncm.dpt.dwSignature[0],
                     sc->sc_ncm.dpt.dwSignature[1],
                     sc->sc_ncm.dpt.dwSignature[2],
                     sc->sc_ncm.dpt.dwSignature[3]);
            goto tr_stall;
        }
        nframes = UGETW(sc->sc_ncm.dpt.wLength) / 4;

        /* Subtract size of header and last zero padded entry */
        if (nframes >= (2 + 1))
            nframes -= (2 + 1);
        else
            nframes = 0;

        DPRINTFN(1, "nframes = %u\n", nframes);

        temp += sizeof(sc->sc_ncm.dpt);

        if ((temp + (4 * nframes)) > actlen)
            goto tr_stall;

        if (nframes > CDCE_NCM_SUBFRAMES_MAX) {
            DPRINTFN(1, "Truncating number of frames from %u to %u\n",
                     nframes, CDCE_NCM_SUBFRAMES_MAX);
            nframes = CDCE_NCM_SUBFRAMES_MAX;
        }
        usbd_copy_out(pc, temp, &(sc->sc_ncm.dp), (4 * nframes));

        sumdata = 0;

        for (x = 0; x != nframes; x++) {

            offset = UGETW(sc->sc_ncm.dp[x].wFrameIndex);
            temp = UGETW(sc->sc_ncm.dp[x].wFrameLength);

            if ((offset == 0) ||
                    (temp < (int)sizeof(struct ether_header)) ||
                    (temp > (MCLBYTES - ETHER_ALIGN))) {
                DPRINTFN(1, "NULL frame detected at %d\n", x);
                m = NULL;
                /* silently ignore this frame */
                continue;
            } else if ((offset + temp) > actlen) {
                DPRINTFN(1, "invalid frame "
                         "detected at %d\n", x);
                m = NULL;
                /* silently ignore this frame */
                continue;
            } else if (temp > (int)(MHLEN - ETHER_ALIGN)) {
                m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
            } else {
                m = m_gethdr(M_NOWAIT, MT_DATA);
            }

            DPRINTFN(16, "frame %u, offset = %u, length = %u \n",
                     x, offset, temp);

            /* check if we have a buffer */
            if (m) {
                m_adj(m, ETHER_ALIGN);

                usbd_copy_out(pc, offset, m->m_data, temp);

                /* enqueue */
                uether_rxmbuf(&sc->sc_ue, m, temp);

                sumdata += temp;
            } else {
                ifp->if_ierrors++;
            }
        }

        DPRINTFN(1, "Efficiency: %u/%u bytes\n", sumdata, actlen);

    case USB_ST_SETUP:
tr_setup:
        usbd_xfer_set_frame_len(xfer, 0, sc->sc_ncm.rx_max);
        usbd_xfer_set_frames(xfer, 1);
        usbd_transfer_submit(xfer);
        uether_rxflush(&sc->sc_ue);	/* must be last */
        break;

    default:			/* Error */
        DPRINTFN(1, "error = %s\n",
                 usbd_errstr(error));

        if (error != USB_ERR_CANCELLED) {
tr_stall:
            /* try to clear stall first */
            usbd_xfer_set_stall(xfer);
            usbd_xfer_set_frames(xfer, 0);
            usbd_transfer_submit(xfer);
        }
        break;
    }
}
예제 #13
0
파일: uvisor.c 프로젝트: SylvestreG/bitrig
void
uvisor_attach(struct device *parent, struct device *self, void *aux)
{
	struct uvisor_softc *sc = (struct uvisor_softc *)self;
	struct usb_attach_arg *uaa = aux;
	struct usbd_device *dev = uaa->device;
	struct usbd_interface *iface;
	usb_interface_descriptor_t *id;
	struct uvisor_connection_info coninfo;
	struct uvisor_palm_connection_info palmconinfo;
	usb_endpoint_descriptor_t *ed;
	int i, j, hasin, hasout, port;
	usbd_status err;
	struct ucom_attach_args uca;

	DPRINTFN(10,("\nuvisor_attach: sc=%p\n", sc));

	/* Move the device into the configured state. */
	err = usbd_set_config_index(dev, UVISOR_CONFIG_INDEX, 1);
	if (err) {
		printf(": failed to set configuration, err=%s\n",
		    usbd_errstr(err));
		goto bad;
	}

	err = usbd_device2interface_handle(dev, UVISOR_IFACE_INDEX, &iface);
	if (err) {
		printf(": failed to get interface, err=%s\n",
		    usbd_errstr(err));
		goto bad;
	}

	sc->sc_flags = uvisor_lookup(uaa->vendor, uaa->product)->uv_flags;
	sc->sc_vendor = uaa->vendor;
	
	if ((sc->sc_flags & (VISOR | PALM4)) == 0) {
		printf("%s: device is neither visor nor palm\n", 
		    sc->sc_dev.dv_xname);
		goto bad;
	}

	id = usbd_get_interface_descriptor(iface);

	sc->sc_udev = dev;
	sc->sc_iface = iface;

	uca.ibufsize = UVISORIBUFSIZE;
	uca.obufsize = UVISOROBUFSIZE;
	uca.ibufsizepad = UVISORIBUFSIZE;
	uca.opkthdrlen = 0;
	uca.device = dev;
	uca.iface = iface;
	uca.methods = &uvisor_methods;
	uca.arg = sc;

	err = uvisor_init(sc, &coninfo, &palmconinfo);
	if (err) {
		printf("%s: init failed, %s\n", sc->sc_dev.dv_xname,
		       usbd_errstr(err));
		goto bad;
	}

	if (sc->sc_flags & VISOR) {
		sc->sc_numcon = UGETW(coninfo.num_ports);
		if (sc->sc_numcon > UVISOR_MAX_CONN)
			sc->sc_numcon = UVISOR_MAX_CONN;

		/* Attach a ucom for each connection. */
		for (i = 0; i < sc->sc_numcon; ++i) {
			switch (coninfo.connections[i].port_function_id) {
			case UVISOR_FUNCTION_GENERIC:
				uca.info = "Generic";
				break;
			case UVISOR_FUNCTION_DEBUGGER:
				uca.info = "Debugger";
				break;
			case UVISOR_FUNCTION_HOTSYNC:
				uca.info = "HotSync";
				break;
			case UVISOR_FUNCTION_REMOTE_FILE_SYS:
				uca.info = "Remote File System";
				break;
			default:
				uca.info = "unknown";
				break;
			}
			port = coninfo.connections[i].port;
			uca.portno = port;
			uca.bulkin = port | UE_DIR_IN;
			uca.bulkout = port | UE_DIR_OUT;
			/* Verify that endpoints exist. */
			hasin = 0;
			hasout = 0;
			for (j = 0; j < id->bNumEndpoints; j++) {
				ed = usbd_interface2endpoint_descriptor(iface, j);
				if (ed == NULL)
					break;
				if (UE_GET_ADDR(ed->bEndpointAddress) == port &&
				    (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
					if (UE_GET_DIR(ed->bEndpointAddress)
					    == UE_DIR_IN)
						hasin++;
					else
						hasout++;
				}
			}
			if (hasin == 1 && hasout == 1)
				sc->sc_subdevs[i] = config_found_sm(self, &uca,
				    ucomprint, ucomsubmatch);
			else
				printf("%s: no proper endpoints for port %d (%d,%d)\n",
				    sc->sc_dev.dv_xname, port, hasin, hasout);
		}
	} else {
		sc->sc_numcon = palmconinfo.num_ports;
		if (sc->sc_numcon > UVISOR_MAX_CONN)
			sc->sc_numcon = UVISOR_MAX_CONN;

		/* Attach a ucom for each connection. */
		for (i = 0; i < sc->sc_numcon; ++i) {
			/*
			 * XXX this should copy out 4-char string from the
			 * XXX port_function_id, but where would the string go?
			 * XXX uca.info is a const char *, not an array.
			 */
			uca.info = "sync";
			uca.portno = i;
			if (palmconinfo.endpoint_numbers_different) {
				port = palmconinfo.connections[i].end_point_info;
				uca.bulkin = (port >> 4) | UE_DIR_IN;
				uca.bulkout = (port & 0xf) | UE_DIR_OUT;
			} else {
				port = palmconinfo.connections[i].port;
				uca.bulkin = port | UE_DIR_IN;
				uca.bulkout = port | UE_DIR_OUT;
			}
			sc->sc_subdevs[i] = config_found_sm(self, &uca,
			    ucomprint, ucomsubmatch);
		}
예제 #14
0
void 
uipaq_attach(device_t parent, device_t self, void *aux)
{
	struct uipaq_softc *sc = device_private(self);
	struct usb_attach_arg *uaa = aux;
	usbd_device_handle dev = uaa->device;
	usbd_interface_handle iface;
	usb_interface_descriptor_t *id;
	usb_endpoint_descriptor_t *ed;
	char *devinfop;
	const char *devname = device_xname(self);
	int i;
	usbd_status err;
	struct ucom_attach_args uca;

	DPRINTFN(10,("\nuipaq_attach: sc=%p\n", sc));

	sc->sc_dev = self;

	aprint_naive("\n");
	aprint_normal("\n");

	devinfop = usbd_devinfo_alloc(dev, 0);
	aprint_normal_dev(self, "%s\n", devinfop);
	usbd_devinfo_free(devinfop);

	/* Move the device into the configured state. */
	err = usbd_set_config_no(dev, UIPAQ_CONFIG_NO, 1);
	if (err) {
		aprint_error_dev(self, "failed to set configuration"
		    ", err=%s\n", usbd_errstr(err));
		goto bad;
	}

	err = usbd_device2interface_handle(dev, UIPAQ_IFACE_INDEX, &iface);
	if (err) {
		aprint_error("\n%s: failed to get interface, err=%s\n",
		    devname, usbd_errstr(err));
		goto bad;
	}

	sc->sc_flags = uipaq_lookup(uaa->vendor, uaa->product)->uv_flags;

	id = usbd_get_interface_descriptor(iface);

	sc->sc_udev = dev;
	sc->sc_iface = iface;

	uca.ibufsize = UIPAQIBUFSIZE;
	uca.obufsize = UIPAQOBUFSIZE;
	uca.ibufsizepad = UIPAQIBUFSIZE;
	uca.opkthdrlen = 0;
	uca.device = dev;
	uca.iface = iface;
	uca.methods = &uipaq_methods;
	uca.arg = sc;
	uca.portno = UCOM_UNK_PORTNO;
	uca.info = "Generic";

/*	err = uipaq_init(sc);
	if (err) {
		printf("%s: init failed, %s\n", device_xname(sc->sc_dev),
		    usbd_errstr(err));
		goto bad;
	}*/

	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
	    sc->sc_dev);

	uca.bulkin = uca.bulkout = -1;
	for (i=0; i<id->bNumEndpoints; i++) {
		ed = usbd_interface2endpoint_descriptor(iface, i);
		if (ed == NULL) {
			aprint_error_dev(self,
			    "no endpoint descriptor for %d\n", i);
			goto bad;
		}
		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
		    (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
			uca.bulkin = ed->bEndpointAddress;
		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
		    (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
			uca.bulkout = ed->bEndpointAddress;
		}
	}
	if (uca.bulkin == -1 || uca.bulkout == -1) {
		aprint_error_dev(self, "no proper endpoints found (%d,%d) \n",
		    uca.bulkin, uca.bulkout);
		return;
	}

	sc->sc_subdev = config_found_sm_loc(self, "ucombus", NULL, &uca,
					    ucomprint, ucomsubmatch);

	return;

bad:
	DPRINTF(("uipaq_attach: ATTACH ERROR\n"));
	sc->sc_dying = 1;
	return;
}
예제 #15
0
파일: ucom.c 프로젝트: MarginC/kame
Static void
ucomstart(struct tty *tp)
{
	struct ucom_softc *sc;
	struct cblock *cbp;
	usbd_status err;
	int s;
	u_char *data;
	int cnt;

	USB_GET_SC(ucom, UCOMUNIT(tp->t_dev), sc);
	DPRINTF(("ucomstart: sc = %p\n", sc));

	if (sc->sc_dying)
		return;

	s = spltty();

	if (tp->t_state & TS_TBLOCK) {
		if (ISSET(sc->sc_mcr, UMCR_RTS) &&
		    ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
			DPRINTF(("ucomstart: clear RTS\n"));
			(void)ucomctl(sc, UMCR_RTS, DMBIC);
		}
	} else {
		if (!ISSET(sc->sc_mcr, UMCR_RTS) &&
		    tp->t_rawq.c_cc <= tp->t_ilowat &&
		    ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
			DPRINTF(("ucomstart: set RTS\n"));
			(void)ucomctl(sc, UMCR_RTS, DMBIS);
		}
	}

	if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
		ttwwakeup(tp);
		DPRINTF(("ucomstart: stopped\n"));
		goto out;
	}

	if (tp->t_outq.c_cc <= tp->t_olowat) {
		if (ISSET(tp->t_state, TS_SO_OLOWAT)) {
			CLR(tp->t_state, TS_SO_OLOWAT);
			wakeup(TSA_OLOWAT(tp));
		}
		selwakeup(&tp->t_wsel);
		if (tp->t_outq.c_cc == 0) {
			if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
			    TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
				CLR(tp->t_state, TS_SO_OCOMPLETE);
				wakeup(TSA_OCOMPLETE(tp));
			}
			goto out;
		}
	}

	/* Grab the first contiguous region of buffer space. */
	data = tp->t_outq.c_cf;
	cbp = (struct cblock *) ((intptr_t) tp->t_outq.c_cf & ~CROUND);
	cnt = min((char *) (cbp+1) - tp->t_outq.c_cf, tp->t_outq.c_cc);

	if (cnt == 0) {
		DPRINTF(("ucomstart: cnt == 0\n"));
		goto out;
	}

	SET(tp->t_state, TS_BUSY);

	if (cnt > sc->sc_obufsize) {
		DPRINTF(("ucomstart: big buffer %d chars\n", cnt));
		cnt = sc->sc_obufsize;
	}
	if (sc->sc_callback->ucom_write != NULL)
		sc->sc_callback->ucom_write(sc->sc_parent, sc->sc_portno,
					    sc->sc_obuf, data, &cnt);
	else
		memcpy(sc->sc_obuf, data, cnt);

	DPRINTF(("ucomstart: %d chars\n", cnt));
	usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe, 
			(usbd_private_handle)sc, sc->sc_obuf, cnt,
			USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb);
	/* What can we do on error? */
	err = usbd_transfer(sc->sc_oxfer);
	if (err != USBD_IN_PROGRESS)
		printf("ucomstart: err=%s\n", usbd_errstr(err));

	ttwwakeup(tp);

    out:
	splx(s);
}
예제 #16
0
void
smsc_init(void *xsc)
{
	struct smsc_softc	*sc = xsc;
	struct ifnet		*ifp = &sc->sc_ac.ac_if;
	struct smsc_chain	*c;
	usbd_status		 err;
	int			 s, i;
	
	s = splnet();

	/* Cancel pending I/O */
	smsc_stop(sc);

	/* Reset the ethernet interface. */
	smsc_reset(sc);

	/* Init RX ring. */
	if (smsc_rx_list_init(sc) == ENOBUFS) {
		printf("%s: rx list init failed\n", sc->sc_dev.dv_xname);
		splx(s);
		return;
	}

	/* Init TX ring. */
	if (smsc_tx_list_init(sc) == ENOBUFS) {
		printf("%s: tx list init failed\n", sc->sc_dev.dv_xname);
		splx(s);
		return;
	}

	/* Program promiscuous mode and multicast filters. */
	smsc_iff(sc);

	/* Open RX and TX pipes. */
	err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[SMSC_ENDPT_RX],
	    USBD_EXCLUSIVE_USE, &sc->sc_ep[SMSC_ENDPT_RX]);
	if (err) {
		printf("%s: open rx pipe failed: %s\n",
		    sc->sc_dev.dv_xname, usbd_errstr(err));
		splx(s);
		return;
	}

	err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[SMSC_ENDPT_TX],
	    USBD_EXCLUSIVE_USE, &sc->sc_ep[SMSC_ENDPT_TX]);
	if (err) {
		printf("%s: open tx pipe failed: %s\n",
		    sc->sc_dev.dv_xname, usbd_errstr(err));
		splx(s);
		return;
	}

	/* Start up the receive pipe. */
	for (i = 0; i < SMSC_RX_LIST_CNT; i++) {
		c = &sc->sc_cdata.rx_chain[i];
		usbd_setup_xfer(c->sc_xfer, sc->sc_ep[SMSC_ENDPT_RX],
		    c, c->sc_buf, sc->sc_bufsz,
		    USBD_SHORT_XFER_OK | USBD_NO_COPY,
		    USBD_NO_TIMEOUT, smsc_rxeof);
		usbd_transfer(c->sc_xfer);
	}

	/* TCP/UDP checksum offload engines. */
	smsc_sethwcsum(sc);

	/* Indicate we are up and running. */
	ifp->if_flags |= IFF_RUNNING;
	ifp->if_flags &= ~IFF_OACTIVE;

	timeout_add_sec(&sc->sc_stat_ch, 1);

	splx(s);
}
예제 #17
0
/*------------------------------------------------------------------------*
 *	usb_bus_attach
 *
 * This function attaches USB in context of the explore thread.
 *------------------------------------------------------------------------*/
static void
usb_bus_attach(struct usb_proc_msg *pm)
{
	struct usb_bus *bus;
	struct usb_device *child;
	device_t dev;
	usb_error_t err;
	enum usb_dev_speed speed;

	bus = ((struct usb_bus_msg *)pm)->bus;
	dev = bus->bdev;

	DPRINTF("\n");

	switch (bus->usbrev) {
	case USB_REV_1_0:
		speed = USB_SPEED_FULL;
		device_printf(bus->bdev, "12Mbps Full Speed USB v1.0\n");
		break;

	case USB_REV_1_1:
		speed = USB_SPEED_FULL;
		device_printf(bus->bdev, "12Mbps Full Speed USB v1.1\n");
		break;

	case USB_REV_2_0:
		speed = USB_SPEED_HIGH;
		device_printf(bus->bdev, "480Mbps High Speed USB v2.0\n");
		break;

	case USB_REV_2_5:
		speed = USB_SPEED_VARIABLE;
		device_printf(bus->bdev, "480Mbps Wireless USB v2.5\n");
		break;

	case USB_REV_3_0:
		speed = USB_SPEED_SUPER;
		device_printf(bus->bdev, "5.0Gbps Super Speed USB v3.0\n");
		break;

	default:
		device_printf(bus->bdev, "Unsupported USB revision\n");
		usb_root_mount_rel(bus);
		return;
	}

	/* default power_mask value */
	bus->hw_power_state =
	  USB_HW_POWER_CONTROL |
	  USB_HW_POWER_BULK |
	  USB_HW_POWER_INTERRUPT |
	  USB_HW_POWER_ISOC |
	  USB_HW_POWER_NON_ROOT_HUB;

	USB_BUS_UNLOCK(bus);

	/* make sure power is set at least once */

	if (bus->methods->set_hw_power != NULL) {
		(bus->methods->set_hw_power) (bus);
	}

	/* allocate the Root USB device */

	child = usb_alloc_device(bus->bdev, bus, NULL, 0, 0, 1,
	    speed, USB_MODE_HOST);
	if (child) {
		err = usb_probe_and_attach(child,
		    USB_IFACE_INDEX_ANY);
		if (!err) {
			if ((bus->devices[USB_ROOT_HUB_ADDR] == NULL) ||
			    (bus->devices[USB_ROOT_HUB_ADDR]->hub == NULL)) {
				err = USB_ERR_NO_ROOT_HUB;
			}
		}
	} else {
		err = USB_ERR_NOMEM;
	}

	USB_BUS_LOCK(bus);

	if (err) {
		device_printf(bus->bdev, "Root HUB problem, error=%s\n",
		    usbd_errstr(err));
		usb_root_mount_rel(bus);
	}

	/* set softc - we are ready */
	device_set_softc(dev, bus);

	/* start watchdog */
	usb_power_wdog(bus);
}
예제 #18
0
void
smsc_stop(struct smsc_softc *sc)
{
	usbd_status		err;
	struct ifnet		*ifp;
	int			i;

	smsc_reset(sc);

	ifp = &sc->sc_ac.ac_if;
	ifp->if_timer = 0;
	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);

	timeout_del(&sc->sc_stat_ch);

	/* Stop transfers. */
	if (sc->sc_ep[SMSC_ENDPT_RX] != NULL) {
		err = usbd_abort_pipe(sc->sc_ep[SMSC_ENDPT_RX]);
		if (err) {
			printf("%s: abort rx pipe failed: %s\n",
			    sc->sc_dev.dv_xname, usbd_errstr(err));
		}
		err = usbd_close_pipe(sc->sc_ep[SMSC_ENDPT_RX]);
		if (err) {
			printf("%s: close rx pipe failed: %s\n",
			    sc->sc_dev.dv_xname, usbd_errstr(err));
		}
		sc->sc_ep[SMSC_ENDPT_RX] = NULL;
	}

	if (sc->sc_ep[SMSC_ENDPT_TX] != NULL) {
		err = usbd_abort_pipe(sc->sc_ep[SMSC_ENDPT_TX]);
		if (err) {
			printf("%s: abort tx pipe failed: %s\n",
			    sc->sc_dev.dv_xname, usbd_errstr(err));
		}
		err = usbd_close_pipe(sc->sc_ep[SMSC_ENDPT_TX]);
		if (err) {
			printf("%s: close tx pipe failed: %s\n",
			    sc->sc_dev.dv_xname, usbd_errstr(err));
		}
		sc->sc_ep[SMSC_ENDPT_TX] = NULL;
	}

	if (sc->sc_ep[SMSC_ENDPT_INTR] != NULL) {
		err = usbd_abort_pipe(sc->sc_ep[SMSC_ENDPT_INTR]);
		if (err) {
			printf("%s: abort intr pipe failed: %s\n",
			    sc->sc_dev.dv_xname, usbd_errstr(err));
		}
		err = usbd_close_pipe(sc->sc_ep[SMSC_ENDPT_INTR]);
		if (err) {
			printf("%s: close intr pipe failed: %s\n",
			    sc->sc_dev.dv_xname, usbd_errstr(err));
		}
		sc->sc_ep[SMSC_ENDPT_INTR] = NULL;
	}

	/* Free RX resources. */
	for (i = 0; i < SMSC_RX_LIST_CNT; i++) {
		if (sc->sc_cdata.rx_chain[i].sc_mbuf != NULL) {
			m_freem(sc->sc_cdata.rx_chain[i].sc_mbuf);
			sc->sc_cdata.rx_chain[i].sc_mbuf = NULL;
		}
		if (sc->sc_cdata.rx_chain[i].sc_xfer != NULL) {
			usbd_free_xfer(sc->sc_cdata.rx_chain[i].sc_xfer);
			sc->sc_cdata.rx_chain[i].sc_xfer = NULL;
		}
	}

	/* Free TX resources. */
	for (i = 0; i < SMSC_TX_LIST_CNT; i++) {
		if (sc->sc_cdata.tx_chain[i].sc_mbuf != NULL) {
			m_freem(sc->sc_cdata.tx_chain[i].sc_mbuf);
			sc->sc_cdata.tx_chain[i].sc_mbuf = NULL;
		}
		if (sc->sc_cdata.tx_chain[i].sc_xfer != NULL) {
			usbd_free_xfer(sc->sc_cdata.tx_chain[i].sc_xfer);
			sc->sc_cdata.tx_chain[i].sc_xfer = NULL;
		}
	}
}
예제 #19
0
usbd_status
uhub_explore(usbd_device_handle dev)
{
    usb_hub_descriptor_t *hd = &dev->hub->hubdesc;
    struct uhub_softc *sc = dev->hub->hubsoftc;
    struct usbd_port *up;
    usbd_status err;
    int speed;
    int port;
    int change, status, reconnect;

    DPRINTFN(10, ("uhub_explore dev=%p addr=%d\n", dev, dev->address));

    if (!sc->sc_running)
        return (USBD_NOT_STARTED);

    /* Ignore hubs that are too deep. */
    if (dev->depth > USB_HUB_MAX_DEPTH)
        return (USBD_TOO_DEEP);

    for(port = 1; port <= hd->bNbrPorts; port++) {
        up = &dev->hub->ports[port-1];
        err = usbd_get_port_status(dev, port, &up->status);
        if (err) {
            DPRINTF(("uhub_explore: get port status failed, "
                     "error=%s\n", usbd_errstr(err)));
            continue;
        }
        status = UGETW(up->status.wPortStatus);
        change = UGETW(up->status.wPortChange);
        reconnect = up->reattach;
        up->reattach = 0;
        DPRINTFN(3,("uhub_explore: %s port %d status 0x%04x 0x%04x\n",
                    sc->sc_dev.dv_xname, port, status, change));
        if (change & UPS_C_PORT_ENABLED) {
            DPRINTF(("uhub_explore: C_PORT_ENABLED\n"));
            usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);
            if (change & UPS_C_CONNECT_STATUS) {
                /* Ignore the port error if the device
                   vanished. */
            } else if (status & UPS_PORT_ENABLED) {
                printf("%s: illegal enable change, port %d\n",
                       sc->sc_dev.dv_xname, port);
            } else {
                /* Port error condition. */
                if (up->restartcnt) /* no message first time */
                    printf("%s: port error, restarting "
                           "port %d\n",
                           sc->sc_dev.dv_xname, port);

                if (up->restartcnt++ < USBD_RESTART_MAX)
                    goto disco;
                else
                    printf("%s: port error, giving up "
                           "port %d\n",
                           sc->sc_dev.dv_xname, port);
            }
        }
        if (!reconnect && !(change & UPS_C_CONNECT_STATUS)) {
            DPRINTFN(3,("uhub_explore: port=%d !C_CONNECT_"
                        "STATUS\n", port));
            /* No status change, just do recursive explore. */
            if (up->device != NULL && up->device->hub != NULL)
                up->device->hub->explore(up->device);
#if 0 && defined(DIAGNOSTIC)
            if (up->device == NULL &&
                    (status & UPS_CURRENT_CONNECT_STATUS))
                printf("%s: connected, no device\n",
                       sc->sc_dev.dv_xname);
#endif
            continue;
        }

        /* We have a connect status change, handle it. */

        DPRINTF(("uhub_explore: status change hub=%d port=%d\n",
                 dev->address, port));
        usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION);
        /*usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);*/
        /*
         * If there is already a device on the port the change status
         * must mean that is has disconnected.  Looking at the
         * current connect status is not enough to figure this out
         * since a new unit may have been connected before we handle
         * the disconnect.
         */
disco:
        if (up->device != NULL) {
            /* Disconnected */
            DPRINTF(("uhub_explore: device addr=%d disappeared "
                     "on port %d\n", up->device->address, port));
            usb_disconnect_port(up, &sc->sc_dev);
            usbd_clear_port_feature(dev, port,
                                    UHF_C_PORT_CONNECTION);
        }
        if (!(status & UPS_CURRENT_CONNECT_STATUS)) {
            /* Nothing connected, just ignore it. */
            DPRINTFN(3,("uhub_explore: port=%d !CURRENT_CONNECT"
                        "_STATUS\n", port));
            continue;
        }

        /* Connected */

        if (!(status & UPS_PORT_POWER))
            printf("%s: strange, connected port %d has no power\n",
                   sc->sc_dev.dv_xname, port);

        /* Wait for maximum device power up time. */
        usbd_delay_ms(dev, USB_PORT_POWERUP_DELAY);

        /* Reset port, which implies enabling it. */
        if (usbd_reset_port(dev, port, &up->status)) {
            printf("%s: port %d reset failed\n",
                   sc->sc_dev.dv_xname, port);
            continue;
        }
        /* Get port status again, it might have changed during reset */
        err = usbd_get_port_status(dev, port, &up->status);
        if (err) {
            DPRINTF(("uhub_explore: get port status failed, "
                     "error=%s\n", usbd_errstr(err)));
            continue;
        }
        status = UGETW(up->status.wPortStatus);
        change = UGETW(up->status.wPortChange);
        if (!(status & UPS_CURRENT_CONNECT_STATUS)) {
            /* Nothing connected, just ignore it. */
#ifdef UHUB_DEBUG
            printf("%s: port %d, device disappeared after reset\n",
                   sc->sc_dev.dv_xname, port);
#endif
            continue;
        }

        /* Figure out device speed */
        if (status & UPS_HIGH_SPEED)
            speed = USB_SPEED_HIGH;
        else if (status & UPS_LOW_SPEED)
            speed = USB_SPEED_LOW;
        else
            speed = USB_SPEED_FULL;
        /* Get device info and set its address. */
        err = usbd_new_device(&sc->sc_dev, dev->bus,
                              dev->depth + 1, speed, port, up);
        /* XXX retry a few times? */
        if (err) {
            DPRINTFN(-1,("uhub_explore: usbd_new_device failed, "
                         "error=%s\n", usbd_errstr(err)));
            /* Avoid addressing problems by disabling. */
            /* usbd_reset_port(dev, port, &up->status); */

            /*
             * The unit refused to accept a new address, or had
             * some other serious problem.  Since we cannot leave
             * at 0 we have to disable the port instead.
             */
            printf("%s: device problem, disabling port %d\n",
                   sc->sc_dev.dv_xname, port);
            usbd_clear_port_feature(dev, port, UHF_PORT_ENABLE);
        } else {
            /* The port set up succeeded, reset error count. */
            up->restartcnt = 0;

            if (up->device->hub)
                up->device->hub->explore(up->device);
        }
    }
    return (USBD_NORMAL_COMPLETION);
}
예제 #20
0
static void
usie_if_status_cb(void *arg, int pending)
{
    struct usie_softc *sc = arg;
    struct ifnet *ifp = sc->sc_ifp;
    struct usb_device_request req;
    struct usie_hip *hip;
    struct usie_lsi *lsi;
    uint16_t actlen;
    uint8_t ntries;
    uint8_t pad;

    mtx_lock(&sc->sc_mtx);

    req.bmRequestType = UT_READ_CLASS_INTERFACE;
    req.bRequest = UCDC_GET_ENCAPSULATED_RESPONSE;
    USETW(req.wValue, 0);
    USETW(req.wIndex, sc->sc_if_ifnum);
    USETW(req.wLength, sizeof(sc->sc_status_temp));

    for (ntries = 0; ntries != 10; ntries++) {
        int err;

        err = usbd_do_request_flags(sc->sc_udev,
                                    &sc->sc_mtx, &req, sc->sc_status_temp, USB_SHORT_XFER_OK,
                                    &actlen, USB_DEFAULT_TIMEOUT);

        if (err == 0)
            break;

        DPRINTF("Control request failed: %s %d/10\n",
                usbd_errstr(err), ntries);

        usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(10));
    }

    if (ntries == 10) {
        mtx_unlock(&sc->sc_mtx);
        DPRINTF("Timeout\n");
        return;
    }

    hip = (struct usie_hip *)sc->sc_status_temp;

    pad = (hip->id & USIE_HIP_PAD) ? 1 : 0;

    DPRINTF("hip.id=%x hip.len=%d actlen=%u pad=%d\n",
            hip->id, be16toh(hip->len), actlen, pad);

    switch (hip->id & USIE_HIP_MASK) {
    case USIE_HIP_SYNC2H:
        usie_if_cmd(sc, USIE_HIP_SYNC2M);
        break;
    case USIE_HIP_RESTR:
        usb_callout_stop(&sc->sc_if_sync_ch);
        break;
    case USIE_HIP_UMTS:
        lsi = (struct usie_lsi *)(
                  sc->sc_status_temp + sizeof(struct usie_hip) + pad);

        DPRINTF("lsi.proto=%x lsi.len=%d\n", lsi->proto,
                be16toh(lsi->len));

        if (lsi->proto != USIE_LSI_UMTS)
            break;

        if (lsi->area == USIE_LSI_AREA_NO ||
                lsi->area == USIE_LSI_AREA_NODATA) {
            device_printf(sc->sc_dev, "no service available\n");
            break;
        }
        if (lsi->state == USIE_LSI_STATE_IDLE) {
            DPRINTF("lsi.state=%x\n", lsi->state);
            break;
        }
        DPRINTF("ctx=%x\n", hip->param);
        sc->sc_txd.hip.param = hip->param;

        sc->sc_net.addr_len = lsi->pdp_addr_len;
        memcpy(&sc->sc_net.dns1_addr, &lsi->dns1_addr, 16);
        memcpy(&sc->sc_net.dns2_addr, &lsi->dns2_addr, 16);
        memcpy(sc->sc_net.pdp_addr, lsi->pdp_addr, 16);
        memcpy(sc->sc_net.gw_addr, lsi->gw_addr, 16);
        ifp->if_flags |= IFF_UP;
        ifp->if_drv_flags |= IFF_DRV_RUNNING;

        device_printf(sc->sc_dev, "IP Addr=%d.%d.%d.%d\n",
                      *lsi->pdp_addr, *(lsi->pdp_addr + 1),
                      *(lsi->pdp_addr + 2), *(lsi->pdp_addr + 3));
        device_printf(sc->sc_dev, "Gateway Addr=%d.%d.%d.%d\n",
                      *lsi->gw_addr, *(lsi->gw_addr + 1),
                      *(lsi->gw_addr + 2), *(lsi->gw_addr + 3));
        device_printf(sc->sc_dev, "Prim NS Addr=%d.%d.%d.%d\n",
                      *lsi->dns1_addr, *(lsi->dns1_addr + 1),
                      *(lsi->dns1_addr + 2), *(lsi->dns1_addr + 3));
        device_printf(sc->sc_dev, "Scnd NS Addr=%d.%d.%d.%d\n",
                      *lsi->dns2_addr, *(lsi->dns2_addr + 1),
                      *(lsi->dns2_addr + 2), *(lsi->dns2_addr + 3));

        usie_cns_req(sc, USIE_CNS_ID_RSSI, USIE_CNS_OB_RSSI);
        break;

    case USIE_HIP_RCGI:
        /* ignore, workaround for sloppy windows */
        break;
    default:
        DPRINTF("undefined msgid: %x\n", hip->id);
        break;
    }

    mtx_unlock(&sc->sc_mtx);
}
예제 #21
0
static void
ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
{
	struct ucom_softc *sc = (struct ucom_softc *)p;
	struct tty *tp = sc->sc_tty;
	struct ucom_buffer *ub;
	u_int32_t cc;
	u_char *cp;
	int s;

	ub = SIMPLEQ_FIRST(&sc->sc_ibuff_empty);
	SIMPLEQ_REMOVE_HEAD(&sc->sc_ibuff_empty, ub_link);

	if (status == USBD_CANCELLED || status == USBD_IOERROR ||
	    sc->sc_dying) {
		DPRINTF(("ucomreadcb: dying\n"));
		ub->ub_index = ub->ub_len = 0;
		/* Send something to wake upper layer */
		s = spltty();
		if (status != USBD_CANCELLED) {
			(tp->t_linesw->l_rint)('\n', tp);
			mutex_spin_enter(&tty_lock);	/* XXX */
			ttwakeup(tp);
			mutex_spin_exit(&tty_lock);	/* XXX */
		}
		splx(s);
		return;
	}

	if (status == USBD_STALLED) {
		usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
		ucomsubmitread(sc, ub);
		return;
	}

	if (status != USBD_NORMAL_COMPLETION) {
		printf("ucomreadcb: wonky status=%s\n", usbd_errstr(status));
		return;
	}

	usbd_get_xfer_status(xfer, NULL, (void *)&cp, &cc, NULL);

#ifdef UCOM_DEBUG
	/* This is triggered by uslsa(4) occasionally. */
	if ((ucomdebug > 0) && (cc == 0)) {
		device_printf(sc->sc_dev, "ucomreadcb: zero length xfer!\n");
	}
#endif

	KDASSERT(cp == ub->ub_data);

	rnd_add_uint32(&sc->sc_rndsource, cc);

	if (sc->sc_opening) {
		ucomsubmitread(sc, ub);
		return;
	}

	if (sc->sc_methods->ucom_read != NULL) {
		sc->sc_methods->ucom_read(sc->sc_parent, sc->sc_portno,
		    &cp, &cc);
		ub->ub_index = (u_int)(cp - ub->ub_data);
	} else
		ub->ub_index = 0;

	ub->ub_len = cc;

	SIMPLEQ_INSERT_TAIL(&sc->sc_ibuff_full, ub, ub_link);

	ucom_read_complete(sc);
}
예제 #22
0
static int
usie_attach(device_t self)
{
    struct usie_softc *sc = device_get_softc(self);
    struct usb_attach_arg *uaa = device_get_ivars(self);
    struct ifnet *ifp;
    struct usb_interface *iface;
    struct usb_interface_descriptor *id;
    struct usb_device_request req;
    int err;
    uint16_t fwattr;
    uint8_t iface_index;
    uint8_t ifidx;
    uint8_t start;

    device_set_usb_desc(self);
    sc->sc_udev = uaa->device;
    sc->sc_dev = self;

    mtx_init(&sc->sc_mtx, "usie", MTX_NETWORK_LOCK, MTX_DEF);
    ucom_ref(&sc->sc_super_ucom);

    TASK_INIT(&sc->sc_if_status_task, 0, usie_if_status_cb, sc);
    TASK_INIT(&sc->sc_if_sync_task, 0, usie_if_sync_cb, sc);

    usb_callout_init_mtx(&sc->sc_if_sync_ch, &sc->sc_mtx, 0);

    mtx_lock(&sc->sc_mtx);

    /* set power mode to D0 */
    req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
    req.bRequest = USIE_POWER;
    USETW(req.wValue, 0);
    USETW(req.wIndex, 0);
    USETW(req.wLength, 0);
    if (usie_do_request(sc, &req, NULL)) {
        mtx_unlock(&sc->sc_mtx);
        goto detach;
    }
    /* read fw attr */
    fwattr = 0;
    req.bmRequestType = UT_READ_VENDOR_DEVICE;
    req.bRequest = USIE_FW_ATTR;
    USETW(req.wValue, 0);
    USETW(req.wIndex, 0);
    USETW(req.wLength, sizeof(fwattr));
    if (usie_do_request(sc, &req, &fwattr)) {
        mtx_unlock(&sc->sc_mtx);
        goto detach;
    }
    mtx_unlock(&sc->sc_mtx);

    /* check DHCP supports */
    DPRINTF("fwattr=%x\n", fwattr);
    if (!(fwattr & USIE_FW_DHCP)) {
        device_printf(self, "DHCP is not supported. A firmware upgrade might be needed.\n");
    }

    /* find available interfaces */
    sc->sc_nucom = 0;
    for (ifidx = 0; ifidx < USIE_IFACE_MAX; ifidx++) {
        iface = usbd_get_iface(uaa->device, ifidx);
        if (iface == NULL)
            break;

        id = usbd_get_interface_descriptor(iface);
        if ((id == NULL) || (id->bInterfaceClass != UICLASS_VENDOR))
            continue;

        /* setup Direct IP transfer */
        if (id->bInterfaceNumber >= 7 && id->bNumEndpoints == 3) {
            sc->sc_if_ifnum = id->bInterfaceNumber;
            iface_index = ifidx;

            DPRINTF("ifnum=%d, ifidx=%d\n",
                    sc->sc_if_ifnum, ifidx);

            err = usbd_transfer_setup(uaa->device,
                                      &iface_index, sc->sc_if_xfer, usie_if_config,
                                      USIE_IF_N_XFER, sc, &sc->sc_mtx);

            if (err == 0)
                continue;

            device_printf(self,
                          "could not allocate USB transfers on "
                          "iface_index=%d, err=%s\n",
                          iface_index, usbd_errstr(err));
            goto detach;
        }

        /* setup ucom */
        if (sc->sc_nucom >= USIE_UCOM_MAX)
            continue;

        usbd_set_parent_iface(uaa->device, ifidx,
                              uaa->info.bIfaceIndex);

        DPRINTF("NumEndpoints=%d bInterfaceNumber=%d\n",
                id->bNumEndpoints, id->bInterfaceNumber);

        if (id->bNumEndpoints == 2) {
            sc->sc_uc_xfer[sc->sc_nucom][0] = NULL;
            start = 1;
        } else
            start = 0;

        err = usbd_transfer_setup(uaa->device, &ifidx,
                                  sc->sc_uc_xfer[sc->sc_nucom] + start,
                                  usie_uc_config + start, USIE_UC_N_XFER - start,
                                  &sc->sc_ucom[sc->sc_nucom], &sc->sc_mtx);

        if (err != 0) {
            DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(err));
            continue;
        }

        mtx_lock(&sc->sc_mtx);
        for (; start < USIE_UC_N_XFER; start++)
            usbd_xfer_set_stall(sc->sc_uc_xfer[sc->sc_nucom][start]);
        mtx_unlock(&sc->sc_mtx);

        sc->sc_uc_ifnum[sc->sc_nucom] = id->bInterfaceNumber;

        sc->sc_nucom++;		/* found a port */
    }

    if (sc->sc_nucom == 0) {
        device_printf(self, "no comports found\n");
        goto detach;
    }

    err = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom,
                      sc->sc_nucom, sc, &usie_uc_callback, &sc->sc_mtx);

    if (err != 0) {
        DPRINTF("ucom_attach failed\n");
        goto detach;
    }
    DPRINTF("Found %d interfaces.\n", sc->sc_nucom);

    /* setup ifnet (Direct IP) */
    sc->sc_ifp = ifp = if_alloc(IFT_OTHER);

    if (ifp == NULL) {
        device_printf(self, "Could not allocate a network interface\n");
        goto detach;
    }
    if_initname(ifp, "usie", device_get_unit(self));

    ifp->if_softc = sc;
    ifp->if_mtu = USIE_MTU_MAX;
    ifp->if_flags |= IFF_NOARP;
    ifp->if_init = usie_if_init;
    ifp->if_ioctl = usie_if_ioctl;
    ifp->if_start = usie_if_start;
    ifp->if_output = usie_if_output;
    IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
    ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
    IFQ_SET_READY(&ifp->if_snd);

    if_attach(ifp);
    bpfattach(ifp, DLT_RAW, 0);

    if (fwattr & USIE_PM_AUTO) {
        usbd_set_power_mode(uaa->device, USB_POWER_MODE_SAVE);
        DPRINTF("enabling automatic suspend and resume\n");
    } else {
        usbd_set_power_mode(uaa->device, USB_POWER_MODE_ON);
        DPRINTF("USB power is always ON\n");
    }

    DPRINTF("device attached\n");
    return (0);

detach:
    usie_detach(self);
    return (ENOMEM);
}
예제 #23
0
static void
ubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error)
{
	struct ubt_softc		*sc = usbd_xfer_softc(xfer);
	struct usb_device_request	req;
	struct mbuf			*m;
	struct usb_page_cache		*pc;
	int				actlen;

	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);

	switch (USB_GET_STATE(xfer)) {
	case USB_ST_TRANSFERRED:
		UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen);
		UBT_STAT_BYTES_SENT(sc, actlen);
		UBT_STAT_PCKTS_SENT(sc);
		/* FALLTHROUGH */

	case USB_ST_SETUP:
send_next:
		/* Get next command mbuf, if any */
		UBT_NG_LOCK(sc);
		NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
		UBT_NG_UNLOCK(sc);

		if (m == NULL) {
			UBT_INFO(sc, "HCI command queue is empty\n");
			break;	/* transfer complete */
		}

		/* Initialize a USB control request and then schedule it */
		bzero(&req, sizeof(req));
		req.bmRequestType = UBT_HCI_REQUEST;
		USETW(req.wLength, m->m_pkthdr.len);

		UBT_INFO(sc, "Sending control request, " \
			"bmRequestType=0x%02x, wLength=%d\n",
			req.bmRequestType, UGETW(req.wLength));

		pc = usbd_xfer_get_frame(xfer, 0);
		usbd_copy_in(pc, 0, &req, sizeof(req));
		pc = usbd_xfer_get_frame(xfer, 1);
		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);

		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
		usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len);
		usbd_xfer_set_frames(xfer, 2);

		NG_FREE_M(m);

		usbd_transfer_submit(xfer);
		break;

	default: /* Error */
		if (error != USB_ERR_CANCELLED) {
			UBT_WARN(sc, "control transfer failed: %s\n",
				usbd_errstr(error));

			UBT_STAT_OERROR(sc);
			goto send_next;
		}

		/* transfer cancelled */
		break;
	}
} /* ubt_ctrl_write_callback */
예제 #24
0
static void
usie_if_rx_callback(struct usb_xfer *xfer, usb_error_t error)
{
    struct usie_softc *sc = usbd_xfer_softc(xfer);
    struct ifnet *ifp = sc->sc_ifp;
    struct mbuf *m0;
    struct mbuf *m = NULL;
    struct usie_desc *rxd;
    uint32_t actlen;
    uint16_t err;
    uint16_t pkt;
    uint16_t ipl;
    uint16_t len;
    uint16_t diff;
    uint8_t pad;
    uint8_t ipv;

    usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);

    switch (USB_GET_STATE(xfer)) {
    case USB_ST_TRANSFERRED:
        DPRINTFN(15, "rx done, actlen=%u\n", actlen);

        if (actlen < sizeof(struct usie_hip)) {
            DPRINTF("data too short %u\n", actlen);
            goto tr_setup;
        }
        m = sc->sc_rxm;
        sc->sc_rxm = NULL;

    /* fall though */
    case USB_ST_SETUP:
tr_setup:

        if (sc->sc_rxm == NULL) {
            sc->sc_rxm = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR,
                                  MJUMPAGESIZE /* could be bigger than MCLBYTES */ );
        }
        if (sc->sc_rxm == NULL) {
            DPRINTF("could not allocate Rx mbuf\n");
            if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
            usbd_xfer_set_stall(xfer);
            usbd_xfer_set_frames(xfer, 0);
        } else {
            /*
             * Directly loading a mbuf cluster into DMA to
             * save some data copying. This works because
             * there is only one cluster.
             */
            usbd_xfer_set_frame_data(xfer, 0,
                                     mtod(sc->sc_rxm, caddr_t), MIN(MJUMPAGESIZE, USIE_RXSZ_MAX));
            usbd_xfer_set_frames(xfer, 1);
        }
        usbd_transfer_submit(xfer);
        break;

    default:			/* Error */
        DPRINTF("USB transfer error, %s\n", usbd_errstr(error));

        if (error != USB_ERR_CANCELLED) {
            /* try to clear stall first */
            usbd_xfer_set_stall(xfer);
            if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
            goto tr_setup;
        }
        if (sc->sc_rxm != NULL) {
            m_freem(sc->sc_rxm);
            sc->sc_rxm = NULL;
        }
        break;
    }

    if (m == NULL)
        return;

    mtx_unlock(&sc->sc_mtx);

    m->m_pkthdr.len = m->m_len = actlen;

    err = pkt = 0;

    /* HW can aggregate multiple frames in a single USB xfer */
    for (;;) {
        rxd = mtod(m, struct usie_desc *);

        len = be16toh(rxd->hip.len) & USIE_HIP_IP_LEN_MASK;
        pad = (rxd->hip.id & USIE_HIP_PAD) ? 1 : 0;
        ipl = (len - pad - ETHER_HDR_LEN);
        if (ipl >= len) {
            DPRINTF("Corrupt frame\n");
            m_freem(m);
            break;
        }
        diff = sizeof(struct usie_desc) + ipl + pad;

        if (((rxd->hip.id & USIE_HIP_MASK) != USIE_HIP_IP) ||
                (be16toh(rxd->desc_type) & USIE_TYPE_MASK) != USIE_IP_RX) {
            DPRINTF("received wrong type of packet\n");
            m->m_data += diff;
            m->m_pkthdr.len = (m->m_len -= diff);
            err++;
            if (m->m_pkthdr.len > 0)
                continue;
            m_freem(m);
            break;
        }
        switch (be16toh(rxd->ethhdr.ether_type)) {
        case ETHERTYPE_IP:
            ipv = NETISR_IP;
            break;
#ifdef INET6
        case ETHERTYPE_IPV6:
            ipv = NETISR_IPV6;
            break;
#endif
        default:
            DPRINTF("unsupported ether type\n");
            err++;
            break;
        }

        /* the last packet */
        if (m->m_pkthdr.len <= diff) {
            m->m_data += (sizeof(struct usie_desc) + pad);
            m->m_pkthdr.len = m->m_len = ipl;
            m->m_pkthdr.rcvif = ifp;
            BPF_MTAP(sc->sc_ifp, m);
            netisr_dispatch(ipv, m);
            break;
        }
        /* copy aggregated frames to another mbuf */
        m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
        if (__predict_false(m0 == NULL)) {
            DPRINTF("could not allocate mbuf\n");
            err++;
            m_freem(m);
            break;
        }
        m_copydata(m, sizeof(struct usie_desc) + pad, ipl, mtod(m0, caddr_t));
        m0->m_pkthdr.rcvif = ifp;
        m0->m_pkthdr.len = m0->m_len = ipl;

        BPF_MTAP(sc->sc_ifp, m0);
        netisr_dispatch(ipv, m0);

        m->m_data += diff;
        m->m_pkthdr.len = (m->m_len -= diff);
    }

    mtx_lock(&sc->sc_mtx);

    if_inc_counter(ifp, IFCOUNTER_IERRORS, err);
    if_inc_counter(ifp, IFCOUNTER_IPACKETS, pkt);
}
예제 #25
0
static void
ubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
{
	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
	struct mbuf		*m;
	struct usb_page_cache	*pc;
	int			actlen;

	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);

	switch (USB_GET_STATE(xfer)) {
	case USB_ST_TRANSFERRED:
		UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen);
		UBT_STAT_BYTES_SENT(sc, actlen);
		UBT_STAT_PCKTS_SENT(sc);
		/* FALLTHROUGH */

	case USB_ST_SETUP:
send_next:
		/* Get next mbuf, if any */
		UBT_NG_LOCK(sc);
		NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
		UBT_NG_UNLOCK(sc);

		if (m == NULL) {
			UBT_INFO(sc, "ACL data queue is empty\n");
			break; /* transfer completed */
		}

		/*
		 * Copy ACL data frame back to a linear USB transfer buffer
		 * and schedule transfer
		 */

		pc = usbd_xfer_get_frame(xfer, 0);
		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
		usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len);

		UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
			m->m_pkthdr.len);

		NG_FREE_M(m);

		usbd_transfer_submit(xfer);
		break;

	default: /* Error */
		if (error != USB_ERR_CANCELLED) {
			UBT_WARN(sc, "bulk-out transfer failed: %s\n",
				usbd_errstr(error));

			UBT_STAT_OERROR(sc);

			/* try to clear stall first */
			usbd_xfer_set_stall(xfer);
			goto send_next;
		}
			/* transfer cancelled */
		break;
	}
} /* ubt_bulk_write_callback */
예제 #26
0
static void
usie_if_tx_callback(struct usb_xfer *xfer, usb_error_t error)
{
    struct usie_softc *sc = usbd_xfer_softc(xfer);
    struct usb_page_cache *pc;
    struct ifnet *ifp = sc->sc_ifp;
    struct mbuf *m;
    uint16_t size;

    switch (USB_GET_STATE(xfer)) {
    case USB_ST_TRANSFERRED:
        DPRINTFN(11, "transfer complete\n");
        ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
        if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);

    /* fall though */
    case USB_ST_SETUP:
tr_setup:

        if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
            break;

        IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
        if (m == NULL)
            break;

        if (m->m_pkthdr.len > (int)(MCLBYTES - ETHER_HDR_LEN +
                                    ETHER_CRC_LEN - sizeof(sc->sc_txd))) {
            DPRINTF("packet len is too big: %d\n",
                    m->m_pkthdr.len);
            break;
        }
        pc = usbd_xfer_get_frame(xfer, 0);

        sc->sc_txd.hip.len = htobe16(m->m_pkthdr.len +
                                     ETHER_HDR_LEN + ETHER_CRC_LEN);
        size = sizeof(sc->sc_txd);

        usbd_copy_in(pc, 0, &sc->sc_txd, size);
        usbd_m_copy_in(pc, size, m, 0, m->m_pkthdr.len);
        usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len +
                                size + ETHER_CRC_LEN);

        BPF_MTAP(ifp, m);

        m_freem(m);

        usbd_transfer_submit(xfer);
        break;

    default:			/* Error */
        DPRINTF("USB transfer error, %s\n",
                usbd_errstr(error));
        if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);

        if (error != USB_ERR_CANCELLED) {
            usbd_xfer_set_stall(xfer);
            if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
            goto tr_setup;
        }
        break;
    }
}
예제 #27
0
파일: uhid.c 프로젝트: vkhromov/freebsd
static int
uhid_attach(device_t dev)
{
	struct usb_attach_arg *uaa = device_get_ivars(dev);
	struct uhid_softc *sc = device_get_softc(dev);
	int unit = device_get_unit(dev);
	int error = 0;

	DPRINTFN(10, "sc=%p\n", sc);

	device_set_usb_desc(dev);

	mtx_init(&sc->sc_mtx, "uhid lock", NULL, MTX_DEF | MTX_RECURSE);

	sc->sc_udev = uaa->device;

	sc->sc_iface_no = uaa->info.bIfaceNum;
	sc->sc_iface_index = uaa->info.bIfaceIndex;

	error = usbd_transfer_setup(uaa->device,
	    &uaa->info.bIfaceIndex, sc->sc_xfer, uhid_config,
	    UHID_N_TRANSFER, sc, &sc->sc_mtx);

	if (error) {
		DPRINTF("error=%s\n", usbd_errstr(error));
		goto detach;
	}
	if (uaa->info.idVendor == USB_VENDOR_WACOM) {

		/* the report descriptor for the Wacom Graphire is broken */

		if (uaa->info.idProduct == USB_PRODUCT_WACOM_GRAPHIRE) {

			sc->sc_repdesc_size = sizeof(uhid_graphire_report_descr);
			sc->sc_repdesc_ptr = (void *)&uhid_graphire_report_descr;
			sc->sc_flags |= UHID_FLAG_STATIC_DESC;

		} else if (uaa->info.idProduct == USB_PRODUCT_WACOM_GRAPHIRE3_4X5) {

			static uint8_t reportbuf[] = {2, 2, 2};

			/*
			 * The Graphire3 needs 0x0202 to be written to
			 * feature report ID 2 before it'll start
			 * returning digitizer data.
			 */
			error = usbd_req_set_report(uaa->device, NULL,
			    reportbuf, sizeof(reportbuf),
			    uaa->info.bIfaceIndex, UHID_FEATURE_REPORT, 2);

			if (error) {
				DPRINTF("set report failed, error=%s (ignored)\n",
				    usbd_errstr(error));
			}
			sc->sc_repdesc_size = sizeof(uhid_graphire3_4x5_report_descr);
			sc->sc_repdesc_ptr = (void *)&uhid_graphire3_4x5_report_descr;
			sc->sc_flags |= UHID_FLAG_STATIC_DESC;
		}
	} else if ((uaa->info.bInterfaceClass == UICLASS_VENDOR) &&
		    (uaa->info.bInterfaceSubClass == UISUBCLASS_XBOX360_CONTROLLER) &&
	    (uaa->info.bInterfaceProtocol == UIPROTO_XBOX360_GAMEPAD)) {

		/* the Xbox 360 gamepad has no report descriptor */
		sc->sc_repdesc_size = sizeof(uhid_xb360gp_report_descr);
		sc->sc_repdesc_ptr = (void *)&uhid_xb360gp_report_descr;
		sc->sc_flags |= UHID_FLAG_STATIC_DESC;
	}
	if (sc->sc_repdesc_ptr == NULL) {

		error = usbd_req_get_hid_desc(uaa->device, NULL,
		    &sc->sc_repdesc_ptr, &sc->sc_repdesc_size,
		    M_USBDEV, uaa->info.bIfaceIndex);

		if (error) {
			device_printf(dev, "no report descriptor\n");
			goto detach;
		}
	}
	error = usbd_req_set_idle(uaa->device, NULL,
	    uaa->info.bIfaceIndex, 0, 0);

	if (error) {
		DPRINTF("set idle failed, error=%s (ignored)\n",
		    usbd_errstr(error));
	}
	sc->sc_isize = hid_report_size
	    (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_input, &sc->sc_iid);

	sc->sc_osize = hid_report_size
	    (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_output, &sc->sc_oid);

	sc->sc_fsize = hid_report_size
	    (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_feature, &sc->sc_fid);

	if (sc->sc_isize > UHID_BSIZE) {
		DPRINTF("input size is too large, "
		    "%d bytes (truncating)\n",
		    sc->sc_isize);
		sc->sc_isize = UHID_BSIZE;
	}
	if (sc->sc_osize > UHID_BSIZE) {
		DPRINTF("output size is too large, "
		    "%d bytes (truncating)\n",
		    sc->sc_osize);
		sc->sc_osize = UHID_BSIZE;
	}
	if (sc->sc_fsize > UHID_BSIZE) {
		DPRINTF("feature size is too large, "
		    "%d bytes (truncating)\n",
		    sc->sc_fsize);
		sc->sc_fsize = UHID_BSIZE;
	}

	error = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx,
	    &uhid_fifo_methods, &sc->sc_fifo,
	    unit, -1, uaa->info.bIfaceIndex,
	    UID_ROOT, GID_OPERATOR, 0644);
	if (error) {
		goto detach;
	}
	return (0);			/* success */

detach:
	uhid_detach(dev);
	return (ENOMEM);
}
예제 #28
0
파일: ucom.c 프로젝트: MarginC/kame
Static int
ucomopen(dev_t dev, int flag, int mode, usb_proc_ptr p)
{
	int unit = UCOMUNIT(dev);
	struct ucom_softc *sc;
	usbd_status err;
	struct tty *tp;
	int s;
	int error;

	USB_GET_SC_OPEN(ucom, unit, sc);

	if (sc->sc_dying)
		return (ENXIO);

	tp = sc->sc_tty;

	DPRINTF(("%s: ucomopen: tp = %p\n", USBDEVNAME(sc->sc_dev), tp));

	if (ISSET(tp->t_state, TS_ISOPEN) &&
	    ISSET(tp->t_state, TS_XCLUDE) &&
	    suser(p))
		return (EBUSY);

	/*
	 * Do the following iff this is a first open.
	 */
	s = spltty();
	while (sc->sc_opening)
		tsleep(&sc->sc_opening, PRIBIO, "ucomop", 0);
	sc->sc_opening = 1;

	if (!ISSET(tp->t_state, TS_ISOPEN)) {
		struct termios t;

		sc->sc_poll = 0;
		sc->sc_lsr = sc->sc_msr = sc->sc_mcr = 0;

		tp->t_dev = dev;

		/*
		 * Initialize the termios status to the defaults.  Add in the
		 * sticky bits from TIOCSFLAGS.
		 */
		t.c_ispeed = 0;
		t.c_ospeed = TTYDEF_SPEED;
		t.c_cflag = TTYDEF_CFLAG;
		/* Make sure ucomparam() will do something. */
		tp->t_ospeed = 0;
		(void)ucomparam(tp, &t);
		tp->t_iflag = TTYDEF_IFLAG;
		tp->t_oflag = TTYDEF_OFLAG;
		tp->t_lflag = TTYDEF_LFLAG;
		ttychars(tp);
		ttsetwater(tp);

		/*
		 * Turn on DTR.  We must always do this, even if carrier is not
		 * present, because otherwise we'd have to use TIOCSDTR
		 * immediately after setting CLOCAL, which applications do not
		 * expect.  We always assert DTR while the device is open
		 * unless explicitly requested to deassert it.
		 */
		(void)ucomctl(sc, TIOCM_DTR | TIOCM_RTS, DMBIS);

		/* Device specific open */
		if (sc->sc_callback->ucom_open != NULL) {
			error = sc->sc_callback->ucom_open(sc->sc_parent,
							   sc->sc_portno);
			if (error) {
				ucom_cleanup(sc);
				sc->sc_opening = 0;
				wakeup(&sc->sc_opening);
				splx(s);
				return (error);
			}
		}

		DPRINTF(("ucomopen: open pipes in = %d out = %d\n",
			 sc->sc_bulkin_no, sc->sc_bulkout_no));

		/* Open the bulk pipes */
		/* Bulk-in pipe */
		err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no, 0,
				     &sc->sc_bulkin_pipe);
		if (err) {
			printf("%s: open bulk out error (addr %d): %s\n",
			       USBDEVNAME(sc->sc_dev), sc->sc_bulkin_no, 
			       usbd_errstr(err));
			error = EIO;
			goto fail_0;
		}
		/* Bulk-out pipe */
		err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no,
				     USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
		if (err) {
			printf("%s: open bulk in error (addr %d): %s\n",
			       USBDEVNAME(sc->sc_dev), sc->sc_bulkout_no,
			       usbd_errstr(err));
			error = EIO;
			goto fail_1;
		}

		/* Allocate a request and an input buffer and start reading. */
		sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
		if (sc->sc_ixfer == NULL) {
			error = ENOMEM;
			goto fail_2;
		}

		sc->sc_ibuf = usbd_alloc_buffer(sc->sc_ixfer,
						sc->sc_ibufsizepad);
		if (sc->sc_ibuf == NULL) {
			error = ENOMEM;
			goto fail_3;
		}

		sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
		if (sc->sc_oxfer == NULL) {
			error = ENOMEM;
			goto fail_3;
		}

		sc->sc_obuf = usbd_alloc_buffer(sc->sc_oxfer,
						sc->sc_obufsize +
						sc->sc_opkthdrlen);
		if (sc->sc_obuf == NULL) {
			error = ENOMEM;
			goto fail_4;
		}

		/*
		 * Handle initial DCD.
		 */
		if (ISSET(sc->sc_msr, UMSR_DCD) ||
		    (minor(dev) & UCOM_CALLOUT_MASK))
			(*linesw[tp->t_line].l_modem)(tp, 1);

		ucomstartread(sc);
	}

	sc->sc_opening = 0;
	wakeup(&sc->sc_opening);
	splx(s);

	error = ttyopen(dev, tp);
	if (error)
		goto bad;

	error = (*linesw[tp->t_line].l_open)(dev, tp);
	if (error)
		goto bad;

	disc_optim(tp, &tp->t_termios, sc);

	DPRINTF(("%s: ucomopen: success\n", USBDEVNAME(sc->sc_dev)));

	sc->sc_poll = 1;
	sc->sc_refcnt++;

	return (0);

fail_4:
	usbd_free_xfer(sc->sc_oxfer);
	sc->sc_oxfer = NULL;
fail_3:
	usbd_free_xfer(sc->sc_ixfer);
	sc->sc_ixfer = NULL;
fail_2:
	usbd_close_pipe(sc->sc_bulkout_pipe);
	sc->sc_bulkout_pipe = NULL;
fail_1:
	usbd_close_pipe(sc->sc_bulkin_pipe);
	sc->sc_bulkin_pipe = NULL;
fail_0:
	sc->sc_opening = 0;
	wakeup(&sc->sc_opening);
	splx(s);
	return (error);

bad:
	if (!ISSET(tp->t_state, TS_ISOPEN)) {
		/*
		 * We failed to open the device, and nobody else had it opened.
		 * Clean up the state as appropriate.
		 */
		ucom_cleanup(sc);
	}

	DPRINTF(("%s: ucomopen: failed\n", USBDEVNAME(sc->sc_dev)));

	return (error);
}
예제 #29
0
Static void
ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
{
	struct ucom_softc *sc = (struct ucom_softc *)p;
	struct tty *tp = sc->sc_tty;
	int (*rint) (int c, struct tty *tp) = linesw[tp->t_line].l_rint;
	usbd_status err;
	u_int32_t cc;
	u_char *cp;
	int lostcc;
	int s;

	DPRINTF(("ucomreadcb: status = %d\n", status));

	if (status != USBD_NORMAL_COMPLETION) {
		if (!(sc->sc_state & UCS_RXSTOP))
			printf("%s: ucomreadcb: %s\n",
			       USBDEVNAME(sc->sc_dev), usbd_errstr(status));
		if (status == USBD_STALLED)
			usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
		/* XXX we should restart after some delay. */
		return;
	}

	usbd_get_xfer_status(xfer, NULL, (void **)&cp, &cc, NULL);
	DPRINTF(("ucomreadcb: got %d chars, tp = %p\n", cc, tp));
	if (cc == 0)
		goto resubmit;

	if (sc->sc_callback->ucom_read != NULL)
		sc->sc_callback->ucom_read(sc->sc_parent, sc->sc_portno,
					   &cp, &cc);

	if (cc > sc->sc_ibufsize) {
		printf("%s: invalid receive data size, %d chars\n",
		       USBDEVNAME(sc->sc_dev), cc);
		goto resubmit;
	}
	if (cc < 1)
		goto resubmit;

	s = spltty();
	if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
		if (tp->t_rawq.c_cc + cc > tp->t_ihiwat
		    && (sc->sc_state & UCS_RTS_IFLOW
			|| tp->t_iflag & IXOFF)
		    && !(tp->t_state & TS_TBLOCK))
			ttyblock(tp);
		lostcc = b_to_q((char *)cp, cc, &tp->t_rawq);
		tp->t_rawcc += cc;
		ttwakeup(tp);
		if (tp->t_state & TS_TTSTOP
		    && (tp->t_iflag & IXANY
			|| tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
			tp->t_state &= ~TS_TTSTOP;
			tp->t_lflag &= ~FLUSHO;
			ucomstart(tp);
		}
		if (lostcc > 0)
			printf("%s: lost %d chars\n", USBDEVNAME(sc->sc_dev),
			       lostcc);
	} else {
		/* Give characters to tty layer. */
		while (cc > 0) {
			DPRINTFN(7, ("ucomreadcb: char = 0x%02x\n", *cp));
			if ((*rint)(*cp, tp) == -1) {
				/* XXX what should we do? */
				printf("%s: lost %d chars\n",
				       USBDEVNAME(sc->sc_dev), cc);
				break;
			}
			cc--;
			cp++;
		}
	}
	splx(s);

    resubmit:
	err = ucomstartread(sc);
	if (err) {
		printf("%s: read start failed\n", USBDEVNAME(sc->sc_dev));
		/* XXX what should we dow now? */
	}

	if ((sc->sc_state & UCS_RTS_IFLOW) && !ISSET(sc->sc_mcr, UMCR_RTS)
	    && !(tp->t_state & TS_TBLOCK))
		ucomctl(sc, UMCR_RTS, DMBIS);
}
예제 #30
0
static int
ubt_attach(device_t self)
{
	struct ubt_softc *sc = device_get_softc(self);
	struct usb_attach_arg *uaa = device_get_ivars(self);

	usb_config_descriptor_t *cd;
	usb_endpoint_descriptor_t *ed;
	int err;
	uint8_t count, i;

	DPRINTFN(50, "ubt_attach: sc=%p\n", sc);

	sc->sc_udev = uaa->device;
	sc->sc_dev = self;

	/*
	 * Move the device into the configured state
	 */
	err = usbd_set_config_index(sc->sc_udev, 0, 1);
	if (err) {
		kprintf("%s: failed to set configuration idx 0: %s\n",
		    device_get_nameunit(sc->sc_dev), usbd_errstr(err));

		return ENXIO;
	}

	/*
	 * Interface 0 must have 3 endpoints
	 *	1) Interrupt endpoint to receive HCI events
	 *	2) Bulk IN endpoint to receive ACL data
	 *	3) Bulk OUT endpoint to send ACL data
	 */
	err = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface0);
	if (err) {
		kprintf("%s: Could not get interface 0 handle %s (%d)\n",
				device_get_nameunit(sc->sc_dev), usbd_errstr(err), err);

		return ENXIO;
	}

	sc->sc_evt_addr = -1;
	sc->sc_aclrd_addr = -1;
	sc->sc_aclwr_addr = -1;

	count = 0;
	(void)usbd_endpoint_count(sc->sc_iface0, &count);

	for (i = 0 ; i < count ; i++) {
		int dir, type;

		ed = usbd_interface2endpoint_descriptor(sc->sc_iface0, i);
		if (ed == NULL) {
			kprintf("%s: could not read endpoint descriptor %d\n",
			    device_get_nameunit(sc->sc_dev), i);

			return ENXIO;
		}

		dir = UE_GET_DIR(ed->bEndpointAddress);
		type = UE_GET_XFERTYPE(ed->bmAttributes);

		if (dir == UE_DIR_IN && type == UE_INTERRUPT)
			sc->sc_evt_addr = ed->bEndpointAddress;
		else if (dir == UE_DIR_IN && type == UE_BULK)
			sc->sc_aclrd_addr = ed->bEndpointAddress;
		else if (dir == UE_DIR_OUT && type == UE_BULK)
			sc->sc_aclwr_addr = ed->bEndpointAddress;
	}

	if (sc->sc_evt_addr == -1) {
		kprintf("%s: missing INTERRUPT endpoint on interface 0\n",
				device_get_nameunit(sc->sc_dev));

		return ENXIO;
	}
	if (sc->sc_aclrd_addr == -1) {
		kprintf("%s: missing BULK IN endpoint on interface 0\n",
				device_get_nameunit(sc->sc_dev));

		return ENXIO;
	}
	if (sc->sc_aclwr_addr == -1) {
		kprintf("%s: missing BULK OUT endpoint on interface 0\n",
				device_get_nameunit(sc->sc_dev));

		return ENXIO;
	}

	/*
	 * Interface 1 must have 2 endpoints
	 *	1) Isochronous IN endpoint to receive SCO data
	 *	2) Isochronous OUT endpoint to send SCO data
	 *
	 * and will have several configurations, which can be selected
	 * via a sysctl variable. We select config 0 to start, which
	 * means that no SCO data will be available.
	 */
	err = usbd_device2interface_handle(sc->sc_udev, 1, &sc->sc_iface1);
	if (err) {
		kprintf("%s: Could not get interface 1 handle %s (%d)\n",
		    device_get_nameunit(sc->sc_dev), usbd_errstr(err), err);

		return ENXIO;
	}

	cd = usbd_get_config_descriptor(sc->sc_udev);
	if (cd == NULL) {
		kprintf("%s: could not get config descriptor\n",
			device_get_nameunit(sc->sc_dev));

		return ENXIO;
	}

	sc->sc_alt_config = usbd_get_no_alts(cd, 1);

	/* set initial config */
	err = ubt_set_isoc_config(sc);
	if (err) {
		kprintf("%s: ISOC config failed\n",
			device_get_nameunit(sc->sc_dev));

		return ENXIO;
	}

	/* Attach HCI */
	sc->sc_unit = hci_attach(&ubt_hci, sc->sc_dev, 0);

	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
			   sc->sc_dev);

	sc->sc_ok = 1;

	sysctl_ctx_init(&sc->sysctl_ctx);
	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
					  SYSCTL_STATIC_CHILDREN(_hw),
					  OID_AUTO,
					  device_get_nameunit(sc->sc_dev),
					  CTLFLAG_RD, 0, "");

	if (sc->sysctl_tree == NULL) {
		/* Failure isn't fatal */
		device_printf(sc->sc_dev, "Unable to create sysctl tree\n");
		return 0;
	}

	SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
			OID_AUTO, "config", CTLTYPE_INT|CTLFLAG_RW, (void *)sc,
			0, ubt_sysctl_config, "I", "Configuration number");
	SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
		       OID_AUTO, "alt_config", CTLFLAG_RD, &sc->sc_alt_config,
		       0, "Number of alternate configurations");
	SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
		       OID_AUTO, "sco_rxsize", CTLFLAG_RD, &sc->sc_scord_size,
		       0, "Max SCO receive size");
	SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
		       OID_AUTO, "sco_wrsize", CTLFLAG_RD, &sc->sc_scowr_size,
		       0, "Max SCO transmit size");

	return 0;
}