/*------------------------------------------------------------------------*
 *	ugen_get_iface_driver
 *
 * This function generates an USB interface description for userland.
 *
 * Returns:
 *    0: Success
 * Else: Failure
 *------------------------------------------------------------------------*/
static int
ugen_get_iface_driver(struct usb_fifo *f, struct usb_gen_descriptor *ugd)
{
	struct usb_device *udev = f->udev;
	struct usb_interface *iface;
	const char *ptr;
	const char *desc;
	unsigned int len;
	unsigned int maxlen;
	char buf[128];
	int error;

	DPRINTFN(6, "\n");

	if ((ugd->ugd_data == NULL) || (ugd->ugd_maxlen == 0)) {
		/* userland pointer should not be zero */
		return (EINVAL);
	}

	iface = usbd_get_iface(udev, ugd->ugd_iface_index);
	if ((iface == NULL) || (iface->idesc == NULL)) {
		/* invalid interface index */
		return (EINVAL);
	}

	/* read out device nameunit string, if any */
	if ((iface->subdev != NULL) &&
	    device_is_attached(iface->subdev) &&
	    (ptr = device_get_nameunit(iface->subdev)) &&
	    (desc = device_get_desc(iface->subdev))) {

		/* print description */
		snprintf(buf, sizeof(buf), "%s: <%s>", ptr, desc);

		/* range checks */
		maxlen = ugd->ugd_maxlen - 1;
		len = strlen(buf);
		if (len > maxlen)
			len = maxlen;

		/* update actual length, including terminating zero */
		ugd->ugd_actlen = len + 1;

		/* copy out interface description */
		error = copyout(buf, ugd->ugd_data, ugd->ugd_actlen);
	} else {
		/* zero length string is default */
		error = copyout("", ugd->ugd_data, 1);
	}
	return (error);
}
static int
ugen_get_iface_desc(struct usb_fifo *f,
    struct usb_interface_descriptor *idesc)
{
	struct usb_interface *iface;

	iface = usbd_get_iface(f->udev, f->iface_index);
	if (iface && iface->idesc) {
		*idesc = *(iface->idesc);
	} else {
		return (EIO);
	}
	return (0);
}
예제 #3
0
static void
usie_autoinst(void *arg, struct usb_device *udev,
    struct usb_attach_arg *uaa)
{
	struct usb_interface *iface;
	struct usb_interface_descriptor *id;
	struct usb_device_request req;
	int err;

	if (uaa->dev_state != UAA_DEV_READY)
		return;

	iface = usbd_get_iface(udev, 0);
	if (iface == NULL)
		return;

	id = iface->idesc;
	if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
		return;

	if (usbd_lookup_id_by_uaa(usie_devs, sizeof(usie_devs), uaa) != 0)
		return;			/* no device match */

	if (bootverbose) {
		DPRINTF("Ejecting %s %s\n",
		    usb_get_manufacturer(udev),
		    usb_get_product(udev));
	}
	req.bmRequestType = UT_VENDOR;
	req.bRequest = UR_SET_INTERFACE;
	USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
	USETW(req.wIndex, UHF_PORT_CONNECTION);
	USETW(req.wLength, 0);

	/* at this moment there is no mutex */
	err = usbd_do_request_flags(udev, NULL, &req,
	    NULL, 0, NULL, 250 /* ms */ );

	/* success, mark the udev as disappearing */
	if (err == 0)
		uaa->dev_state = UAA_DEV_EJECTING;
}
예제 #4
0
파일: uhso.c 프로젝트: vkhromov/freebsd
static void
uhso_test_autoinst(void *arg, struct usb_device *udev,
    struct usb_attach_arg *uaa)
{
	struct usb_interface *iface;
	struct usb_interface_descriptor *id;

	if (uaa->dev_state != UAA_DEV_READY || !uhso_autoswitch)
		return;

	iface = usbd_get_iface(udev, 0);
	if (iface == NULL)
		return;
	id = iface->idesc;
	if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
		return;
	if (usbd_lookup_id_by_uaa(uhso_devs, sizeof(uhso_devs), uaa))
		return;		/* no device match */

	if (usb_msc_eject(udev, 0, MSC_EJECT_REZERO) == 0) {
		/* success, mark the udev as disappearing */
		uaa->dev_state = UAA_DEV_EJECTING;
	}
}
static int
ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
{
	union {
		struct usb_interface_descriptor *idesc;
		struct usb_alt_interface *ai;
		struct usb_device_descriptor *ddesc;
		struct usb_config_descriptor *cdesc;
		struct usb_device_stats *stat;
		struct usb_fs_init *pinit;
		struct usb_fs_uninit *puninit;
		uint32_t *ptime;
		void   *addr;
		int    *pint;
	}     u;
	struct usb_device_descriptor *dtemp;
	struct usb_config_descriptor *ctemp;
	struct usb_interface *iface;
	int error = 0;
	uint8_t n;

	u.addr = addr;

	DPRINTFN(6, "cmd=0x%08lx\n", cmd);

	switch (cmd) {
	case USB_DISCOVER:
		usb_needs_explore_all();
		break;

	case USB_SETDEBUG:
		if (!(fflags & FWRITE)) {
			error = EPERM;
			break;
		}
		usb_debug = *(int *)addr;
		break;

	case USB_GET_CONFIG:
		*(int *)addr = f->udev->curr_config_index;
		break;

	case USB_SET_CONFIG:
		if (!(fflags & FWRITE)) {
			error = EPERM;
			break;
		}
		error = ugen_set_config(f, *(int *)addr);
		break;

	case USB_GET_ALTINTERFACE:
		iface = usbd_get_iface(f->udev,
		    u.ai->uai_interface_index);
		if (iface && iface->idesc) {
			u.ai->uai_alt_index = iface->alt_index;
		} else {
			error = EINVAL;
		}
		break;

	case USB_SET_ALTINTERFACE:
		if (!(fflags & FWRITE)) {
			error = EPERM;
			break;
		}
		error = ugen_set_interface(f,
		    u.ai->uai_interface_index, u.ai->uai_alt_index);
		break;

	case USB_GET_DEVICE_DESC:
		dtemp = usbd_get_device_descriptor(f->udev);
		if (!dtemp) {
			error = EIO;
			break;
		}
		*u.ddesc = *dtemp;
		break;

	case USB_GET_CONFIG_DESC:
		ctemp = usbd_get_config_descriptor(f->udev);
		if (!ctemp) {
			error = EIO;
			break;
		}
		*u.cdesc = *ctemp;
		break;

	case USB_GET_FULL_DESC:
		error = ugen_get_cdesc(f, addr);
		break;

	case USB_GET_STRING_DESC:
		error = ugen_get_sdesc(f, addr);
		break;

	case USB_GET_IFACE_DRIVER:
		error = ugen_get_iface_driver(f, addr);
		break;

	case USB_REQUEST:
	case USB_DO_REQUEST:
		if (!(fflags & FWRITE)) {
			error = EPERM;
			break;
		}
		error = ugen_do_request(f, addr);
		break;

	case USB_DEVICEINFO:
	case USB_GET_DEVICEINFO:
		error = usb_gen_fill_deviceinfo(f, addr);
		break;

	case USB_DEVICESTATS:
		for (n = 0; n != 4; n++) {

			u.stat->uds_requests_fail[n] =
			    f->udev->bus->stats_err.uds_requests[n];

			u.stat->uds_requests_ok[n] =
			    f->udev->bus->stats_ok.uds_requests[n];
		}
		break;

	case USB_DEVICEENUMERATE:
		error = ugen_re_enumerate(f);
		break;

	case USB_GET_PLUGTIME:
		*u.ptime = f->udev->plugtime;
		break;

	case USB_CLAIM_INTERFACE:
	case USB_RELEASE_INTERFACE:
		/* TODO */
		break;

	case USB_IFACE_DRIVER_ACTIVE:
		/* TODO */
		*u.pint = 0;
		break;

	case USB_IFACE_DRIVER_DETACH:
		/* TODO */
		error = priv_check(curthread, PRIV_DRIVER);
		if (error) {
			break;
		}
		error = EINVAL;
		break;

	case USB_SET_POWER_MODE:
		error = ugen_set_power_mode(f, *u.pint);
		break;

	case USB_GET_POWER_MODE:
		*u.pint = ugen_get_power_mode(f);
		break;

	case USB_SET_PORT_ENABLE:
		error = ugen_do_port_feature(f,
		    *u.pint, 1, UHF_PORT_ENABLE);
		break;

	case USB_SET_PORT_DISABLE:
		error = ugen_do_port_feature(f,
		    *u.pint, 0, UHF_PORT_ENABLE);
		break;

	case USB_FS_INIT:
		/* verify input parameters */
		if (u.pinit->pEndpoints == NULL) {
			error = EINVAL;
			break;
		}
		if (u.pinit->ep_index_max > 127) {
			error = EINVAL;
			break;
		}
		if (u.pinit->ep_index_max == 0) {
			error = EINVAL;
			break;
		}
		if (f->fs_xfer != NULL) {
			error = EBUSY;
			break;
		}
		if (f->dev_ep_index != 0) {
			error = EINVAL;
			break;
		}
		if (ugen_fifo_in_use(f, fflags)) {
			error = EBUSY;
			break;
		}
		error = usb_fifo_alloc_buffer(f, 1, u.pinit->ep_index_max);
		if (error) {
			break;
		}
		f->fs_xfer = malloc(sizeof(f->fs_xfer[0]) *
		    u.pinit->ep_index_max, M_USB, M_WAITOK | M_ZERO);
		if (f->fs_xfer == NULL) {
			usb_fifo_free_buffer(f);
			error = ENOMEM;
			break;
		}
		f->fs_ep_max = u.pinit->ep_index_max;
		f->fs_ep_ptr = u.pinit->pEndpoints;
		break;

	case USB_FS_UNINIT:
		if (u.puninit->dummy != 0) {
			error = EINVAL;
			break;
		}
		error = ugen_fs_uninit(f);
		break;

	default:
		mtx_lock(f->priv_mtx);
		error = ugen_iface_ioctl(f, cmd, addr, fflags);
		mtx_unlock(f->priv_mtx);
		break;
	}
	DPRINTFN(6, "error=%d\n", error);
	return (error);
}
/*------------------------------------------------------------------------*
 *	usb_handle_iface_request
 *
 * Returns:
 *    0: Success
 * Else: Failure
 *------------------------------------------------------------------------*/
static usb_error_t
usb_handle_iface_request(struct usb_xfer *xfer,
    void **ppdata, uint16_t *plen,
    struct usb_device_request req, uint16_t off, uint8_t state)
{
	struct usb_interface *iface;
	struct usb_interface *iface_parent;	/* parent interface */
	struct usb_device *udev = xfer->xroot->udev;
	int error;
	uint8_t iface_index;
	uint8_t temp_state;

	if ((req.bmRequestType & 0x1F) == UT_INTERFACE) {
		iface_index = req.wIndex[0];	/* unicast */
	} else {
		iface_index = 0;	/* broadcast */
	}

	/*
	 * We need to protect against other threads doing probe and
	 * attach:
	 */
	USB_XFER_UNLOCK(xfer);

	usbd_enum_lock(udev);

	error = ENXIO;

tr_repeat:
	iface = usbd_get_iface(udev, iface_index);
	if ((iface == NULL) ||
	    (iface->idesc == NULL)) {
		/* end of interfaces non-existing interface */
		goto tr_stalled;
	}
	/* set initial state */

	temp_state = state;

	/* forward request to interface, if any */

	if ((error != 0) &&
	    (error != ENOTTY) &&
	    (iface->subdev != NULL) &&
	    device_is_attached(iface->subdev)) {
#if 0
		DEVMETHOD(usb_handle_request, NULL);	/* dummy */
#endif
		error = USB_HANDLE_REQUEST(iface->subdev,
		    &req, ppdata, plen,
		    off, &temp_state);
	}
	iface_parent = usbd_get_iface(udev, iface->parent_iface_index);

	if ((iface_parent == NULL) ||
	    (iface_parent->idesc == NULL)) {
		/* non-existing interface */
		iface_parent = NULL;
	}
	/* forward request to parent interface, if any */

	if ((error != 0) &&
	    (error != ENOTTY) &&
	    (iface_parent != NULL) &&
	    (iface_parent->subdev != NULL) &&
	    ((req.bmRequestType & 0x1F) == UT_INTERFACE) &&
	    (iface_parent->subdev != iface->subdev) &&
	    device_is_attached(iface_parent->subdev)) {
		error = USB_HANDLE_REQUEST(iface_parent->subdev,
		    &req, ppdata, plen, off, &temp_state);
	}
	if (error == 0) {
		/* negativly adjust pointer and length */
		*ppdata = ((uint8_t *)(*ppdata)) - off;
		*plen += off;

		if ((state == USB_HR_NOT_COMPLETE) &&
		    (temp_state == USB_HR_COMPLETE_OK))
			goto tr_short;
		else
			goto tr_valid;
	} else if (error == ENOTTY) {
		goto tr_stalled;
	}
	if ((req.bmRequestType & 0x1F) != UT_INTERFACE) {
		iface_index++;		/* iterate */
		goto tr_repeat;
	}
	if (state != USB_HR_NOT_COMPLETE) {
		/* we are complete */
		goto tr_valid;
	}
	switch (req.bmRequestType) {
	case UT_WRITE_INTERFACE:
		switch (req.bRequest) {
		case UR_SET_INTERFACE:
			/*
			 * We assume that the endpoints are the same
			 * accross the alternate settings.
			 *
			 * Reset the endpoints, because re-attaching
			 * only a part of the device is not possible.
			 */
			error = usb_check_alt_setting(udev,
			    iface, req.wValue[0]);
			if (error) {
				DPRINTF("alt setting does not exist %s\n",
				    usbd_errstr(error));
				goto tr_stalled;
			}
			error = usb_reset_iface_endpoints(udev, iface_index);
			if (error) {
				DPRINTF("alt setting failed %s\n",
				    usbd_errstr(error));
				goto tr_stalled;
			}
			/* update the current alternate setting */
			iface->alt_index = req.wValue[0];
			break;

		default:
			goto tr_stalled;
		}
		break;

	case UT_READ_INTERFACE:
		switch (req.bRequest) {
		case UR_GET_INTERFACE:
			*ppdata = &iface->alt_index;
			*plen = 1;
			break;

		default:
			goto tr_stalled;
		}
		break;
	default:
		goto tr_stalled;
	}
tr_valid:
	usbd_enum_unlock(udev);
	USB_XFER_LOCK(xfer);
	return (0);

tr_short:
	usbd_enum_unlock(udev);
	USB_XFER_LOCK(xfer);
	return (USB_ERR_SHORT_XFER);

tr_stalled:
	usbd_enum_unlock(udev);
	USB_XFER_LOCK(xfer);
	return (USB_ERR_STALLED);
}
예제 #7
0
파일: uhso.c 프로젝트: vkhromov/freebsd
/*
 * Probes an interface for its particular capabilities and attaches if
 * it's a supported interface.
 */
static int
uhso_probe_iface(struct uhso_softc *sc, int index,
    int (*probe)(struct usb_device *, int))
{
	struct usb_interface *iface;
	int type, error;

	UHSO_DPRINTF(1, "Probing for interface %d, probe_func=%p\n", index, probe);

	type = probe(sc->sc_udev, index);
	UHSO_DPRINTF(1, "Probe result %x\n", type);
	if (type <= 0)
		return (ENXIO);

	sc->sc_type = type;
	iface = usbd_get_iface(sc->sc_udev, index);

	if (UHSO_IFACE_PORT_TYPE(type) == UHSO_PORT_TYPE_NETWORK) {
		error = uhso_attach_ifnet(sc, iface, type);
		if (error) {
			UHSO_DPRINTF(1, "uhso_attach_ifnet failed");
			return (ENXIO);
		}

		/*
		 * If there is an additional interrupt endpoint on this
		 * interface then we most likely have a multiplexed serial port
		 * available.
		 */
		if (iface->idesc->bNumEndpoints < 3) {
			sc->sc_type = UHSO_IFACE_SPEC( 
			    UHSO_IFACE_USB_TYPE(type) & ~UHSO_IF_MUX,
			    UHSO_IFACE_PORT(type) & ~UHSO_PORT_SERIAL,
			    UHSO_IFACE_PORT_TYPE(type));
			return (0);
		}

		UHSO_DPRINTF(1, "Trying to attach mux. serial\n");
		error = uhso_attach_muxserial(sc, iface, type);
		if (error == 0 && sc->sc_ttys > 0) {
			error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom,
			    sc->sc_ttys, sc, &uhso_ucom_callback, &sc->sc_mtx);
			if (error) {
				device_printf(sc->sc_dev, "ucom_attach failed\n");
				return (ENXIO);
			}
			ucom_set_pnpinfo_usb(&sc->sc_super_ucom, sc->sc_dev);

			mtx_lock(&sc->sc_mtx);
			usbd_transfer_start(sc->sc_xfer[UHSO_MUX_ENDPT_INTR]);
			mtx_unlock(&sc->sc_mtx);
		}
	} else if ((UHSO_IFACE_USB_TYPE(type) & UHSO_IF_BULK) &&
	    UHSO_IFACE_PORT(type) & UHSO_PORT_SERIAL) {

		error = uhso_attach_bulkserial(sc, iface, type);
		if (error)
			return (ENXIO);

		error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom,
		    sc->sc_ttys, sc, &uhso_ucom_callback, &sc->sc_mtx);
		if (error) {
			device_printf(sc->sc_dev, "ucom_attach failed\n");
			return (ENXIO);
		}
		ucom_set_pnpinfo_usb(&sc->sc_super_ucom, sc->sc_dev);
	}
	else {
		UHSO_DPRINTF(0, "Unknown type %x\n", type);
		return (ENXIO);
	}

	return (0);
}
예제 #8
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);

	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);
}
예제 #9
0
static int
cdce_attach(device_t dev)
{
	struct cdce_softc *sc = device_get_softc(dev);
	struct usb_ether *ue = &sc->sc_ue;
	struct usb_attach_arg *uaa = device_get_ivars(dev);
	struct usb_interface *iface;
	const struct usb_cdc_union_descriptor *ud;
	const struct usb_interface_descriptor *id;
	const struct usb_cdc_ethernet_descriptor *ued;
	const struct usb_config *pcfg;
	int error;
	uint8_t i;
	uint8_t data_iface_no;
	char eaddr_str[5 * ETHER_ADDR_LEN];	/* approx */

	sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
	sc->sc_ue.ue_udev = uaa->device;

	device_set_usb_desc(dev);

	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);

	ud = usbd_find_descriptor
	    (uaa->device, NULL, uaa->info.bIfaceIndex,
	    UDESC_CS_INTERFACE, 0 - 1, UDESCSUB_CDC_UNION, 0 - 1);

	if ((ud == NULL) || (ud->bLength < sizeof(*ud)) ||
	    (sc->sc_flags & CDCE_FLAG_NO_UNION)) {
		DPRINTFN(1, "No union descriptor!\n");
		sc->sc_ifaces_index[0] = uaa->info.bIfaceIndex;
		sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex;
		goto alloc_transfers;
	}
	data_iface_no = ud->bSlaveInterface[0];

	for (i = 0;; i++) {

		iface = usbd_get_iface(uaa->device, i);

		if (iface) {

			id = usbd_get_interface_descriptor(iface);

			if (id && (id->bInterfaceNumber == data_iface_no)) {
				sc->sc_ifaces_index[0] = i;
				sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex;
				usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
				break;
			}
		} else {
			device_printf(dev, "no data interface found\n");
			goto detach;
		}
	}

	/*
	 * <quote>
	 *
	 *  The Data Class interface of a networking device shall have
	 *  a minimum of two interface settings. The first setting
	 *  (the default interface setting) includes no endpoints and
	 *  therefore no networking traffic is exchanged whenever the
	 *  default interface setting is selected. One or more
	 *  additional interface settings are used for normal
	 *  operation, and therefore each includes a pair of endpoints
	 *  (one IN, and one OUT) to exchange network traffic. Select
	 *  an alternate interface setting to initialize the network
	 *  aspects of the device and to enable the exchange of
	 *  network traffic.
	 *
	 * </quote>
	 *
	 * Some devices, most notably cable modems, include interface
	 * settings that have no IN or OUT endpoint, therefore loop
	 * through the list of all available interface settings
	 * looking for one with both IN and OUT endpoints.
	 */

alloc_transfers:

	pcfg = cdce_config;	/* Default Configuration */

	for (i = 0; i != 32; i++) {

		error = usbd_set_alt_interface_index(uaa->device,
		    sc->sc_ifaces_index[0], i);
		if (error)
			break;
#if CDCE_HAVE_NCM
		if ((i == 0) && (cdce_ncm_init(sc) == 0))
			pcfg = cdce_ncm_config;
#endif
		error = usbd_transfer_setup(uaa->device,
		    sc->sc_ifaces_index, sc->sc_xfer,
		    pcfg, CDCE_N_TRANSFER, sc, &sc->sc_mtx);

		if (error == 0)
			break;
	}

	if (error || (i == 32)) {
		device_printf(dev, "No valid alternate "
		    "setting found\n");
		goto detach;
	}

	ued = usbd_find_descriptor
	    (uaa->device, NULL, uaa->info.bIfaceIndex,
	    UDESC_CS_INTERFACE, 0 - 1, UDESCSUB_CDC_ENF, 0 - 1);

	if ((ued == NULL) || (ued->bLength < sizeof(*ued))) {
		error = USB_ERR_INVAL;
	} else {
		error = usbd_req_get_string_any(uaa->device, NULL, 
		    eaddr_str, sizeof(eaddr_str), ued->iMacAddress);
	}

	if (error) {

		/* fake MAC address */

		device_printf(dev, "faking MAC address\n");
		sc->sc_ue.ue_eaddr[0] = 0x2a;
		memcpy(&sc->sc_ue.ue_eaddr[1], &ticks, sizeof(uint32_t));
		sc->sc_ue.ue_eaddr[5] = device_get_unit(dev);

	} else {

		memset(sc->sc_ue.ue_eaddr, 0, sizeof(sc->sc_ue.ue_eaddr));

		for (i = 0; i != (ETHER_ADDR_LEN * 2); i++) {

			char c = eaddr_str[i];

			if ('0' <= c && c <= '9')
				c -= '0';
			else if (c != 0)
				c -= 'A' - 10;
			else
				break;

			c &= 0xf;

			if ((i & 1) == 0)
				c <<= 4;
			sc->sc_ue.ue_eaddr[i / 2] |= c;
		}

		if (uaa->usb_mode == USB_MODE_DEVICE) {
			/*
			 * Do not use the same MAC address like the peer !
			 */
			sc->sc_ue.ue_eaddr[5] ^= 0xFF;
		}
	}

	ue->ue_sc = sc;
	ue->ue_dev = dev;
	ue->ue_udev = uaa->device;
	ue->ue_mtx = &sc->sc_mtx;
	ue->ue_methods = &cdce_ue_methods;

	error = uether_ifattach(ue);
	if (error) {
		device_printf(dev, "could not attach interface\n");
		goto detach;
	}
	return (0);			/* success */

detach:
	cdce_detach(dev);
	return (ENXIO);			/* failure */
}
예제 #10
0
파일: ng_ubt.c 프로젝트: 2asoft/freebsd
static int
ubt_attach(device_t dev)
{
	struct usb_attach_arg		*uaa = device_get_ivars(dev);
	struct ubt_softc		*sc = device_get_softc(dev);
	struct usb_endpoint_descriptor	*ed;
	struct usb_interface_descriptor *id;
	struct usb_interface		*iface;
	uint16_t			wMaxPacketSize;
	uint8_t				alt_index, i, j;
	uint8_t				iface_index[2] = { 0, 1 };

	device_set_usb_desc(dev);

	sc->sc_dev = dev;
	sc->sc_debug = NG_UBT_WARN_LEVEL;

	/* 
	 * Create Netgraph node
	 */

	if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
		UBT_ALERT(sc, "could not create Netgraph node\n");
		return (ENXIO);
	}

	/* Name Netgraph node */
	if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) {
		UBT_ALERT(sc, "could not name Netgraph node\n");
		NG_NODE_UNREF(sc->sc_node);
		return (ENXIO);
	}
	NG_NODE_SET_PRIVATE(sc->sc_node, sc);
	NG_NODE_FORCE_WRITER(sc->sc_node);

	/*
	 * Initialize device softc structure
	 */

	/* initialize locks */
	mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF);
	mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE);

	/* initialize packet queues */
	NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
	NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
	NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);

	/* initialize glue task */
	TASK_INIT(&sc->sc_task, 0, ubt_task, sc);

	/*
	 * Configure Bluetooth USB device. Discover all required USB
	 * interfaces and endpoints.
	 *
	 * USB device must present two interfaces:
	 * 1) Interface 0 that has 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
	 *
	 * 2) Interface 1 then has 2 endpoints
	 *	1) Isochronous IN endpoint to receive SCO data
 	 *	2) Isochronous OUT endpoint to send SCO data
	 *
	 * Interface 1 (with isochronous endpoints) has several alternate
	 * configurations with different packet size.
	 */

	/*
	 * For interface #1 search alternate settings, and find
	 * the descriptor with the largest wMaxPacketSize
	 */

	wMaxPacketSize = 0;
	alt_index = 0;
	i = 0;
	j = 0;
	ed = NULL;

	/* 
	 * Search through all the descriptors looking for the largest
	 * packet size:
	 */
	while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach(
	    usbd_get_config_descriptor(uaa->device), 
	    (struct usb_descriptor *)ed))) {

		if ((ed->bDescriptorType == UDESC_INTERFACE) &&
		    (ed->bLength >= sizeof(*id))) {
			id = (struct usb_interface_descriptor *)ed;
			i = id->bInterfaceNumber;
			j = id->bAlternateSetting;
		}

		if ((ed->bDescriptorType == UDESC_ENDPOINT) &&
		    (ed->bLength >= sizeof(*ed)) &&
		    (i == 1)) {
			uint16_t temp;

			temp = UGETW(ed->wMaxPacketSize);
			if (temp > wMaxPacketSize) {
				wMaxPacketSize = temp;
				alt_index = j;
			}
		}
	}

	/* Set alt configuration on interface #1 only if we found it */
	if (wMaxPacketSize > 0 &&
	    usbd_set_alt_interface_index(uaa->device, 1, alt_index)) {
		UBT_ALERT(sc, "could not set alternate setting %d " \
			"for interface 1!\n", alt_index);
		goto detach;
	}

	/* Setup transfers for both interfaces */
	if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer,
			ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) {
		UBT_ALERT(sc, "could not allocate transfers\n");
		goto detach;
	}

	/* Claim all interfaces belonging to the Bluetooth part */
	for (i = 1;; i++) {
		iface = usbd_get_iface(uaa->device, i);
		if (iface == NULL)
			break;
		id = usbd_get_interface_descriptor(iface);

		if ((id != NULL) &&
		    (id->bInterfaceClass == UICLASS_WIRELESS) &&
		    (id->bInterfaceSubClass == UISUBCLASS_RF) &&
		    (id->bInterfaceProtocol == UIPROTO_BLUETOOTH)) {
			usbd_set_parent_iface(uaa->device, i,
			    uaa->info.bIfaceIndex);
		}
	}
	return (0); /* success */

detach:
	ubt_detach(dev);

	return (ENXIO);
} /* ubt_attach */