Exemple #1
0
usbd_status
usbd_set_config_index(struct usbd_device *dev, int index, int msg)
{
	USBHIST_FUNC(); USBHIST_CALLED(usbdebug);
	usb_config_descriptor_t cd, *cdp;
	usb_bos_descriptor_t *bdp = NULL;
	usbd_status err;
	int i, ifcidx, nifc, len, selfpowered, power;

	DPRINTFN(5, "dev=%p index=%d", dev, index, 0, 0);

	if (index >= dev->ud_ddesc.bNumConfigurations &&
	    index != USB_UNCONFIG_INDEX) {
		/* panic? */
		printf("usbd_set_config_index: illegal index\n");
		return USBD_INVAL;
	}

	/* XXX check that all interfaces are idle */
	if (dev->ud_config != USB_UNCONFIG_NO) {
		DPRINTF("free old config", 0, 0, 0, 0);
		/* Free all configuration data structures. */
		nifc = dev->ud_cdesc->bNumInterface;
		for (ifcidx = 0; ifcidx < nifc; ifcidx++)
			usbd_free_iface_data(dev, ifcidx);
		kmem_free(dev->ud_ifaces, nifc * sizeof(struct usbd_interface));
		kmem_free(dev->ud_cdesc, UGETW(dev->ud_cdesc->wTotalLength));
		if (dev->ud_bdesc != NULL)
			kmem_free(dev->ud_bdesc,
			    UGETW(dev->ud_bdesc->wTotalLength));
		dev->ud_ifaces = NULL;
		dev->ud_cdesc = NULL;
		dev->ud_bdesc = NULL;
		dev->ud_config = USB_UNCONFIG_NO;
	}

	if (index == USB_UNCONFIG_INDEX) {
		/* We are unconfiguring the device, so leave unallocated. */
		DPRINTF("set config 0", 0, 0, 0, 0);
		err = usbd_set_config(dev, USB_UNCONFIG_NO);
		if (err) {
			DPRINTF("setting config=0 failed, err = %d", err,
			    0, 0, 0);
		}
		return err;
	}

	/* Get the short descriptor. */
	err = usbd_get_config_desc(dev, index, &cd);
	if (err) {
		DPRINTF("get_config_desc=%d", err, 0, 0, 0);
		return err;
	}
	len = UGETW(cd.wTotalLength);
	cdp = kmem_alloc(len, KM_SLEEP);
	if (cdp == NULL)
		return USBD_NOMEM;

	/* Get the full descriptor.  Try a few times for slow devices. */
	for (i = 0; i < 3; i++) {
		err = usbd_get_desc(dev, UDESC_CONFIG, index, len, cdp);
		if (!err)
			break;
		usbd_delay_ms(dev, 200);
	}
	if (err) {
		DPRINTF("get_desc=%d", err, 0, 0, 0);
		goto bad;
	}
	if (cdp->bDescriptorType != UDESC_CONFIG) {
		DPRINTF("bad desc %d", cdp->bDescriptorType, 0, 0, 0);
		err = USBD_INVAL;
		goto bad;
	}

	if (USB_IS_SS(dev->ud_speed)) {
		usb_bos_descriptor_t bd;

		/* get short bos desc */
		err = usbd_get_bos_desc(dev, index, &bd);
		if (!err) {
			int blen = UGETW(bd.wTotalLength);
			bdp = kmem_alloc(blen, KM_SLEEP);
			if (bdp == NULL) {
				err = USBD_NOMEM;
				goto bad;
			}

			/* Get the full desc */
			for (i = 0; i < 3; i++) {
				err = usbd_get_desc(dev, UDESC_BOS, index, blen,
				    bdp);
				if (!err)
					break;
				usbd_delay_ms(dev, 200);
			}
			if (err || bdp->bDescriptorType != UDESC_BOS) {
				DPRINTF("error %d or bad desc %d", err,
				    bdp->bDescriptorType, 0, 0);
				kmem_free(bdp, blen);
				bdp = NULL;
			}
		}
	}
	dev->ud_bdesc = bdp;

	/*
	 * Figure out if the device is self or bus powered.
	 */
#if 0 /* XXX various devices don't report the power state correctly */
	selfpowered = 0;
	err = usbd_get_device_status(dev, &ds);
	if (!err && (UGETW(ds.wStatus) & UDS_SELF_POWERED))
		selfpowered = 1;
#endif
	/*
	 * Use the power state in the configuration we are going
	 * to set. This doesn't necessarily reflect the actual
	 * power state of the device; the driver can control this
	 * by choosing the appropriate configuration.
	 */
	selfpowered = !!(cdp->bmAttributes & UC_SELF_POWERED);

	DPRINTF("addr %d cno=%d attr=0x%02x, selfpowered=%d",
	    dev->ud_addr, cdp->bConfigurationValue, cdp->bmAttributes,
	    selfpowered);
	DPRINTF("max power=%d", cdp->bMaxPower * 2, 0, 0, 0);

	/* Check if we have enough power. */
#if 0 /* this is a no-op, see above */
	if ((cdp->bmAttributes & UC_SELF_POWERED) && !selfpowered) {
		if (msg)
			printf("%s: device addr %d (config %d): "
				 "can't set self powered configuration\n",
			       device_xname(dev->ud_bus->bdev), dev->ud_addr,
			       cdp->bConfigurationValue);
		err = USBD_NO_POWER;
		goto bad;
	}
#endif
#ifdef USB_DEBUG
	if (dev->ud_powersrc == NULL) {
		DPRINTF("No power source?", 0, 0, 0, 0);
		err = USBD_IOERROR;
		goto bad;
	}
#endif
	power = cdp->bMaxPower * 2;
	if (power > dev->ud_powersrc->up_power) {
		DPRINTF("power exceeded %d %d", power, dev->ud_powersrc->up_power,
		    0, 0);
		/* XXX print nicer message. */
		if (msg)
			printf("%s: device addr %d (config %d) exceeds power "
				 "budget, %d mA > %d mA\n",
			       device_xname(dev->ud_bus->ub_usbctl), dev->ud_addr,
			       cdp->bConfigurationValue,
			       power, dev->ud_powersrc->up_power);
		err = USBD_NO_POWER;
		goto bad;
	}
	dev->ud_power = power;
	dev->ud_selfpowered = selfpowered;

	/* Set the actual configuration value. */
	DPRINTF("set config %d", cdp->bConfigurationValue, 0, 0, 0);
	err = usbd_set_config(dev, cdp->bConfigurationValue);
	if (err) {
		DPRINTF("setting config=%d failed, error=%d",
		    cdp->bConfigurationValue, err, 0, 0);
		goto bad;
	}

	/* Allocate and fill interface data. */
	nifc = cdp->bNumInterface;
	dev->ud_ifaces = kmem_alloc(nifc * sizeof(struct usbd_interface),
	    KM_SLEEP);
	if (dev->ud_ifaces == NULL) {
		err = USBD_NOMEM;
		goto bad;
	}
	DPRINTFN(5, "dev=%p cdesc=%p", dev, cdp, 0, 0);
	dev->ud_cdesc = cdp;
	dev->ud_config = cdp->bConfigurationValue;
	for (ifcidx = 0; ifcidx < nifc; ifcidx++) {
		err = usbd_fill_iface_data(dev, ifcidx, 0);
		if (err) {
			while (--ifcidx >= 0)
				usbd_free_iface_data(dev, ifcidx);
			goto bad;
		}
	}

	return USBD_NORMAL_COMPLETION;

 bad:
	kmem_free(cdp, len);
	if (bdp != NULL) {
		kmem_free(bdp, UGETW(bdp->wTotalLength));
		dev->ud_bdesc = NULL;
	}
	return err;
}
Exemple #2
0
usbd_status
usbd_set_config_index(usbd_device_handle dev, int index, int msg)
{
	usb_status_t ds;
	usb_config_descriptor_t cd, *cdp;
	usbd_status err;
	int i, ifcidx, nifc, len, selfpowered, power;

	DPRINTFN(5,("usbd_set_config_index: dev=%p index=%d\n", dev, index));

	if (dev->config != USB_UNCONFIG_NO) {
		nifc = dev->cdesc->bNumInterface;

		/* Check that all interfaces are idle */
		for (ifcidx = 0; ifcidx < nifc; ifcidx++) {
			if (LIST_EMPTY(&dev->ifaces[ifcidx].pipes))
				continue;
			DPRINTF(("usbd_set_config_index: open pipes exist\n"));
			return (USBD_IN_USE);
		}

		DPRINTF(("usbd_set_config_index: free old config\n"));
		/* Free all configuration data structures. */
		for (ifcidx = 0; ifcidx < nifc; ifcidx++)
			usbd_free_iface_data(dev, ifcidx);
		kfree(dev->ifaces, M_USB);
		kfree(dev->cdesc, M_USB);
		dev->ifaces = NULL;
		dev->cdesc = NULL;
		dev->config = USB_UNCONFIG_NO;
	}

	if (index == USB_UNCONFIG_INDEX) {
		/* We are unconfiguring the device, so leave unallocated. */
		DPRINTF(("usbd_set_config_index: set config 0\n"));
		err = usbd_set_config(dev, USB_UNCONFIG_NO);
		if (err)
			DPRINTF(("usbd_set_config_index: setting config=0 "
				 "failed, error=%s\n", usbd_errstr(err)));
		return (err);
	}

	/* Get the short descriptor. */
	err = usbd_get_config_desc(dev, index, &cd);
	if (err)
		return (err);
	len = UGETW(cd.wTotalLength);
	cdp = kmalloc(len, M_USB, M_INTWAIT);
	/* Get the full descriptor.  Try a few times for slow devices. */
	for (i = 0; i < 3; i++) {
		err = usbd_get_desc(dev, UDESC_CONFIG, index, len, cdp);
		if (!err)
			break;
		usbd_delay_ms(dev, 200);
	}
	if (err)
		goto bad;
	if (cdp->bDescriptorType != UDESC_CONFIG) {
		DPRINTFN(-1,("usbd_set_config_index: bad desc %d\n",
			     cdp->bDescriptorType));
		err = USBD_INVAL;
		goto bad;
	}

	/* Figure out if the device is self or bus powered. */
	selfpowered = 0;
	if (!(dev->quirks->uq_flags & UQ_BUS_POWERED) &&
	    (cdp->bmAttributes & UC_SELF_POWERED)) {
		/* May be self powered. */
		if (cdp->bmAttributes & UC_BUS_POWERED) {
			/* Must ask device. */
			if (dev->quirks->uq_flags & UQ_POWER_CLAIM) {
				/*
				 * Hub claims to be self powered, but isn't.
				 * It seems that the power status can be
				 * determined by the hub characteristics.
				 */
				usb_hub_descriptor_t hd;
				usb_device_request_t req;
				req.bmRequestType = UT_READ_CLASS_DEVICE;
				req.bRequest = UR_GET_DESCRIPTOR;
				USETW(req.wValue, 0);
				USETW(req.wIndex, 0);
				USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);
				err = usbd_do_request(dev, &req, &hd);
				if (!err &&
				    (UGETW(hd.wHubCharacteristics) &
				     UHD_PWR_INDIVIDUAL))
					selfpowered = 1;
				DPRINTF(("usbd_set_config_index: charac=0x%04x"
				    ", error=%s\n",
				    UGETW(hd.wHubCharacteristics),
				    usbd_errstr(err)));
			} else {
				err = usbd_get_device_status(dev, &ds);
				if (!err &&
				    (UGETW(ds.wStatus) & UDS_SELF_POWERED))
					selfpowered = 1;
				DPRINTF(("usbd_set_config_index: status=0x%04x"
				    ", error=%s\n",
				    UGETW(ds.wStatus), usbd_errstr(err)));
			}
		} else
			selfpowered = 1;
	}
	DPRINTF(("usbd_set_config_index: (addr %d) cno=%d attr=0x%02x, "
		 "selfpowered=%d, power=%d\n",
		 cdp->bConfigurationValue, dev->address, cdp->bmAttributes,
		 selfpowered, cdp->bMaxPower * 2));

	/* Check if we have enough power. */
#ifdef USB_DEBUG
	if (dev->powersrc == NULL) {
		DPRINTF(("usbd_set_config_index: No power source?\n"));
		return (USBD_IOERROR);
	}
#endif
	power = cdp->bMaxPower * 2;
	if (power > dev->powersrc->power) {
		DPRINTF(("power exceeded %d %d\n", power,dev->powersrc->power));
		/* XXX print nicer message. */
		if (msg)
			kprintf("%s: device addr %d (config %d) exceeds power "
				 "budget, %d mA > %d mA\n",
			       device_get_nameunit(dev->bus->bdev), dev->address,
			       cdp->bConfigurationValue,
			       power, dev->powersrc->power);
		err = USBD_NO_POWER;
		goto bad;
	}
	dev->power = power;
	dev->self_powered = selfpowered;

	/* Set the actual configuration value. */
	DPRINTF(("usbd_set_config_index: set config %d\n",
		 cdp->bConfigurationValue));
	err = usbd_set_config(dev, cdp->bConfigurationValue);
	if (err) {
		DPRINTF(("usbd_set_config_index: setting config=%d failed, "
			 "error=%s\n",
			 cdp->bConfigurationValue, usbd_errstr(err)));
		goto bad;
	}

	/* Allocate and fill interface data. */
	nifc = cdp->bNumInterface;
	dev->ifaces = kmalloc(nifc * sizeof(struct usbd_interface),
			     M_USB, M_INTWAIT);
	DPRINTFN(5,("usbd_set_config_index: dev=%p cdesc=%p\n", dev, cdp));
	dev->cdesc = cdp;
	dev->config = cdp->bConfigurationValue;
	for (ifcidx = 0; ifcidx < nifc; ifcidx++) {
		err = usbd_fill_iface_data(dev, ifcidx, 0);
		if (err) {
			while (--ifcidx >= 0)
				usbd_free_iface_data(dev, ifcidx);
			goto bad;
		}
	}

	return (USBD_NORMAL_COMPLETION);

 bad:
	kfree(cdp, M_USB);
	return (err);
}