Bug 181987 - USB isochronous transfer of the USB driver (Mentor Graphics OTG: musb_otg) is not working.
Summary: USB isochronous transfer of the USB driver (Mentor Graphics OTG: musb_otg) is...
Status: Closed FIXED
Alias: None
Product: Base System
Classification: Unclassified
Component: usb (show other bugs)
Version: 10.0-CURRENT
Hardware: Any Any
: Normal Affects Only Me
Assignee: freebsd-usb (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-09-10 12:10 UTC by toshi
Modified: 2019-01-21 09:22 UTC (History)
1 user (show)

See Also:


Attachments
musb.diff (6.28 KB, patch)
2013-10-15 08:05 UTC, HPS
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description toshi 2013-09-10 12:10:00 UTC
The patch attached is somewhat fixed the problem for the
LOGICOOL C920 but still most UVC devices are not working.

My guess is that the not working device requires three
transactions per *microframe* for the data transfer rate as
the video streaming but the current driver performs one
transaction per microframe. The following page imply the
existence of the undocumented setting to perform so.
http://e2e.ti.com/support/embedded/wince/f/353/t/68966.aspx
Comment 1 toshi 2013-09-10 12:37:53 UTC
A missing patch.txt of the previous mail is here:


--- musb_otg.c.orig	2013-09-10 19:57:53.000000000 +0900
+++ musb_otg.c	2013-09-10 19:57:53.000000000 +0900
@@ -1689,7 +1689,6 @@
 	uint16_t count;
 	uint8_t csr, csrh;
 	uint8_t to;
-	uint8_t got_short;
 
 	/* get pointer to softc */
 	sc = MUSBOTG_PC2SC(td->pc);
@@ -1704,7 +1703,7 @@
 	DPRINTFN(1, "ep_no=%d\n", td->channel);
 
 	to = 8;				/* don't loop forever! */
-	got_short = 0;
+	td->short_pkt = 0;
 
 	/* select endpoint */
 	MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->channel);
@@ -1726,7 +1725,10 @@
 		    td->hport);
 
 		/* RX NAK timeout */
-		MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, MAX_NAK_TO);
+		if (td->transfer_type & MUSB2_MASK_TI_PROTO_ISOC)
+			MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, 0);
+		else
+			MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, MAX_NAK_TO);
 
 		/* Protocol, speed, device endpoint */
 		MUSB2_WRITE_1(sc, MUSB2_REG_RXTI, td->transfer_type);
@@ -1797,11 +1799,10 @@
 	/*
 	 * Check for short or invalid packet:
 	 */
-	if (count != td->max_frame_size) {
+	if (count < td->max_frame_size) {
 		if (count < td->max_frame_size) {
 			/* we have a short packet */
 			td->short_pkt = 1;
-			got_short = 1;
 		} else {
 			/* invalid USB packet */
 			td->error = 1;
@@ -1884,13 +1885,18 @@
 	MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, 0);
 
 	/* check if we are complete */
-	if ((td->remainder == 0) || got_short) {
-		if (td->short_pkt) {
-			/* we are complete */
-			musbotg_channel_free(sc, td);
-			return (0);
-		}
-		/* else need to receive a zero length packet */
+        if (td->remainder == 0 || td->short_pkt) {
+		/* we are complete */
+                musbotg_channel_free(sc, td);
+                return (0);
+        }
+
+        else if (td->transfer_type & MUSB2_MASK_TI_PROTO_ISOC) {
+                if (td->remainder != 0) {
+			/* not short_pkt and now getting DATA2 or DATA1 */
+                        td->transaction_started = 0;
+                        return (1);
+                }
 	}
 
 	/* Reset transaction state and restart */
@@ -3159,9 +3165,9 @@
 		if (dynfifo) {
 			if (frx && (temp <= nrx)) {
 				if (temp < 8) {
-					frx = 10;	/* 1K */
+					frx = 12;	/* 4K */
 					MUSB2_WRITE_1(sc, MUSB2_REG_RXFIFOSZ, 
-					    MUSB2_VAL_FIFOSZ_512 |
+					    MUSB2_VAL_FIFOSZ_4096 |
 					    MUSB2_MASK_FIFODB);
 				} else {
 					frx = 7;	/* 128 bytes */
@@ -4042,7 +4048,7 @@
 	 * reasonable dummies:
 	 */
 	parm->hc_max_packet_size = 0x400;
-	parm->hc_max_frame_size = 0x400;
+	parm->hc_max_frame_size = 0xc00;
 
 	if ((parm->methods == &musbotg_device_isoc_methods) ||
 	    (parm->methods == &musbotg_device_intr_methods))
@@ -4116,7 +4122,10 @@
 			td = USB_ADD_BYTES(parm->buf, parm->size[0]);
 
 			/* init TD */
-			td->max_frame_size = xfer->max_frame_size;
+			if (parm->methods == &musbotg_device_isoc_methods)
+				td->max_frame_size = xfer->max_frame_size/3;
+			else
+				td->max_frame_size = xfer->max_frame_size;
 			td->ep_no = ep_no;
 			td->obj_next = last_obj;
Comment 2 HPS 2013-10-15 08:05:26 UTC
On 09/10/13 13:01, SAITOU Toshihide wrote:
>
>> Number:         181987
>> Category:       usb
>> Synopsis:       USB isochronous transfer of the USB driver (Mentor Graphics OTG: musb_otg) is not working.
>> Confidential:   no
>> Severity:       non-critical
>> Priority:       low
>> Responsible:    freebsd-usb
>> State:          open
>> Quarter:
>> Keywords:
>> Date-Required:
>> Class:          sw-bug
>> Submitter-Id:   current-users
>> Arrival-Date:   Tue Sep 10 11:10:00 UTC 2013
>> Closed-Date:
>> Last-Modified:
>> Originator:     SAITOU Toshihide
>> Release:        FreeBSD 10.0-CURRENT
>> Organization:
>> Environment:
> FreeBSD bbb 10.0-CURRENT FreeBSD 10.0-CURRENT #1: Mon Sep  9 23:34:15 JST 2013     toshi@fbsd:/usr/obj/arm.armv6/usr/src/sys/BEAGLEBONE  arm
>
>> Description:
> The patch attached is somewhat fixed the problem for the
> LOGICOOL C920 but still most UVC devices are not working.
>
> My guess is that the not working device requires three
> transactions per *microframe* for the data transfer rate as
> the video streaming but the current driver performs one
> transaction per microframe. The following page imply the
> existence of the undocumented setting to perform so.
> http://e2e.ti.com/support/embedded/wince/f/353/t/68966.aspx
>
>> How-To-Repeat:
>
>> Fix:
>
>
>> Release-Note:
>> Audit-Trail:
>> Unformatted:
> _______________________________________________
> freebsd-usb@freebsd.org mailing list
> http://lists.freebsd.org/mailman/listinfo/freebsd-usb
> To unsubscribe, send any mail to "freebsd-usb-unsubscribe@freebsd.org"
>

Hi,

Can you please test the attached patch and report back?

--HPS
Comment 3 toshi 2013-10-15 14:17:17 UTC
In message: <525CE936.70800@bitfrost.no>
            Hans Petter Selasky <hps@bitfrost.no> writes:
> On 09/10/13 13:01, SAITOU Toshihide wrote:
>>
>>> Number:         181987
>>> Category:       usb
>>> Synopsis: USB isochronous transfer of the USB driver (Mentor
>>> Graphics OTG: musb_otg) is not working.
>>> Confidential:   no
>>> Severity:       non-critical
>>> Priority:       low
>>> Responsible:    freebsd-usb
>>> State:          open
>>> Quarter:
>>> Keywords:
>>> Date-Required:
>>> Class:          sw-bug
>>> Submitter-Id:   current-users
>>> Arrival-Date:   Tue Sep 10 11:10:00 UTC 2013
>>> Closed-Date:
>>> Last-Modified:
>>> Originator:     SAITOU Toshihide
>>> Release:        FreeBSD 10.0-CURRENT
>>> Organization:
>>> Environment:
>> FreeBSD bbb 10.0-CURRENT FreeBSD 10.0-CURRENT #1: Mon Sep 9
>> 23:34:15 JST 2013
>> toshi@fbsd:/usr/obj/arm.armv6/usr/src/sys/BEAGLEBONE arm
>>
>>> Description:
>> The patch attached is somewhat fixed the problem for the
>> LOGICOOL C920 but still most UVC devices are not working.
>>
>> My guess is that the not working device requires three
>> transactions per *microframe* for the data transfer rate as
>> the video streaming but the current driver performs one
>> transaction per microframe. The following page imply the
>> existence of the undocumented setting to perform so.
>> http://e2e.ti.com/support/embedded/wince/f/353/t/68966.aspx
>>
>>> How-To-Repeat:
>>
>>> Fix:
>>
>>
>>> Release-Note:
>>> Audit-Trail:
>>> Unformatted:
>> _______________________________________________
>> freebsd-usb@freebsd.org mailing list
>> http://lists.freebsd.org/mailman/listinfo/freebsd-usb
>> To unsubscribe, send any mail to
>> "freebsd-usb-unsubscribe@freebsd.org"
>>
> 
> Hi,
> 
> Can you please test the attached patch and report back?
> 
> --HPS

I tried to use a UVC device using libusb but couldn't get
the data. The latter patch was the same result.

FreeBSD revision is 255933.

When libusb_submit_transfer's call back is called,
actual_length of libusb_iso_packet_descriptor is 0 or 12.

A snippet of ``sysctl hw.usb.debug=4'' is below:

musbotg_device_isoc_enter: start next=537
musbotg_ep_int_set: ep_no=1, on=1
musbotg_host_data_rx: ep_no=1
musbotg_host_data_rx: csr=0x01
musbotg_host_data_rx: csrh=0x00
musbotg_interrupt: real endpoint interrupt rx=0x0002, tx=0x0000
musbotg_host_data_rx: ep_no=1
musbotg_host_data_rx: csr=0x01
musbotg_host_data_rx: count=0x0a80
musbotg_channel_free: ep_no=1
musbotg_ep_int_set: ep_no=1, on=0
musbotg_device_done: xfer=0xc2c080c0, endpoint=0xc2ac3cb8, error=22

It seems that more data are collected...


Thanks,
-- 
SAITOU Toshihide
Comment 4 dfilter service freebsd_committer freebsd_triage 2013-10-15 18:11:22 UTC
Author: hselasky
Date: Tue Oct 15 17:11:13 2013
New Revision: 256548
URL: http://svnweb.freebsd.org/changeset/base/256548

Log:
  Correct programming of XXX_MAXP register. This register is 16-bit wide
  and not 8-bit. Fix support for isochronous transfers in USB host mode.
  Fix a whitespace while at it.
  
  MFC after:	1 week
  Reported by:	SAITOU Toshihide <toshi@ruby.ocn.ne.jp>
  PR:		usb/181987

Modified:
  head/sys/dev/usb/controller/musb_otg.c
  head/sys/dev/usb/controller/musb_otg.h

Modified: head/sys/dev/usb/controller/musb_otg.c
==============================================================================
--- head/sys/dev/usb/controller/musb_otg.c	Tue Oct 15 17:03:02 2013	(r256547)
+++ head/sys/dev/usb/controller/musb_otg.c	Tue Oct 15 17:11:13 2013	(r256548)
@@ -1661,7 +1661,7 @@ repeat:
 	}
 
 	/* Max packet size */
-	MUSB2_WRITE_1(sc, MUSB2_REG_TXMAXP, td->max_packet);
+	MUSB2_WRITE_2(sc, MUSB2_REG_TXMAXP, td->reg_max_packet);
 
 	/* write command */
 	MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
@@ -1726,13 +1726,16 @@ repeat:
 		    td->hport);
 
 		/* RX NAK timeout */
-		MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, MAX_NAK_TO);
+		if (td->transfer_type & MUSB2_MASK_TI_PROTO_ISOC)
+			MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, 0);
+		else
+			MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, MAX_NAK_TO);
 
 		/* Protocol, speed, device endpoint */
 		MUSB2_WRITE_1(sc, MUSB2_REG_RXTI, td->transfer_type);
 
 		/* Max packet size */
-		MUSB2_WRITE_1(sc, MUSB2_REG_RXMAXP, td->max_packet);
+		MUSB2_WRITE_2(sc, MUSB2_REG_RXMAXP, td->reg_max_packet);
 
 		/* Data Toggle */
 		csrh = MUSB2_READ_1(sc, MUSB2_REG_RXCSRH);
@@ -1938,7 +1941,7 @@ musbotg_host_data_tx(struct musbotg_td *
 		return (0);	/* complete */
 	}
 
-	if (csr & MUSB2_MASK_CSRL_TXNAKTO ) {
+	if (csr & MUSB2_MASK_CSRL_TXNAKTO) {
 		/* 
 		 * Flush TX FIFO before clearing NAK TO
 		 */
@@ -2069,13 +2072,16 @@ musbotg_host_data_tx(struct musbotg_td *
 	    td->hport);
 
 	/* TX NAK timeout */
-	MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO);
+	if (td->transfer_type & MUSB2_MASK_TI_PROTO_ISOC)
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, 0);
+	else
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO);
 
 	/* Protocol, speed, device endpoint */
 	MUSB2_WRITE_1(sc, MUSB2_REG_TXTI, td->transfer_type);
 
 	/* Max packet size */
-	MUSB2_WRITE_1(sc, MUSB2_REG_TXMAXP, td->max_packet);
+	MUSB2_WRITE_2(sc, MUSB2_REG_TXMAXP, td->reg_max_packet);
 
 	if (!td->transaction_started) {
 		csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH);
@@ -2406,7 +2412,6 @@ musbotg_setup_standard_chain(struct usb_
 
 	if (xfer->flags_int.usb_mode == USB_MODE_HOST) {
 		speed =  usbd_get_speed(xfer->xroot->udev);
-		xfer_type = xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE;
 
 		switch (speed) {
 			case USB_SPEED_LOW:
@@ -2444,7 +2449,6 @@ musbotg_setup_standard_chain(struct usb_
 		}
 
 		temp.transfer_type |= ep_no;
-		td->max_packet = xfer->max_packet_size;
 		td->toggle = xfer->endpoint->toggle_next;
 	}
 
@@ -2469,9 +2473,9 @@ musbotg_setup_standard_chain(struct usb_
 		x = 0;
 	}
 
-	if (x != xfer->nframes) {
-		tx = 0;
+	tx = 0;
 
+	if (x != xfer->nframes) {
 		if (xfer->endpointno & UE_DIR_IN)
 		    	tx = 1;
 
@@ -2532,9 +2536,14 @@ musbotg_setup_standard_chain(struct usb_
 
 		} else {
 
-			/* regular data transfer */
-
-			temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1;
+			if (xfer->flags_int.isochronous_xfr) {
+				/* isochronous data transfer */
+				/* don't force short */
+				temp.short_pkt = 1;
+			} else {
+				/* regular data transfer */
+				temp.short_pkt = (xfer->flags.force_short_xfer ? 0 : 1);
+			}
 		}
 
 		musbotg_setup_standard_chain_sub(&temp);
@@ -3158,7 +3167,12 @@ musbotg_init(struct musbotg_softc *sc)
 
 		if (dynfifo) {
 			if (frx && (temp <= nrx)) {
-				if (temp < 8) {
+				if (temp == 1) {
+					frx = 12;	/* 4K */
+					MUSB2_WRITE_1(sc, MUSB2_REG_RXFIFOSZ, 
+					    MUSB2_VAL_FIFOSZ_4096 |
+					    MUSB2_MASK_FIFODB);
+				} else if (temp < 8) {
 					frx = 10;	/* 1K */
 					MUSB2_WRITE_1(sc, MUSB2_REG_RXFIFOSZ, 
 					    MUSB2_VAL_FIFOSZ_512 |
@@ -3175,7 +3189,12 @@ musbotg_init(struct musbotg_softc *sc)
 				offset += (1 << frx);
 			}
 			if (ftx && (temp <= ntx)) {
-				if (temp < 8) {
+				if (temp == 1) {
+					ftx = 12;	/* 4K */
+					MUSB2_WRITE_1(sc, MUSB2_REG_TXFIFOSZ,
+	 				    MUSB2_VAL_FIFOSZ_4096 |
+	 				    MUSB2_MASK_FIFODB);
+				} else if (temp < 8) {
 					ftx = 10;	/* 1K */
 					MUSB2_WRITE_1(sc, MUSB2_REG_TXFIFOSZ,
 	 				    MUSB2_VAL_FIFOSZ_512 |
@@ -4042,7 +4061,7 @@ musbotg_xfer_setup(struct usb_setup_para
 	 * reasonable dummies:
 	 */
 	parm->hc_max_packet_size = 0x400;
-	parm->hc_max_frame_size = 0x400;
+	parm->hc_max_frame_size = 0xc00;
 
 	if ((parm->methods == &musbotg_device_isoc_methods) ||
 	    (parm->methods == &musbotg_device_intr_methods))
@@ -4117,6 +4136,8 @@ musbotg_xfer_setup(struct usb_setup_para
 
 			/* init TD */
 			td->max_frame_size = xfer->max_frame_size;
+			td->reg_max_packet = xfer->max_packet_size |
+			    ((xfer->max_packet_count - 1) << 11);
 			td->ep_no = ep_no;
 			td->obj_next = last_obj;
 

Modified: head/sys/dev/usb/controller/musb_otg.h
==============================================================================
--- head/sys/dev/usb/controller/musb_otg.h	Tue Oct 15 17:03:02 2013	(r256547)
+++ head/sys/dev/usb/controller/musb_otg.h	Tue Oct 15 17:11:13 2013	(r256548)
@@ -316,9 +316,9 @@ struct musbotg_td {
 	uint32_t offset;
 	uint32_t remainder;
 	uint16_t max_frame_size;	/* packet_size * mult */
+	uint16_t reg_max_packet;
 	uint8_t	ep_no;
 	uint8_t	transfer_type;
-	uint8_t	max_packet;
 	uint8_t	error:1;
 	uint8_t	alt_next:1;
 	uint8_t	short_pkt:1;
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
Comment 5 dfilter service freebsd_committer freebsd_triage 2013-10-24 07:22:52 UTC
Author: hselasky
Date: Thu Oct 24 06:22:43 2013
New Revision: 257041
URL: http://svnweb.freebsd.org/changeset/base/257041

Log:
  MFC r252912, r254828 and r256548:
  Add host mode support to the Mentor Graphics USB OTG controller driver.
  
  PR:		usb/181987

Modified:
  stable/9/sys/dev/usb/controller/musb_otg.c
  stable/9/sys/dev/usb/controller/musb_otg.h
  stable/9/sys/dev/usb/controller/musb_otg_atmelarm.c
Directory Properties:
  stable/9/sys/   (props changed)
  stable/9/sys/dev/   (props changed)

Modified: stable/9/sys/dev/usb/controller/musb_otg.c
==============================================================================
--- stable/9/sys/dev/usb/controller/musb_otg.c	Thu Oct 24 06:06:17 2013	(r257040)
+++ stable/9/sys/dev/usb/controller/musb_otg.c	Thu Oct 24 06:22:43 2013	(r257041)
@@ -90,6 +90,8 @@ SYSCTL_INT(_hw_usb_musbotg, OID_AUTO, de
     &musbotgdebug, 0, "Debug level");
 #endif
 
+#define	MAX_NAK_TO	16
+
 /* prototypes */
 
 struct usb_bus_methods musbotg_bus_methods;
@@ -98,17 +100,35 @@ struct usb_pipe_methods musbotg_device_c
 struct usb_pipe_methods musbotg_device_intr_methods;
 struct usb_pipe_methods musbotg_device_isoc_methods;
 
-static musbotg_cmd_t musbotg_setup_rx;
-static musbotg_cmd_t musbotg_setup_data_rx;
-static musbotg_cmd_t musbotg_setup_data_tx;
-static musbotg_cmd_t musbotg_setup_status;
-static musbotg_cmd_t musbotg_data_rx;
-static musbotg_cmd_t musbotg_data_tx;
+/* Control transfers: Device mode */
+static musbotg_cmd_t musbotg_dev_ctrl_setup_rx;
+static musbotg_cmd_t musbotg_dev_ctrl_data_rx;
+static musbotg_cmd_t musbotg_dev_ctrl_data_tx;
+static musbotg_cmd_t musbotg_dev_ctrl_status;
+
+/* Control transfers: Host mode */
+static musbotg_cmd_t musbotg_host_ctrl_setup_tx;
+static musbotg_cmd_t musbotg_host_ctrl_data_rx;
+static musbotg_cmd_t musbotg_host_ctrl_data_tx;
+static musbotg_cmd_t musbotg_host_ctrl_status_rx;
+static musbotg_cmd_t musbotg_host_ctrl_status_tx;
+
+/* Bulk, Interrupt, Isochronous: Device mode */
+static musbotg_cmd_t musbotg_dev_data_rx;
+static musbotg_cmd_t musbotg_dev_data_tx;
+
+/* Bulk, Interrupt, Isochronous: Host mode */
+static musbotg_cmd_t musbotg_host_data_rx;
+static musbotg_cmd_t musbotg_host_data_tx;
+
 static void	musbotg_device_done(struct usb_xfer *, usb_error_t);
 static void	musbotg_do_poll(struct usb_bus *);
 static void	musbotg_standard_done(struct usb_xfer *);
 static void	musbotg_interrupt_poll(struct musbotg_softc *);
 static void	musbotg_root_intr(struct musbotg_softc *);
+static int	musbotg_channel_alloc(struct musbotg_softc *, struct musbotg_td *td);
+static void	musbotg_channel_free(struct musbotg_softc *, struct musbotg_td *td);
+static void	musbotg_ep_int_set(struct musbotg_softc *sc, int channel, int on);
 
 /*
  * Here is a configuration that the chip supports.
@@ -123,6 +143,64 @@ static const struct usb_hw_ep_profile mu
 	}
 };
 
+static int
+musbotg_channel_alloc(struct musbotg_softc *sc, struct musbotg_td *td)
+{
+	int ch;
+	int ep;
+
+	ep = td->ep_no;
+
+	/* In device mode each EP got its own channel */
+	if (sc->sc_mode == MUSB2_DEVICE_MODE) {
+		musbotg_ep_int_set(sc, ep, 1);
+		return (ep);
+	}
+
+	/*
+	 * All control transactions go through EP0
+	 */
+	if (ep == 0) {
+		if (sc->sc_channel_mask & (1 << 0))
+			return (-1);
+		sc->sc_channel_mask |= (1 << 0);
+		musbotg_ep_int_set(sc, ep, 1);
+		return (0);
+	}
+
+	for (ch = 1; ch < MUSB2_EP_MAX; ch++) {
+		if (!(sc->sc_channel_mask & (1 << ch))) {
+			sc->sc_channel_mask |= (1 << ch);
+			musbotg_ep_int_set(sc, ch, 1);
+			return (ch);
+		}
+	}
+
+	DPRINTFN(-1, "No available channels. Mask: %04x\n",  sc->sc_channel_mask);
+
+	return (-1);
+}
+
+static void	
+musbotg_channel_free(struct musbotg_softc *sc, struct musbotg_td *td)
+{
+
+	DPRINTFN(1, "ep_no=%d\n", td->channel);
+
+	if (sc->sc_mode == MUSB2_DEVICE_MODE)
+		return;
+
+	if (td == NULL)
+		return;
+	if (td->channel == -1)
+		return;
+
+	musbotg_ep_int_set(sc, td->channel, 0);
+	sc->sc_channel_mask &= ~(1 << td->channel);
+
+	td->channel = -1;
+}
+
 static void
 musbotg_get_hw_ep_profile(struct usb_device *udev,
     const struct usb_hw_ep_profile **ppf, uint8_t ep_addr)
@@ -213,6 +291,46 @@ musbotg_pull_down(struct musbotg_softc *
 }
 
 static void
+musbotg_suspend_host(struct musbotg_softc *sc)
+{
+	uint8_t temp;
+
+	if (sc->sc_flags.status_suspend) {
+		return;
+	}
+
+	temp = MUSB2_READ_1(sc, MUSB2_REG_POWER);
+	temp |= MUSB2_MASK_SUSPMODE;
+	MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp);
+	sc->sc_flags.status_suspend = 1;
+}
+
+static void
+musbotg_wakeup_host(struct musbotg_softc *sc)
+{
+	uint8_t temp;
+
+	if (!(sc->sc_flags.status_suspend)) {
+		return;
+	}
+
+	temp = MUSB2_READ_1(sc, MUSB2_REG_POWER);
+	temp &= ~MUSB2_MASK_SUSPMODE;
+	temp |= MUSB2_MASK_RESUME;
+	MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp);
+
+	/* wait 20 milliseconds */
+	/* Wait for reset to complete. */
+	usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50);
+
+	temp = MUSB2_READ_1(sc, MUSB2_REG_POWER);
+	temp &= ~MUSB2_MASK_RESUME;
+	MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp);
+
+	sc->sc_flags.status_suspend = 0;
+}
+
+static void
 musbotg_wakeup_peer(struct musbotg_softc *sc)
 {
 	uint8_t temp;
@@ -243,7 +361,7 @@ musbotg_set_address(struct musbotg_softc
 }
 
 static uint8_t
-musbotg_setup_rx(struct musbotg_td *td)
+musbotg_dev_ctrl_setup_rx(struct musbotg_td *td)
 {
 	struct musbotg_softc *sc;
 	struct usb_device_request req;
@@ -253,6 +371,15 @@ musbotg_setup_rx(struct musbotg_td *td)
 	/* get pointer to softc */
 	sc = MUSBOTG_PC2SC(td->pc);
 
+	if (td->channel == -1)
+		td->channel = musbotg_channel_alloc(sc, td);
+
+	/* EP0 is busy, wait */
+	if (td->channel == -1)
+		return (1);
+
+	DPRINTFN(1, "ep_no=%d\n", td->channel);
+
 	/* select endpoint 0 */
 	MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
 
@@ -269,8 +396,10 @@ musbotg_setup_rx(struct musbotg_td *td)
 		/* do not stall at this point */
 		td->did_stall = 1;
 		/* wait for interrupt */
+		DPRINTFN(0, "CSR0 DATAEND\n");
 		goto not_complete;
 	}
+
 	if (csr & MUSB2_MASK_CSR0L_SENTSTALL) {
 		/* clear SENTSTALL */
 		MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
@@ -289,6 +418,7 @@ musbotg_setup_rx(struct musbotg_td *td)
 		sc->sc_ep0_busy = 0;
 	}
 	if (sc->sc_ep0_busy) {
+		DPRINTFN(0, "EP0 BUSY\n");
 		goto not_complete;
 	}
 	if (!(csr & MUSB2_MASK_CSR0L_RXPKTRDY)) {
@@ -337,6 +467,8 @@ musbotg_setup_rx(struct musbotg_td *td)
 	} else {
 		sc->sc_dv_addr = 0xFF;
 	}
+
+	musbotg_channel_free(sc, td);
 	return (0);			/* complete */
 
 not_complete:
@@ -350,10 +482,117 @@ not_complete:
 	return (1);			/* not complete */
 }
 
+static uint8_t
+musbotg_host_ctrl_setup_tx(struct musbotg_td *td)
+{
+	struct musbotg_softc *sc;
+	struct usb_device_request req;
+	uint8_t csr, csrh;
+
+	/* get pointer to softc */
+	sc = MUSBOTG_PC2SC(td->pc);
+
+	if (td->channel == -1)
+		td->channel = musbotg_channel_alloc(sc, td);
+
+	/* EP0 is busy, wait */
+	if (td->channel == -1)
+		return (1);
+
+	DPRINTFN(1, "ep_no=%d\n", td->channel);
+
+	/* select endpoint 0 */
+	MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+	/* read out FIFO status */
+	csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+	DPRINTFN(4, "csr=0x%02x\n", csr);
+
+	/* Not ready yet yet */
+	if (csr & MUSB2_MASK_CSR0L_TXPKTRDY)
+		return (1);
+
+	/* Failed */
+	if (csr & (MUSB2_MASK_CSR0L_RXSTALL |
+	    MUSB2_MASK_CSR0L_ERROR))
+	{
+		/* Clear status bit */
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+		DPRINTFN(1, "error bit set, csr=0x%02x\n", csr);
+		td->error = 1;
+	}
+
+	if (csr & MUSB2_MASK_CSR0L_NAKTIMO) {
+		DPRINTFN(1, "NAK timeout\n");
+
+		if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) {
+			csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH);
+			csrh |= MUSB2_MASK_CSR0H_FFLUSH;
+			MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh);
+			csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+			if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) {
+				csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH);
+				csrh |= MUSB2_MASK_CSR0H_FFLUSH;
+				MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh);
+				csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+			}
+		}
+
+		csr &= ~MUSB2_MASK_CSR0L_NAKTIMO;
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr);
+
+		td->error = 1;
+	}
+
+	if (td->error) {
+		musbotg_channel_free(sc, td);
+		return (0);
+	}
+
+	/* Fifo is not empty and there is no NAK timeout */
+	if (csr & MUSB2_MASK_CSR0L_TXPKTRDY)
+		return (1);
+
+	/* check if we are complete */
+	if (td->remainder == 0) {
+		/* we are complete */
+		musbotg_channel_free(sc, td);
+		return (0);
+	}
+
+	/* copy data into real buffer */
+	usbd_copy_out(td->pc, 0, &req, sizeof(req));
+
+	/* send data */
+	bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+	    MUSB2_REG_EPFIFO(0), (void *)&req, sizeof(req));
+
+	/* update offset and remainder */
+	td->offset += sizeof(req);
+	td->remainder -= sizeof(req);
+
+
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO);
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXFADDR(0), td->dev_addr);
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXHADDR(0), td->haddr);
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXHUBPORT(0), td->hport);
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXTI, td->transfer_type);
+
+	/* write command */
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+	    MUSB2_MASK_CSR0L_TXPKTRDY | 
+	    MUSB2_MASK_CSR0L_SETUPPKT);
+
+	/* Just to be consistent, not used above */
+	td->transaction_started = 1;
+
+	return (1);			/* in progress */
+}
+
 /* Control endpoint only data handling functions (RX/TX/SYNC) */
 
 static uint8_t
-musbotg_setup_data_rx(struct musbotg_td *td)
+musbotg_dev_ctrl_data_rx(struct musbotg_td *td)
 {
 	struct usb_page_search buf_res;
 	struct musbotg_softc *sc;
@@ -496,7 +735,7 @@ musbotg_setup_data_rx(struct musbotg_td 
 }
 
 static uint8_t
-musbotg_setup_data_tx(struct musbotg_td *td)
+musbotg_dev_ctrl_data_tx(struct musbotg_td *td)
 {
 	struct usb_page_search buf_res;
 	struct musbotg_softc *sc;
@@ -614,77 +853,943 @@ musbotg_setup_data_tx(struct musbotg_td 
 }
 
 static uint8_t
-musbotg_setup_status(struct musbotg_td *td)
+musbotg_host_ctrl_data_rx(struct musbotg_td *td)
 {
+	struct usb_page_search buf_res;
 	struct musbotg_softc *sc;
+	uint16_t count;
 	uint8_t csr;
+	uint8_t got_short;
 
 	/* get pointer to softc */
 	sc = MUSBOTG_PC2SC(td->pc);
 
+	if (td->channel == -1)
+		td->channel = musbotg_channel_alloc(sc, td);
+
+	/* EP0 is busy, wait */
+	if (td->channel == -1)
+		return (1);
+
+	DPRINTFN(1, "ep_no=%d\n", td->channel);
+
 	/* select endpoint 0 */
 	MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
 
-	if (sc->sc_ep0_busy) {
-		sc->sc_ep0_busy = 0;
-		sc->sc_ep0_cmd |= MUSB2_MASK_CSR0L_DATAEND;
-		MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd);
-		sc->sc_ep0_cmd = 0;
-	}
 	/* read out FIFO status */
 	csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
 
 	DPRINTFN(4, "csr=0x%02x\n", csr);
 
-	if (csr & MUSB2_MASK_CSR0L_DATAEND) {
-		/* wait for interrupt */
-		return (1);		/* not complete */
+	got_short = 0;
+	if (!td->transaction_started) {
+		td->transaction_started = 1;
+
+		MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, MAX_NAK_TO);
+
+		MUSB2_WRITE_1(sc, MUSB2_REG_RXFADDR(0),
+		    td->dev_addr);
+		MUSB2_WRITE_1(sc, MUSB2_REG_RXHADDR(0), td->haddr);
+		MUSB2_WRITE_1(sc, MUSB2_REG_RXHUBPORT(0), td->hport);
+		MUSB2_WRITE_1(sc, MUSB2_REG_RXTI, td->transfer_type);
+
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+		    MUSB2_MASK_CSR0L_REQPKT);
+
+		return (1);
+	}
+
+	if (csr & MUSB2_MASK_CSR0L_NAKTIMO) {
+		csr &= ~MUSB2_MASK_CSR0L_REQPKT;
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr);
+
+		csr &= ~MUSB2_MASK_CSR0L_NAKTIMO;
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr);
+
+		td->error = 1;
+	}
+
+	/* Failed */
+	if (csr & (MUSB2_MASK_CSR0L_RXSTALL |
+	    MUSB2_MASK_CSR0L_ERROR))
+	{
+		/* Clear status bit */
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+		DPRINTFN(1, "error bit set, csr=0x%02x\n", csr);
+		td->error = 1;
+	}
+
+	if (td->error) {
+		musbotg_channel_free(sc, td);
+		return (0);	/* we are complete */
+	}
+
+	if (!(csr & MUSB2_MASK_CSR0L_RXPKTRDY))
+		return (1); /* not yet */
+
+	/* get the packet byte count */
+	count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT);
+
+	/* verify the packet byte count */
+	if (count != td->max_frame_size) {
+		if (count < td->max_frame_size) {
+			/* we have a short packet */
+			td->short_pkt = 1;
+			got_short = 1;
+		} else {
+			/* invalid USB packet */
+			td->error = 1;
+			musbotg_channel_free(sc, td);
+			return (0);	/* we are complete */
+		}
+	}
+	/* verify the packet byte count */
+	if (count > td->remainder) {
+		/* invalid USB packet */
+		td->error = 1;
+		musbotg_channel_free(sc, td);
+		return (0);		/* we are complete */
+	}
+	while (count > 0) {
+		uint32_t temp;
+
+		usbd_get_page(td->pc, td->offset, &buf_res);
+
+		/* get correct length */
+		if (buf_res.length > count) {
+			buf_res.length = count;
+		}
+		/* check for unaligned memory address */
+		if (USB_P2U(buf_res.buffer) & 3) {
+
+			temp = count & ~3;
+
+			if (temp) {
+				/* receive data 4 bytes at a time */
+				bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
+				    MUSB2_REG_EPFIFO(0), sc->sc_bounce_buf,
+				    temp / 4);
+			}
+			temp = count & 3;
+			if (temp) {
+				/* receive data 1 byte at a time */
+				bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+				    MUSB2_REG_EPFIFO(0),
+				    (void *)(&sc->sc_bounce_buf[count / 4]), temp);
+			}
+			usbd_copy_in(td->pc, td->offset,
+			    sc->sc_bounce_buf, count);
+
+			/* update offset and remainder */
+			td->offset += count;
+			td->remainder -= count;
+			break;
+		}
+		/* check if we can optimise */
+		if (buf_res.length >= 4) {
+
+			/* receive data 4 bytes at a time */
+			bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
+			    MUSB2_REG_EPFIFO(0), buf_res.buffer,
+			    buf_res.length / 4);
+
+			temp = buf_res.length & ~3;
+
+			/* update counters */
+			count -= temp;
+			td->offset += temp;
+			td->remainder -= temp;
+			continue;
+		}
+		/* receive data */
+		bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+		    MUSB2_REG_EPFIFO(0), buf_res.buffer, buf_res.length);
+
+		/* update counters */
+		count -= buf_res.length;
+		td->offset += buf_res.length;
+		td->remainder -= buf_res.length;
+	}
+
+	csr &= ~MUSB2_MASK_CSR0L_RXPKTRDY;
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr);
+
+	/* check if we are complete */
+	if ((td->remainder == 0) || got_short) {
+		if (td->short_pkt) {
+			/* we are complete */
+
+			musbotg_channel_free(sc, td);
+			return (0);
+		}
+		/* else need to receive a zero length packet */
+	}
+
+	td->transaction_started = 1;
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+	    MUSB2_MASK_CSR0L_REQPKT);
+
+	return (1);			/* not complete */
+}
+
+static uint8_t
+musbotg_host_ctrl_data_tx(struct musbotg_td *td)
+{
+	struct usb_page_search buf_res;
+	struct musbotg_softc *sc;
+	uint16_t count;
+	uint8_t csr, csrh;
+
+	/* get pointer to softc */
+	sc = MUSBOTG_PC2SC(td->pc);
+
+	if (td->channel == -1)
+		td->channel = musbotg_channel_alloc(sc, td);
+
+	/* No free EPs */
+	if (td->channel == -1)
+		return (1);
+
+	DPRINTFN(1, "ep_no=%d\n", td->channel);
+
+	/* select endpoint */
+	MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+	/* read out FIFO status */
+	csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+	DPRINTFN(4, "csr=0x%02x\n", csr);
+
+	if (csr & (MUSB2_MASK_CSR0L_RXSTALL |
+	    MUSB2_MASK_CSR0L_ERROR)) {
+		/* clear status bits */
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+		td->error = 1;
+	}
+
+	if (csr & MUSB2_MASK_CSR0L_NAKTIMO ) {
+
+		if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) {
+			csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH);
+			csrh |= MUSB2_MASK_CSR0H_FFLUSH;
+			MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh);
+			csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+			if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) {
+				csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH);
+				csrh |= MUSB2_MASK_CSR0H_FFLUSH;
+				MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh);
+				csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+			}
+		}
+
+		csr &= ~MUSB2_MASK_CSR0L_NAKTIMO;
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr);
+
+		td->error = 1;
+	}
+
+
+	if (td->error) {
+		musbotg_channel_free(sc, td);
+		return (0);	/* complete */
+	}
+
+	/*
+	 * Wait while FIFO is empty. 
+	 * Do not flush it because it will cause transactions
+	 * with size more then packet size. It might upset
+	 * some devices
+	 */
+	if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY)
+		return (1);
+
+	/* Packet still being processed */
+	if (csr & MUSB2_MASK_CSR0L_TXPKTRDY)
+		return (1);
+
+	if (td->transaction_started) {
+		/* check remainder */
+		if (td->remainder == 0) {
+			if (td->short_pkt) {
+				musbotg_channel_free(sc, td);
+				return (0);	/* complete */
+			}
+			/* else we need to transmit a short packet */
+		}
+
+		/* We're not complete - more transactions required */
+		td->transaction_started = 0;
+	}
+
+	/* check for short packet */
+	count = td->max_frame_size;
+	if (td->remainder < count) {
+		/* we have a short packet */
+		td->short_pkt = 1;
+		count = td->remainder;
+	}
+
+	while (count > 0) {
+		uint32_t temp;
+
+		usbd_get_page(td->pc, td->offset, &buf_res);
+
+		/* get correct length */
+		if (buf_res.length > count) {
+			buf_res.length = count;
+		}
+		/* check for unaligned memory address */
+		if (USB_P2U(buf_res.buffer) & 3) {
+
+			usbd_copy_out(td->pc, td->offset,
+			    sc->sc_bounce_buf, count);
+
+			temp = count & ~3;
+
+			if (temp) {
+				/* transmit data 4 bytes at a time */
+				bus_space_write_multi_4(sc->sc_io_tag,
+				    sc->sc_io_hdl, MUSB2_REG_EPFIFO(0),
+				    sc->sc_bounce_buf, temp / 4);
+			}
+			temp = count & 3;
+			if (temp) {
+				/* receive data 1 byte at a time */
+				bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+				    MUSB2_REG_EPFIFO(0),
+				    ((void *)&sc->sc_bounce_buf[count / 4]), temp);
+			}
+			/* update offset and remainder */
+			td->offset += count;
+			td->remainder -= count;
+			break;
+		}
+		/* check if we can optimise */
+		if (buf_res.length >= 4) {
+
+			/* transmit data 4 bytes at a time */
+			bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
+			    MUSB2_REG_EPFIFO(0), buf_res.buffer,
+			    buf_res.length / 4);
+
+			temp = buf_res.length & ~3;
+
+			/* update counters */
+			count -= temp;
+			td->offset += temp;
+			td->remainder -= temp;
+			continue;
+		}
+		/* transmit data */
+		bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+		    MUSB2_REG_EPFIFO(0), buf_res.buffer,
+		    buf_res.length);
+
+		/* update counters */
+		count -= buf_res.length;
+		td->offset += buf_res.length;
+		td->remainder -= buf_res.length;
+	}
+
+	/* Function address */
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXFADDR(0), td->dev_addr);
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXHADDR(0), td->haddr);
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXHUBPORT(0), td->hport);
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXTI, td->transfer_type);
+
+	/* TX NAK timeout */
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO);
+
+	/* write command */
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+	    MUSB2_MASK_CSR0L_TXPKTRDY);
+
+	td->transaction_started = 1;
+
+	return (1);			/* not complete */
+}
+
+static uint8_t
+musbotg_dev_ctrl_status(struct musbotg_td *td)
+{
+	struct musbotg_softc *sc;
+	uint8_t csr;
+
+	/* get pointer to softc */
+	sc = MUSBOTG_PC2SC(td->pc);
+
+	/* select endpoint 0 */
+	MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+	if (sc->sc_ep0_busy) {
+		sc->sc_ep0_busy = 0;
+		sc->sc_ep0_cmd |= MUSB2_MASK_CSR0L_DATAEND;
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd);
+		sc->sc_ep0_cmd = 0;
+	}
+	/* read out FIFO status */
+	csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+
+	DPRINTFN(4, "csr=0x%02x\n", csr);
+
+	if (csr & MUSB2_MASK_CSR0L_DATAEND) {
+		/* wait for interrupt */
+		return (1);		/* not complete */
+	}
+	if (sc->sc_dv_addr != 0xFF) {
+		/* write function address */
+		musbotg_set_address(sc, sc->sc_dv_addr);
+	}
+
+	musbotg_channel_free(sc, td);
+	return (0);			/* complete */
+}
+
+static uint8_t
+musbotg_host_ctrl_status_rx(struct musbotg_td *td)
+{
+	struct musbotg_softc *sc;
+	uint8_t csr, csrh;
+
+	/* get pointer to softc */
+	sc = MUSBOTG_PC2SC(td->pc);
+
+	if (td->channel == -1)
+		td->channel = musbotg_channel_alloc(sc, td);
+
+	/* EP0 is busy, wait */
+	if (td->channel == -1)
+		return (1);
+
+	DPRINTFN(1, "ep_no=%d\n", td->channel);
+
+	/* select endpoint 0 */
+	MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+	if (!td->transaction_started) {
+		MUSB2_WRITE_1(sc, MUSB2_REG_RXFADDR(0),
+		    td->dev_addr);
+
+		MUSB2_WRITE_1(sc, MUSB2_REG_RXHADDR(0), td->haddr);
+		MUSB2_WRITE_1(sc, MUSB2_REG_RXHUBPORT(0), td->hport);
+		MUSB2_WRITE_1(sc, MUSB2_REG_RXTI, td->transfer_type);
+
+		/* RX NAK timeout */
+		MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, MAX_NAK_TO);
+
+		td->transaction_started = 1;
+
+		/* Disable PING */
+		csrh = MUSB2_READ_1(sc, MUSB2_REG_RXCSRH);
+		csrh |= MUSB2_MASK_CSR0H_PING_DIS;
+		MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, csrh);
+
+		/* write command */
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+		    MUSB2_MASK_CSR0L_STATUSPKT | 
+		    MUSB2_MASK_CSR0L_REQPKT);
+
+		return (1); /* Just started */
+
+	}
+
+	csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+
+	DPRINTFN(4, "IN STATUS csr=0x%02x\n", csr);
+
+	if (csr & MUSB2_MASK_CSR0L_RXPKTRDY) {
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+		    MUSB2_MASK_CSR0L_RXPKTRDY_CLR);
+		musbotg_channel_free(sc, td);
+		return (0); /* complete */
+	}
+
+	if (csr & MUSB2_MASK_CSR0L_NAKTIMO) {
+		csr &= ~ (MUSB2_MASK_CSR0L_STATUSPKT |
+		    MUSB2_MASK_CSR0L_REQPKT);
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr);
+
+		csr &= ~MUSB2_MASK_CSR0L_NAKTIMO;
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr);
+		td->error = 1;
+	}
+
+	/* Failed */
+	if (csr & (MUSB2_MASK_CSR0L_RXSTALL |
+	    MUSB2_MASK_CSR0L_ERROR))
+	{
+		/* Clear status bit */
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+		DPRINTFN(1, "error bit set, csr=0x%02x\n", csr);
+		td->error = 1;
+	}
+
+	if (td->error) {
+		musbotg_channel_free(sc, td);
+		return (0);
+	}
+
+	return (1);			/* Not ready yet */
+}
+
+static uint8_t
+musbotg_host_ctrl_status_tx(struct musbotg_td *td)
+{
+	struct musbotg_softc *sc;
+	uint8_t csr;
+
+	/* get pointer to softc */
+	sc = MUSBOTG_PC2SC(td->pc);
+
+	if (td->channel == -1)
+		td->channel = musbotg_channel_alloc(sc, td);
+
+	/* EP0 is busy, wait */
+	if (td->channel == -1)
+		return (1);
+
+	DPRINTFN(1, "ep_no=%d/%d [%d@%d.%d/%02x]\n", td->channel, td->transaction_started, 
+			td->dev_addr,td->haddr,td->hport, td->transfer_type);
+
+	/* select endpoint 0 */
+	MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+	csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+	DPRINTFN(4, "csr=0x%02x\n", csr);
+
+	/* Not yet */
+	if (csr & MUSB2_MASK_CSR0L_TXPKTRDY)
+		return (1);
+
+	/* Failed */
+	if (csr & (MUSB2_MASK_CSR0L_RXSTALL |
+	    MUSB2_MASK_CSR0L_ERROR))
+	{
+		/* Clear status bit */
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+		DPRINTFN(1, "error bit set, csr=0x%02x\n", csr);
+		td->error = 1;
+		musbotg_channel_free(sc, td);
+		return (0); /* complete */
+	}
+
+	if (td->transaction_started) {
+		musbotg_channel_free(sc, td);
+		return (0); /* complete */
+	} 
+
+	MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, MUSB2_MASK_CSR0H_PING_DIS);
+
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXFADDR(0), td->dev_addr);
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXHADDR(0), td->haddr);
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXHUBPORT(0), td->hport);
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXTI, td->transfer_type);
+
+	/* TX NAK timeout */
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO);
+
+	td->transaction_started = 1;
+
+	/* write command */
+	MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+	    MUSB2_MASK_CSR0L_STATUSPKT | 
+	    MUSB2_MASK_CSR0L_TXPKTRDY);
+
+	return (1);			/* wait for interrupt */
+}
+
+static uint8_t
+musbotg_dev_data_rx(struct musbotg_td *td)
+{
+	struct usb_page_search buf_res;
+	struct musbotg_softc *sc;
+	uint16_t count;
+	uint8_t csr;
+	uint8_t to;
+	uint8_t got_short;
+
+	to = 8;				/* don't loop forever! */
+	got_short = 0;
+
+	/* get pointer to softc */
+	sc = MUSBOTG_PC2SC(td->pc);
+
+	if (td->channel == -1)
+		td->channel = musbotg_channel_alloc(sc, td);
+
+	/* EP0 is busy, wait */
+	if (td->channel == -1)
+		return (1);
+
+	/* select endpoint */
+	MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->channel);
+
+repeat:
+	/* read out FIFO status */
+	csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL);
+
+	DPRINTFN(4, "csr=0x%02x\n", csr);
+
+	/* clear overrun */
+	if (csr & MUSB2_MASK_CSRL_RXOVERRUN) {
+		/* make sure we don't clear "RXPKTRDY" */
+		MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL,
+		    MUSB2_MASK_CSRL_RXPKTRDY);
+	}
+
+	/* check status */
+	if (!(csr & MUSB2_MASK_CSRL_RXPKTRDY))
+		return (1); /* not complete */
+
+	/* get the packet byte count */
+	count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT);
+
+	DPRINTFN(4, "count=0x%04x\n", count);
+
+	/*
+	 * Check for short or invalid packet:
+	 */
+	if (count != td->max_frame_size) {
+		if (count < td->max_frame_size) {
+			/* we have a short packet */
+			td->short_pkt = 1;
+			got_short = 1;
+		} else {
+			/* invalid USB packet */
+			td->error = 1;
+			musbotg_channel_free(sc, td);
+			return (0);	/* we are complete */
+		}
+	}
+	/* verify the packet byte count */
+	if (count > td->remainder) {
+		/* invalid USB packet */
+		td->error = 1;
+		musbotg_channel_free(sc, td);
+		return (0);		/* we are complete */
+	}
+	while (count > 0) {
+		uint32_t temp;
+
+		usbd_get_page(td->pc, td->offset, &buf_res);
+
+		/* get correct length */
+		if (buf_res.length > count) {
+			buf_res.length = count;
+		}
+		/* check for unaligned memory address */
+		if (USB_P2U(buf_res.buffer) & 3) {
+
+			temp = count & ~3;
+
+			if (temp) {
+				/* receive data 4 bytes at a time */
+				bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
+				    MUSB2_REG_EPFIFO(td->channel), sc->sc_bounce_buf,
+				    temp / 4);
+			}
+			temp = count & 3;
+			if (temp) {
+				/* receive data 1 byte at a time */
+				bus_space_read_multi_1(sc->sc_io_tag,
+				    sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->channel),
+				    ((void *)&sc->sc_bounce_buf[count / 4]), temp);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
Comment 6 dfilter service freebsd_committer freebsd_triage 2013-10-24 08:38:40 UTC
Author: hselasky
Date: Thu Oct 24 07:38:32 2013
New Revision: 257043
URL: http://svnweb.freebsd.org/changeset/base/257043

Log:
  MFC r256548:
  Correct programming of XXX_MAXP register. This register is 16-bit wide
  and not 8-bit. Fix support for isochronous transfers in USB host mode.
  Fix a whitespace while at it.
  
  PR:		usb/181987
  Approved by:	re (Xin Li)

Modified:
  stable/10/sys/dev/usb/controller/musb_otg.c
  stable/10/sys/dev/usb/controller/musb_otg.h
Directory Properties:
  stable/10/sys/   (props changed)

Modified: stable/10/sys/dev/usb/controller/musb_otg.c
==============================================================================
--- stable/10/sys/dev/usb/controller/musb_otg.c	Thu Oct 24 06:25:52 2013	(r257042)
+++ stable/10/sys/dev/usb/controller/musb_otg.c	Thu Oct 24 07:38:32 2013	(r257043)
@@ -1661,7 +1661,7 @@ repeat:
 	}
 
 	/* Max packet size */
-	MUSB2_WRITE_1(sc, MUSB2_REG_TXMAXP, td->max_packet);
+	MUSB2_WRITE_2(sc, MUSB2_REG_TXMAXP, td->reg_max_packet);
 
 	/* write command */
 	MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
@@ -1726,13 +1726,16 @@ repeat:
 		    td->hport);
 
 		/* RX NAK timeout */
-		MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, MAX_NAK_TO);
+		if (td->transfer_type & MUSB2_MASK_TI_PROTO_ISOC)
+			MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, 0);
+		else
+			MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, MAX_NAK_TO);
 
 		/* Protocol, speed, device endpoint */
 		MUSB2_WRITE_1(sc, MUSB2_REG_RXTI, td->transfer_type);
 
 		/* Max packet size */
-		MUSB2_WRITE_1(sc, MUSB2_REG_RXMAXP, td->max_packet);
+		MUSB2_WRITE_2(sc, MUSB2_REG_RXMAXP, td->reg_max_packet);
 
 		/* Data Toggle */
 		csrh = MUSB2_READ_1(sc, MUSB2_REG_RXCSRH);
@@ -1938,7 +1941,7 @@ musbotg_host_data_tx(struct musbotg_td *
 		return (0);	/* complete */
 	}
 
-	if (csr & MUSB2_MASK_CSRL_TXNAKTO ) {
+	if (csr & MUSB2_MASK_CSRL_TXNAKTO) {
 		/* 
 		 * Flush TX FIFO before clearing NAK TO
 		 */
@@ -2069,13 +2072,16 @@ musbotg_host_data_tx(struct musbotg_td *
 	    td->hport);
 
 	/* TX NAK timeout */
-	MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO);
+	if (td->transfer_type & MUSB2_MASK_TI_PROTO_ISOC)
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, 0);
+	else
+		MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO);
 
 	/* Protocol, speed, device endpoint */
 	MUSB2_WRITE_1(sc, MUSB2_REG_TXTI, td->transfer_type);
 
 	/* Max packet size */
-	MUSB2_WRITE_1(sc, MUSB2_REG_TXMAXP, td->max_packet);
+	MUSB2_WRITE_2(sc, MUSB2_REG_TXMAXP, td->reg_max_packet);
 
 	if (!td->transaction_started) {
 		csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH);
@@ -2406,7 +2412,6 @@ musbotg_setup_standard_chain(struct usb_
 
 	if (xfer->flags_int.usb_mode == USB_MODE_HOST) {
 		speed =  usbd_get_speed(xfer->xroot->udev);
-		xfer_type = xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE;
 
 		switch (speed) {
 			case USB_SPEED_LOW:
@@ -2444,7 +2449,6 @@ musbotg_setup_standard_chain(struct usb_
 		}
 
 		temp.transfer_type |= ep_no;
-		td->max_packet = xfer->max_packet_size;
 		td->toggle = xfer->endpoint->toggle_next;
 	}
 
@@ -2469,9 +2473,9 @@ musbotg_setup_standard_chain(struct usb_
 		x = 0;
 	}
 
-	if (x != xfer->nframes) {
-		tx = 0;
+	tx = 0;
 
+	if (x != xfer->nframes) {
 		if (xfer->endpointno & UE_DIR_IN)
 		    	tx = 1;
 
@@ -2532,9 +2536,14 @@ musbotg_setup_standard_chain(struct usb_
 
 		} else {
 
-			/* regular data transfer */
-
-			temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1;
+			if (xfer->flags_int.isochronous_xfr) {
+				/* isochronous data transfer */
+				/* don't force short */
+				temp.short_pkt = 1;
+			} else {
+				/* regular data transfer */
+				temp.short_pkt = (xfer->flags.force_short_xfer ? 0 : 1);
+			}
 		}
 
 		musbotg_setup_standard_chain_sub(&temp);
@@ -3158,7 +3167,12 @@ musbotg_init(struct musbotg_softc *sc)
 
 		if (dynfifo) {
 			if (frx && (temp <= nrx)) {
-				if (temp < 8) {
+				if (temp == 1) {
+					frx = 12;	/* 4K */
+					MUSB2_WRITE_1(sc, MUSB2_REG_RXFIFOSZ, 
+					    MUSB2_VAL_FIFOSZ_4096 |
+					    MUSB2_MASK_FIFODB);
+				} else if (temp < 8) {
 					frx = 10;	/* 1K */
 					MUSB2_WRITE_1(sc, MUSB2_REG_RXFIFOSZ, 
 					    MUSB2_VAL_FIFOSZ_512 |
@@ -3175,7 +3189,12 @@ musbotg_init(struct musbotg_softc *sc)
 				offset += (1 << frx);
 			}
 			if (ftx && (temp <= ntx)) {
-				if (temp < 8) {
+				if (temp == 1) {
+					ftx = 12;	/* 4K */
+					MUSB2_WRITE_1(sc, MUSB2_REG_TXFIFOSZ,
+	 				    MUSB2_VAL_FIFOSZ_4096 |
+	 				    MUSB2_MASK_FIFODB);
+				} else if (temp < 8) {
 					ftx = 10;	/* 1K */
 					MUSB2_WRITE_1(sc, MUSB2_REG_TXFIFOSZ,
 	 				    MUSB2_VAL_FIFOSZ_512 |
@@ -4042,7 +4061,7 @@ musbotg_xfer_setup(struct usb_setup_para
 	 * reasonable dummies:
 	 */
 	parm->hc_max_packet_size = 0x400;
-	parm->hc_max_frame_size = 0x400;
+	parm->hc_max_frame_size = 0xc00;
 
 	if ((parm->methods == &musbotg_device_isoc_methods) ||
 	    (parm->methods == &musbotg_device_intr_methods))
@@ -4117,6 +4136,8 @@ musbotg_xfer_setup(struct usb_setup_para
 
 			/* init TD */
 			td->max_frame_size = xfer->max_frame_size;
+			td->reg_max_packet = xfer->max_packet_size |
+			    ((xfer->max_packet_count - 1) << 11);
 			td->ep_no = ep_no;
 			td->obj_next = last_obj;
 

Modified: stable/10/sys/dev/usb/controller/musb_otg.h
==============================================================================
--- stable/10/sys/dev/usb/controller/musb_otg.h	Thu Oct 24 06:25:52 2013	(r257042)
+++ stable/10/sys/dev/usb/controller/musb_otg.h	Thu Oct 24 07:38:32 2013	(r257043)
@@ -316,9 +316,9 @@ struct musbotg_td {
 	uint32_t offset;
 	uint32_t remainder;
 	uint16_t max_frame_size;	/* packet_size * mult */
+	uint16_t reg_max_packet;
 	uint8_t	ep_no;
 	uint8_t	transfer_type;
-	uint8_t	max_packet;
 	uint8_t	error:1;
 	uint8_t	alt_next:1;
 	uint8_t	short_pkt:1;
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
Comment 7 Eitan Adler freebsd_committer freebsd_triage 2017-12-31 07:59:38 UTC
For bugs matching the following criteria:

Status: In Progress Changed: (is less than) 2014-06-01

Reset to default assignee and clear in-progress tags.

Mail being skipped
Comment 8 Oleksandr Tymoshenko freebsd_committer freebsd_triage 2019-01-21 09:22:17 UTC
There is a commit referencing this PR, but it's still not closed and has been inactive for some time. Closing the PR as fixed but feel free to re-open it if the issue hasn't been completely resolved.

Thanks