diff options
| author | Andrew Thompson <thompsa@FreeBSD.org> | 2009-12-09 22:37:06 +0000 | 
|---|---|---|
| committer | Andrew Thompson <thompsa@FreeBSD.org> | 2009-12-09 22:37:06 +0000 | 
| commit | b7167d5d0dd29985387f9c91684dca4bedc784fe (patch) | |
| tree | 9181604e616511ede3447b6e345913444c9755ea /sys/dev | |
| parent | 62460f64ff5955c09e10daaf7210dc229d9ae7dc (diff) | |
Notes
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/usb/controller/ehci.c | 59 | ||||
| -rw-r--r-- | sys/dev/usb/usb_core.h | 3 | ||||
| -rw-r--r-- | sys/dev/usb/usb_device.c | 2 | ||||
| -rw-r--r-- | sys/dev/usb/usb_hub.c | 181 | ||||
| -rw-r--r-- | sys/dev/usb/usb_hub.h | 4 | ||||
| -rw-r--r-- | sys/dev/usb/usb_transfer.c | 20 | ||||
| -rw-r--r-- | sys/dev/usb/usbdi.h | 11 | 
7 files changed, 209 insertions, 71 deletions
diff --git a/sys/dev/usb/controller/ehci.c b/sys/dev/usb/controller/ehci.c index 5cc9b29aea72..d665b89077cd 100644 --- a/sys/dev/usb/controller/ehci.c +++ b/sys/dev/usb/controller/ehci.c @@ -2016,8 +2016,8 @@ ehci_setup_standard_chain(struct usb_xfer *xfer, ehci_qh_t **qh_last)  	qh_endphub =  	    (EHCI_QH_SET_MULT(xfer->max_packet_count & 3) | -	    EHCI_QH_SET_CMASK(xfer->usb_cmask) | -	    EHCI_QH_SET_SMASK(xfer->usb_smask) | +	    EHCI_QH_SET_CMASK(xfer->endpoint->usb_cmask) | +	    EHCI_QH_SET_SMASK(xfer->endpoint->usb_smask) |  	    EHCI_QH_SET_HUBA(xfer->xroot->udev->hs_hub_addr) |  	    EHCI_QH_SET_PORT(xfer->xroot->udev->hs_port_no)); @@ -2162,7 +2162,7 @@ ehci_isoc_hs_done(ehci_softc_t *sc, struct usb_xfer *xfer)  		DPRINTFN(2, "status=0x%08x, len=%u\n", status, len); -		if (xfer->usb_smask & (1 << td_no)) { +		if (xfer->endpoint->usb_smask & (1 << td_no)) {  			if (*plen >= len) {  				/* @@ -2348,22 +2348,8 @@ ehci_device_intr_open(struct usb_xfer *xfer)  	uint16_t best;  	uint16_t bit;  	uint16_t x; -	uint8_t slot; -	/* Allocate a microframe slot first: */ - -	slot = usb_intr_schedule_adjust -	    (xfer->xroot->udev, xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX); - -	if (usbd_get_speed(xfer->xroot->udev) == USB_SPEED_HIGH) { -		xfer->usb_uframe = slot; -		xfer->usb_smask = (1 << slot) & 0xFF; -		xfer->usb_cmask = 0; -	} else { -		xfer->usb_uframe = slot; -		xfer->usb_smask = (1 << slot) & 0x3F; -		xfer->usb_cmask = (-(4 << slot)) & 0xFE; -	} +	usb_hs_bandwidth_alloc(xfer);  	/*  	 * Find the best QH position corresponding to the given interval: @@ -2399,12 +2385,12 @@ ehci_device_intr_close(struct usb_xfer *xfer)  {  	ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); -	usb_intr_schedule_adjust(xfer->xroot->udev, -	    -(xfer->max_frame_size), xfer->usb_uframe); -  	sc->sc_intr_stat[xfer->qh_pos]--;  	ehci_device_done(xfer, USB_ERR_CANCELLED); + +	/* bandwidth must be freed after device done */ +	usb_hs_bandwidth_free(xfer);  }  static void @@ -2726,28 +2712,8 @@ ehci_device_isoc_hs_open(struct usb_xfer *xfer)  	ehci_itd_t *td;  	uint32_t temp;  	uint8_t ds; -	uint8_t slot; - -	slot = usb_intr_schedule_adjust(xfer->xroot->udev, xfer->max_frame_size, -	    USB_HS_MICRO_FRAMES_MAX); - -	xfer->usb_uframe = slot; -	xfer->usb_cmask = 0; -	switch (usbd_xfer_get_fps_shift(xfer)) { -	case 0: -		xfer->usb_smask = 0xFF; -		break; -	case 1: -		xfer->usb_smask = 0x55 << (slot & 1); -		break; -	case 2: -		xfer->usb_smask = 0x11 << (slot & 3); -		break; -	default: -		xfer->usb_smask = 0x01 << (slot & 7); -		break; -	} +	usb_hs_bandwidth_alloc(xfer);  	/* initialize all TD's */ @@ -2791,11 +2757,10 @@ ehci_device_isoc_hs_open(struct usb_xfer *xfer)  static void  ehci_device_isoc_hs_close(struct usb_xfer *xfer)  { - -	usb_intr_schedule_adjust(xfer->xroot->udev, -	    -(xfer->max_frame_size), xfer->usb_uframe); -  	ehci_device_done(xfer, USB_ERR_CANCELLED); + +	/* bandwidth must be freed after device done */ +	usb_hs_bandwidth_free(xfer);  }  static void @@ -2905,7 +2870,7 @@ ehci_device_isoc_hs_enter(struct usb_xfer *xfer)  			*plen = xfer->max_frame_size;  		} -		if (xfer->usb_smask & (1 << td_no)) { +		if (xfer->endpoint->usb_smask & (1 << td_no)) {  			status = (EHCI_ITD_SET_LEN(*plen) |  			    EHCI_ITD_ACTIVE |  			    EHCI_ITD_SET_PG(0)); diff --git a/sys/dev/usb/usb_core.h b/sys/dev/usb/usb_core.h index 7541adbc7039..3dfd0d1ab6c5 100644 --- a/sys/dev/usb/usb_core.h +++ b/sys/dev/usb/usb_core.h @@ -161,9 +161,6 @@ struct usb_xfer {  	uint8_t	address;		/* physical USB address */  	uint8_t	endpointno;		/* physical USB endpoint */  	uint8_t	max_packet_count; -	uint8_t	usb_smask; -	uint8_t	usb_cmask; -	uint8_t	usb_uframe;  	uint8_t	usb_state;  	uint8_t fps_shift;		/* down shift of FPS, 0..3 */ diff --git a/sys/dev/usb/usb_device.c b/sys/dev/usb/usb_device.c index 4339cb247779..ae71f7da598f 100644 --- a/sys/dev/usb/usb_device.c +++ b/sys/dev/usb/usb_device.c @@ -665,7 +665,7 @@ usb_config_parse(struct usb_device *udev, uint8_t iface_index, uint8_t cmd)  			/* look for matching endpoints */  			if ((iface_index == USB_IFACE_INDEX_ANY) ||  			    (iface_index == ep->iface_index)) { -				if (ep->refcount != 0) { +				if (ep->refcount_alloc != 0) {  					/*  					 * This typically indicates a  					 * more serious error. diff --git a/sys/dev/usb/usb_hub.c b/sys/dev/usb/usb_hub.c index 07a0970b33b4..08090864c728 100644 --- a/sys/dev/usb/usb_hub.c +++ b/sys/dev/usb/usb_hub.c @@ -1106,43 +1106,62 @@ done:   *   The best Transaction Translation slot for an interrupt endpoint.   *------------------------------------------------------------------------*/  static uint8_t -usb_intr_find_best_slot(usb_size_t *ptr, uint8_t start, uint8_t end) +usb_intr_find_best_slot(usb_size_t *ptr, uint8_t start, +    uint8_t end, uint8_t mask)  { -	usb_size_t max = 0 - 1; +	usb_size_t min = 0 - 1; +	usb_size_t sum;  	uint8_t x;  	uint8_t y; +	uint8_t z;  	y = 0;  	/* find the last slot with lesser used bandwidth */  	for (x = start; x < end; x++) { -		if (max >= ptr[x]) { -			max = ptr[x]; + +		sum = 0; + +		/* compute sum of bandwidth */ +		for (z = x; z < end; z++) { +			if (mask & (1U << (z - x))) +				sum += ptr[z]; +		} + +		/* check if the current multi-slot is more optimal */ +		if (min >= sum) { +			min = sum;  			y = x;  		} + +		/* check if the mask is about to be shifted out */ +		if (mask & (1U << (end - 1 - x))) +			break;  	}  	return (y);  }  /*------------------------------------------------------------------------* - *	usb_intr_schedule_adjust + *	usb_hs_bandwidth_adjust   *   * This function will update the bandwith usage for the microframe   * having index "slot" by "len" bytes. "len" can be negative.  If the   * "slot" argument is greater or equal to "USB_HS_MICRO_FRAMES_MAX"   * the "slot" argument will be replaced by the slot having least used - * bandwidth. + * bandwidth. The "mask" argument is used for multi-slot allocations.   *   * Returns: - *   The slot on which the bandwidth update was done. + *    The slot in which the bandwidth update was done: 0..7   *------------------------------------------------------------------------*/ -uint8_t -usb_intr_schedule_adjust(struct usb_device *udev, int16_t len, uint8_t slot) +static uint8_t +usb_hs_bandwidth_adjust(struct usb_device *udev, int16_t len, +    uint8_t slot, uint8_t mask)  {  	struct usb_bus *bus = udev->bus;  	struct usb_hub *hub;  	enum usb_dev_speed speed; +	uint8_t x;  	USB_BUS_LOCK_ASSERT(bus, MA_OWNED); @@ -1164,23 +1183,157 @@ usb_intr_schedule_adjust(struct usb_device *udev, int16_t len, uint8_t slot)  		hub = udev->parent_hs_hub->hub;  		if (slot >= USB_HS_MICRO_FRAMES_MAX) {  			slot = usb_intr_find_best_slot(hub->uframe_usage, -			    USB_FS_ISOC_UFRAME_MAX, 6); +			    USB_FS_ISOC_UFRAME_MAX, 6, mask); +		} +		for (x = slot; x < 8; x++) { +			if (mask & (1U << (x - slot))) { +				hub->uframe_usage[x] += len; +				bus->uframe_usage[x] += len; +			}  		} -		hub->uframe_usage[slot] += len; -		bus->uframe_usage[slot] += len;  		break;  	default:  		if (slot >= USB_HS_MICRO_FRAMES_MAX) {  			slot = usb_intr_find_best_slot(bus->uframe_usage, 0, -			    USB_HS_MICRO_FRAMES_MAX); +			    USB_HS_MICRO_FRAMES_MAX, mask); +		} +		for (x = slot; x < 8; x++) { +			if (mask & (1U << (x - slot))) { +				bus->uframe_usage[x] += len; +			}  		} -		bus->uframe_usage[slot] += len;  		break;  	}  	return (slot);  }  /*------------------------------------------------------------------------* + *	usb_hs_bandwidth_alloc + * + * This function is a wrapper function for "usb_hs_bandwidth_adjust()". + *------------------------------------------------------------------------*/ +void +usb_hs_bandwidth_alloc(struct usb_xfer *xfer) +{ +	struct usb_device *udev; +	uint8_t slot; +	uint8_t mask; +	uint8_t speed; + +	udev = xfer->xroot->udev; + +	if (udev->flags.usb_mode != USB_MODE_HOST) +		return;		/* not supported */ + +	xfer->endpoint->refcount_bw++; +	if (xfer->endpoint->refcount_bw != 1) +		return;		/* already allocated */ + +	speed = usbd_get_speed(udev); + +	switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) { +	case UE_INTERRUPT: +		/* allocate a microframe slot */ + +		mask = 0x01; +		slot = usb_hs_bandwidth_adjust(udev, +		    xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX, mask); + +		xfer->endpoint->usb_uframe = slot; +		xfer->endpoint->usb_smask = mask << slot; + +		if ((speed != USB_SPEED_FULL) && +		    (speed != USB_SPEED_LOW)) { +			xfer->endpoint->usb_cmask = 0x00 ; +		} else { +			xfer->endpoint->usb_cmask = (-(0x04 << slot)) & 0xFE; +		} +		break; + +	case UE_ISOCHRONOUS: +		switch (usbd_xfer_get_fps_shift(xfer)) { +		case 0: +			mask = 0xFF; +			break; +		case 1: +			mask = 0x55; +			break; +		case 2: +			mask = 0x11; +			break; +		default: +			mask = 0x01; +			break; +		} + +		/* allocate a microframe multi-slot */ + +		slot = usb_hs_bandwidth_adjust(udev, +		    xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX, mask); + +		xfer->endpoint->usb_uframe = slot; +		xfer->endpoint->usb_cmask = 0; +		xfer->endpoint->usb_smask = mask << slot; +		break; + +	default: +		xfer->endpoint->usb_uframe = 0; +		xfer->endpoint->usb_cmask = 0; +		xfer->endpoint->usb_smask = 0; +		break; +	} + +	DPRINTFN(11, "slot=%d, mask=0x%02x\n",  +	    xfer->endpoint->usb_uframe,  +	    xfer->endpoint->usb_smask >> xfer->endpoint->usb_uframe); +} + +/*------------------------------------------------------------------------* + *	usb_hs_bandwidth_free + * + * This function is a wrapper function for "usb_hs_bandwidth_adjust()". + *------------------------------------------------------------------------*/ +void +usb_hs_bandwidth_free(struct usb_xfer *xfer) +{ +	struct usb_device *udev; +	uint8_t slot; +	uint8_t mask; + +	udev = xfer->xroot->udev; + +	if (udev->flags.usb_mode != USB_MODE_HOST) +		return;		/* not supported */ + +	xfer->endpoint->refcount_bw--; +	if (xfer->endpoint->refcount_bw != 0) +		return;		/* still allocated */ + +	switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) { +	case UE_INTERRUPT: +	case UE_ISOCHRONOUS: + +		slot = xfer->endpoint->usb_uframe; +		mask = xfer->endpoint->usb_smask; + +		/* free microframe slot(s): */ 	   +		usb_hs_bandwidth_adjust(udev, +		    -xfer->max_frame_size, slot, mask >> slot); + +		DPRINTFN(11, "slot=%d, mask=0x%02x\n",  +		    slot, mask >> slot); + +		xfer->endpoint->usb_uframe = 0; +		xfer->endpoint->usb_cmask = 0; +		xfer->endpoint->usb_smask = 0; +		break; + +	default: +		break; +	} +} + +/*------------------------------------------------------------------------*   *	usbd_fs_isoc_schedule_init_sub   *   * This function initialises an USB FULL speed isochronous schedule diff --git a/sys/dev/usb/usb_hub.h b/sys/dev/usb/usb_hub.h index ceb0cc2283d9..5b8dedf6e647 100644 --- a/sys/dev/usb/usb_hub.h +++ b/sys/dev/usb/usb_hub.h @@ -66,8 +66,8 @@ struct usb_hub {  /* function prototypes */ -uint8_t	usb_intr_schedule_adjust(struct usb_device *udev, int16_t len, -	    uint8_t slot); +void	usb_hs_bandwidth_alloc(struct usb_xfer *xfer); +void	usb_hs_bandwidth_free(struct usb_xfer *xfer);  void	usbd_fs_isoc_schedule_init_all(struct usb_fs_isoc_schedule *fss);  void	usb_bus_port_set_device(struct usb_bus *bus, struct usb_port *up,  	    struct usb_device *udev, uint8_t device_index); diff --git a/sys/dev/usb/usb_transfer.c b/sys/dev/usb/usb_transfer.c index 4169ac8b5af4..c434703ad01c 100644 --- a/sys/dev/usb/usb_transfer.c +++ b/sys/dev/usb/usb_transfer.c @@ -942,10 +942,18 @@ usbd_transfer_setup(struct usb_device *udev,  				 * configuration and alternate setting  				 * when USB transfers are in use on  				 * the given interface. Search the USB -				 * code for "endpoint->refcount" if you +				 * code for "endpoint->refcount_alloc" if you  				 * want more information.  				 */ -				xfer->endpoint->refcount++; +				USB_BUS_LOCK(info->bus); +				if (xfer->endpoint->refcount_alloc >= USB_EP_REF_MAX) +					parm.err = USB_ERR_INVAL; + +				xfer->endpoint->refcount_alloc++; + +				if (xfer->endpoint->refcount_alloc == 0) +					panic("usbd_transfer_setup(): Refcount wrapped to zero\n"); +				USB_BUS_UNLOCK(info->bus);  				/*  				 * Whenever we set ppxfer[] then we @@ -960,6 +968,10 @@ usbd_transfer_setup(struct usb_device *udev,  				 */  				ppxfer[n] = xfer;  			} + +			/* check for error */ +			if (parm.err) +				goto done;  		}  		if (buf || parm.err) { @@ -1179,7 +1191,9 @@ usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup)  		 * NOTE: default endpoint does not have an  		 * interface, even if endpoint->iface_index == 0  		 */ -		xfer->endpoint->refcount--; +		USB_BUS_LOCK(info->bus); +		xfer->endpoint->refcount_alloc--; +		USB_BUS_UNLOCK(info->bus);  		usb_callout_drain(&xfer->timeout_handle); diff --git a/sys/dev/usb/usbdi.h b/sys/dev/usb/usbdi.h index 46747d7baf00..1a6665227beb 100644 --- a/sys/dev/usb/usbdi.h +++ b/sys/dev/usb/usbdi.h @@ -130,13 +130,22 @@ struct usb_endpoint {  	struct usb_pipe_methods *methods;	/* set by HC driver */  	uint16_t isoc_next; -	uint16_t refcount;  	uint8_t	toggle_next:1;		/* next data toggle value */  	uint8_t	is_stalled:1;		/* set if endpoint is stalled */  	uint8_t	is_synced:1;		/* set if we a synchronised */  	uint8_t	unused:5;  	uint8_t	iface_index;		/* not used by "default endpoint" */ + +	uint8_t refcount_alloc;		/* allocation refcount */ +	uint8_t refcount_bw;		/* bandwidth refcount */ +#define	USB_EP_REF_MAX 0x3f + +	/* High-Speed resource allocation (valid if "refcount_bw" > 0) */ + +	uint8_t	usb_smask;		/* USB start mask */ +	uint8_t	usb_cmask;		/* USB complete mask */ +	uint8_t	usb_uframe;		/* USB microframe */  };  /*  | 
