PERFORCE change 131192 for review

Hans Petter Selasky hselasky at FreeBSD.org
Tue Dec 18 17:39:12 PST 2007


http://perforce.freebsd.org/chv.cgi?CH=131192

Change 131192 by hselasky at hselasky_laptop001 on 2007/12/19 01:38:19

	
	This commit is USB device side related.
	
	Bring in the first complete version of the
	AT91 USB Device Controller Interface driver.
	
	There are still some things left to do before
	it is fully functional, but most things are
	in place.

Affected files ...

.. //depot/projects/usb/src/sys/dev/usb/at9100_dci.c#2 edit
.. //depot/projects/usb/src/sys/dev/usb/at9100_dci.h#2 edit

Differences ...

==== //depot/projects/usb/src/sys/dev/usb/at9100_dci.c#2 (text+ko) ====

@@ -31,6 +31,17 @@
  * This file contains the driver for the AT91 series USB Device
  * Controller
  */
+
+/*
+ * Thanks to "David Brownell" for helping out regarding the hardware
+ * endpoint profiles.
+ */
+
+/*
+ * NOTE: the "fifo_bank" is not reset in hardware when the endpoint is
+ * reset !
+ */
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
@@ -71,10 +82,26 @@
 
 /* prototypes */
 
+struct usbd_bus_methods at9100_dci_bus_methods;
+struct usbd_pipe_methods at9100_dci_device_bulk_methods;
+struct usbd_pipe_methods at9100_dci_device_ctrl_methods;
+struct usbd_pipe_methods at9100_dci_device_intr_methods;
+struct usbd_pipe_methods at9100_dci_device_isoc_fs_methods;
+struct usbd_pipe_methods at9100_dci_root_ctrl_methods;
+struct usbd_pipe_methods at9100_dci_root_intr_methods;
+
 static at9100_dci_cmd_t at9100_dci_setup_rx;
 static at9100_dci_cmd_t at9100_dci_data_rx;
 static at9100_dci_cmd_t at9100_dci_data_tx;
 static at9100_dci_cmd_t at9100_dci_data_tx_sync;
+static void at9100_dci_device_done(struct usbd_xfer *xfer, usbd_status_t error);
+static void at9100_dci_do_poll(struct usbd_bus *bus);
+static void at9100_dci_root_ctrl_poll(struct at9100_dci_softc *sc);
+static void at9100_dci_standard_done(struct usbd_xfer *xfer);
+
+static usbd_std_root_transfer_func_t at9100_dci_root_intr_done;
+static usbd_std_root_transfer_func_t at9100_dci_root_ctrl_done;
+static usbd_config_td_command_t at9100_dci_root_ctrl_task;
 
 /*
  * NOTE: Some of the bits in the CSR register have inverse meaning so
@@ -86,12 +113,162 @@
   (csr) |= ((AT91_UDP_CSR_RX_DATA_BK0|		\
 	     AT91_UDP_CSR_RX_DATA_BK1|		\
 	     AT91_UDP_CSR_TXCOMP|		\
-	     AT91_UDP_CSR_RXSETUP) ^ (what));	\
+	     AT91_UDP_CSR_RXSETUP|		\
+	     AT91_UDP_CSR_STALLSENT) ^ (what));	\
 } while (0)
 
+/*
+ * Here is a list of what the chip supports.
+ * Probably it supports more than listed here!
+ */
+static const struct usbd_hw_ep_profile
+	at9100_dci_ep_profile[AT91_UDP_EP_MAX] = {
+
+	[0] = {
+		.max_frame_size = 8,
+		.is_simplex = 1,
+		.support_control = 1,
+	},
+	[1] = {
+		.max_frame_size = 64,
+		.is_simplex = 1,
+		.support_multi_buffer = 1,
+		.support_bulk = 1,
+		.support_interrupt = 1,
+		.support_isochronous = 1,
+		.support_in = 1,
+		.support_out = 1,
+	},
+	[2] = {
+		.max_frame_size = 64,
+		.is_simplex = 1,
+		.support_multi_buffer = 1,
+		.support_bulk = 1,
+		.support_interrupt = 1,
+		.support_isochronous = 1,
+		.support_in = 1,
+		.support_out = 1,
+	},
+	[3] = {
+		/* can also do BULK */
+		.max_frame_size = 8,
+		.is_simplex = 1,
+		.support_interrupt = 1,
+		.support_in = 1,
+		.support_out = 1,
+	},
+	[4] = {
+		.max_frame_size = 256,
+		.is_simplex = 1,
+		.support_multi_buffer = 1,
+		.support_bulk = 1,
+		.support_interrupt = 1,
+		.support_isochronous = 1,
+		.support_in = 1,
+		.support_out = 1,
+	},
+	[5] = {
+		.max_frame_size = 256,
+		.is_simplex = 1,
+		.support_multi_buffer = 1,
+		.support_bulk = 1,
+		.support_interrupt = 1,
+		.support_isochronous = 1,
+		.support_in = 1,
+		.support_out = 1,
+	},
+};
+
+static void
+at9100_dci_get_hw_ep_profile(struct usbd_device *udev,
+    const struct usbd_hw_ep_profile **ppf, uint8_t ep_addr)
+{
+	if (ep_addr < AT91_UDP_EP_MAX) {
+		*ppf = (at9100_dci_ep_profile + ep_addr);
+	} else {
+		*ppf = NULL;
+	}
+	return;
+}
+
+static void
+at9100_dci_clocks_on(struct at9100_dci_softc *sc)
+{
+	if (sc->sc_flags.clocks_off &&
+	    sc->sc_flags.port_powered) {
+
+		if (sc->sc_clocks_on) {
+			(sc->sc_clocks_on) (sc->sc_clocks_arg);
+		}
+		sc->sc_flags.clocks_off = 0;
+
+		/* enable Transceiver */
+		AT91_UDP_WRITE_4(sc, AT91_UDP_TXVC, 0);
+	}
+	return;
+}
+
+static void
+at9100_dci_clocks_off(struct at9100_dci_softc *sc)
+{
+	if (!sc->sc_flags.clocks_off) {
+
+		/* disable Transceiver */
+		AT91_UDP_WRITE_4(sc, AT91_UDP_TXVC, AT91_UDP_TXVC_DIS);
+
+		if (sc->sc_clocks_off) {
+			(sc->sc_clocks_off) (sc->sc_clocks_arg);
+		}
+		sc->sc_flags.clocks_off = 1;
+	}
+	return;
+}
+
+static void
+at9100_dci_pull_up(struct at9100_dci_softc *sc)
+{
+	if (!sc->sc_flags.d_pulled_up &&
+	    sc->sc_flags.port_powered) {
+		sc->sc_flags.d_pulled_up = 1;
+		(sc->sc_pull_up) (sc->sc_pull_arg);
+	}
+	return;
+}
+
+static void
+at9100_dci_pull_down(struct at9100_dci_softc *sc)
+{
+	if (sc->sc_flags.d_pulled_up) {
+		sc->sc_flags.d_pulled_up = 0;
+		(sc->sc_pull_down) (sc->sc_pull_arg);
+	}
+	return;
+}
+
+static void
+at9100_dci_wakeup_peer(struct at9100_dci_softc *sc)
+{
+	uint32_t temp;
+
+	if (!(sc->sc_flags.status_suspend)) {
+		return;
+	}
+	temp = AT91_UDP_READ_4(sc, AT91_UDP_GSTATE);
+
+	if (!(temp & AT91_UDP_GSTATE_ESR)) {
+		return;
+	}
+	temp |= AT91_UDP_GSTATE_ESR;
+
+	AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, temp);
+
+	return;
+}
+
 static uint8_t
 at9100_dci_setup_rx(struct at9100_dci_td *td)
 {
+	struct usbd_page_search buf_res;
 	uint32_t csr;
 	uint16_t count;
 
@@ -109,26 +286,40 @@
 	count = (csr & AT91_UDP_CSR_RXBYTECNT) >> 16;
 
 	/* verify data length */
-	if (count != 8) {
+	if (count != td->remainder) {
 		DPRINTFN(0, "Invalid SETUP packet "
 		    "length, %d bytes\n", count);
 		td->error = 1;
 		return (0);		/* we are complete */
 	}
-	/* receive data */
-	bus_space_read_multi_1(td->io_tag, td->io_hdl,
-	    td->fifo_reg, td->buffer, count);
+	while (count > 0) {
+		usbd_get_page(td->pc, td->offset, &buf_res);
+
+		/* get correct length */
+		if (buf_res.length > count) {
+			buf_res.length = count;
+		}
+		/* receive data */
+		bus_space_read_multi_1(td->io_tag, td->io_hdl,
+		    td->fifo_reg, buf_res.buffer, buf_res.length);
+
+		if (td->offset == 0) {
+			/* sneak peek the endpoint direction */
+			if ((*(uint8_t *)(buf_res.buffer)) & UE_DIR_IN) {
+				csr |= AT91_UDP_CSR_DIR;
+			} else {
+				csr &= ~AT91_UDP_CSR_DIR;
+			}
+		}
+		/* update counters */
+		count -= buf_res.length;
+		td->offset += buf_res.length;
+		td->remainder -= buf_res.length;
+	}
 
 	/* clear RXSETUP */
 	AT91_CSR_ACK(csr, AT91_UDP_CSR_RXSETUP);
 
-	/* sneak peek the endpoint direction */
-	if ((*(uint8_t *)(td->buffer)) & UE_DIR_IN) {
-		csr |= AT91_UDP_CSR_DIR;
-	} else {
-		csr &= ~AT91_UDP_CSR_DIR;
-	}
-
 	/* write command */
 	bus_space_write_4(td->io_tag, td->io_hdl,
 	    td->status_reg, csr);
@@ -139,6 +330,7 @@
 static uint8_t
 at9100_dci_data_rx(struct at9100_dci_td *td)
 {
+	struct usbd_page_search buf_res;
 	uint32_t csr;
 	uint16_t count;
 	uint8_t to;
@@ -164,7 +356,7 @@
 		/* verify the packet byte count */
 		if (count != td->max_packet_size) {
 			if (count < td->max_packet_size) {
-				/* short packet */
+				/* we have a short packet */
 				td->short_pkt = 1;
 			} else {
 				/* invalid USB packet */
@@ -178,16 +370,25 @@
 			td->error = 1;
 			return (0);	/* we are complete */
 		}
-		/* receive data */
-		bus_space_read_multi_1(td->io_tag, td->io_hdl,
-		    td->fifo_reg, td->buffer, count);
+		while (count > 0) {
+			usbd_get_page(td->pc, td->offset, &buf_res);
+
+			/* get correct length */
+			if (buf_res.length > count) {
+				buf_res.length = count;
+			}
+			/* receive data */
+			bus_space_read_multi_1(td->io_tag, td->io_hdl,
+			    td->fifo_reg, buf_res.buffer, buf_res.length);
 
-		/* update counters */
-		td->remainder -= count;
-		td->buffer = USBD_ADD_BYTES(td->buffer, count);
+			/* update counters */
+			count -= buf_res.length;
+			td->offset += buf_res.length;
+			td->remainder -= buf_res.length;
+		}
 
 		/* clear status bits */
-		if (td->double_buffer) {
+		if (td->support_multi_buffer) {
 			if (td->fifo_bank) {
 				td->fifo_bank = 0;
 				AT91_CSR_ACK(csr, AT91_UDP_CSR_RX_DATA_BK1);
@@ -208,6 +409,15 @@
 		 * NOTE: We may have to delay a little bit before
 		 * proceeding after clearing the DATA_BK bits.
 		 */
+
+		/* check if we are complete */
+		if (td->remainder == 0) {
+			if (td->short_pkt) {
+				/* we are complete */
+				return (0);
+			}
+			/* else need to receive a zero length packet */
+		}
 	} while (--to);
 	return (1);			/* not complete */
 }
@@ -215,6 +425,7 @@
 static uint8_t
 at9100_dci_data_tx(struct at9100_dci_td *td)
 {
+	struct usbd_page_search buf_res;
 	uint32_t csr;
 	uint16_t count;
 
@@ -236,17 +447,26 @@
 		count = td->max_packet_size;
 		if (td->remainder < count) {
 			/* we have a short packet */
-			td->short_pkt = 0;
-
+			td->short_pkt = 1;
 			count = td->remainder;
 		}
-		/* transmit data */
-		bus_space_write_multi_1(td->io_tag, td->io_hdl,
-		    td->fifo_reg, td->buffer, count);
+		while (count > 0) {
+
+			usbd_get_page(td->pc, td->offset, &buf_res);
+
+			/* get correct length */
+			if (buf_res.length > count) {
+				buf_res.length = count;
+			}
+			/* transmit data */
+			bus_space_write_multi_1(td->io_tag, td->io_hdl,
+			    td->fifo_reg, buf_res.buffer, buf_res.length);
 
-		/* update counters */
-		td->remainder -= count;
-		td->buffer = USBD_ADD_BYTES(td->buffer, count);
+			/* update counters */
+			count -= buf_res.length;
+			td->offset += buf_res.length;
+			td->remainder -= buf_res.length;
+		}
 
 		/* write command */
 		bus_space_write_4(td->io_tag, td->io_hdl,
@@ -254,15 +474,20 @@
 
 		/* check remainder */
 		if (td->remainder == 0) {
-			if (!td->short_pkt) {
+			if (td->short_pkt) {
 				return (0);	/* complete */
 			}
+			/* else we need to transmit a short packet */
 		}
 		/* check for double buffering */
-		if (!td->double_buffer) {
+		if (!td->support_multi_buffer) {
+			break;
+		}
+		/* check if we can do a multi buffer */
+		if (td->did_multi_buffer) {
 			break;
 		}
-		td->double_buffer = 0;
+		td->did_multi_buffer = 1;
 
 		/* only set TXPKTRDY next time  */
 		AT91_CSR_ACK(csr, AT91_UDP_CSR_TXPKTRDY);
@@ -273,6 +498,7 @@
 static uint8_t
 at9100_dci_data_tx_sync(struct at9100_dci_td *td)
 {
+	struct usbd_xfer *xfer;
 	uint32_t csr;
 
 	/* read out FIFO status */
@@ -294,11 +520,13 @@
 		bus_space_write_4(td->io_tag, td->io_hdl,
 		    td->status_reg, csr);
 
-		if (!td->double_buffer) {
+		if (!td->did_multi_buffer) {
+			/* restore double buffer flag */
+			xfer = td->pc->xfer;
 			return (0);	/* complete */
 		}
 		/* wait for the second and final interrupt */
-		td->double_buffer = 0;
+		td->did_multi_buffer = 0;
 	}
 	return (1);			/* not complete */
 }
@@ -306,8 +534,8 @@
 static uint8_t
 at9100_dci_xfer_do_fifo(struct usbd_xfer *xfer)
 {
+	struct at9100_dci_softc *sc;
 	struct at9100_dci_td *td;
-	struct at9100_dci_td *td_next;
 	uint8_t temp;
 
 	td = xfer->td_transfer_cache;
@@ -321,93 +549,1753 @@
 		}
 		if (td->error) {
 			goto done;
-		} else if (td->short_pkt) {
+		} else if (td->remainder > 0) {
+			/*
+			 * We had a short transfer. If there is no alternate
+			 * next, stop processing !
+			 */
 			if (!td->alt_next) {
 				goto done;
 			}
-			td_next = td->alt_next;
-		} else {
-			td_next = td->next;
 		}
-
 		/*
-		 * Fetch the next transfer descriptor and transfer the
-		 * "fifo_bank" value to the next transfer descriptor
+		 * Fetch the next transfer descriptor and transfer
+		 * some flags to the next transfer descriptor
 		 */
-		temp = td->fifo_bank;
-		td = td_next;
+		temp = 0;
+		if (td->fifo_bank)
+			temp |= 1;
+		if (td->did_multi_buffer)
+			temp |= 2;
+		td = td->obj_next;
 		xfer->td_transfer_cache = td;
-		td->fifo_bank = temp;
+		if (temp & 1)
+			td->fifo_bank = 1;
+		if (temp & 2)
+			td->did_multi_buffer = 1;
 	}
 	return (1);			/* not complete */
 
 done:
-	/* update the "fifo_bank" shadow */
-	xfer->flags_int.custom_00 = td->fifo_bank;
+	sc = xfer->usb_sc;
+	temp = (xfer->pipe->edesc->bEndpointAddress & UE_ADDR);
+
+	/* update FIFO bank flag and multi buffer */
+	if (td->fifo_bank) {
+		sc->sc_ep_flags[temp].fifo_bank = 1;
+	} else {
+		sc->sc_ep_flags[temp].fifo_bank = 0;
+	}
+
+	/* update multi buffer flag */
+	if (td->did_multi_buffer) {
+		sc->sc_ep_flags[temp].did_multi_buffer = 1;
+	} else {
+		sc->sc_ep_flags[temp].did_multi_buffer = 0;
+	}
+
+	/* compute all actual lengths */
+
+	at9100_dci_standard_done(xfer);
+
 	return (0);			/* complete */
 }
 
 static void
-at9100_dci_interrupt_td(struct at9100_dci_softc *sc, struct thread *ctd)
+at9100_dci_interrupt_poll(struct at9100_dci_softc *sc)
 {
-	struct usbd_xfer *xlist[AT91_UDP_EP_MAX + 1];
-	struct usbd_xfer **xptr;
 	struct usbd_xfer *xfer;
-	struct thread *td;
+
+	LIST_FOREACH(xfer, &sc->sc_bus.intr_list_head, interrupt_list) {
+		if (!at9100_dci_xfer_do_fifo(xfer)) {
+			/* queue callback for execution */
+			usbd_callback_wrapper(xfer, NULL,
+			    USBD_CONTEXT_CALLBACK);
+		}
+	}
+	return;
+}
+
+static void
+at9100_dci_vbus_interrupt(struct usbd_bus *bus, uint8_t is_on)
+{
+	struct at9100_dci_softc *sc = AT9100_DCI_BUS2SC(bus);
+
+	DPRINTFN(4, "vbus = %u\n", is_on);
+
+	mtx_lock(&(sc->sc_bus.mtx));
+	if (is_on) {
+		if (!sc->sc_flags.status_vbus) {
+			sc->sc_flags.status_vbus = 1;
+
+			/* complete root HUB interrupt endpoint */
+
+			usbd_std_root_transfer(&(sc->sc_root_intr),
+			    &at9100_dci_root_intr_done);
+		}
+	} else {
+		if (sc->sc_flags.status_vbus) {
+			sc->sc_flags.status_vbus = 0;
+			sc->sc_flags.status_bus_reset = 0;
+			sc->sc_flags.status_suspend = 0;
+			sc->sc_flags.change_suspend = 0;
+			sc->sc_flags.change_connect = 1;
+
+			/* complete root HUB interrupt endpoint */
+
+			usbd_std_root_transfer(&(sc->sc_root_intr),
+			    &at9100_dci_root_intr_done);
+		}
+	}
+
+	mtx_unlock(&(sc->sc_bus.mtx));
+
+	return;
+}
 
-	xptr = xlist;
-	td = ctd;
+void
+at9100_dci_interrupt(struct at9100_dci_softc *sc)
+{
+	uint32_t status;
 
 	mtx_lock(&(sc->sc_bus.mtx));
 
-	if (td) {
+	status = AT91_UDP_READ_4(sc, AT91_UDP_ISR);
+
+	/* check for any bus state change interrupts */
+
+	if (status & AT91_UDP_INT_BUS) {
+
+		DPRINTFN(4, "real bus interrupt 0x%08x\n", status);
+
+		/* acknowledge interrupts */
+
+		AT91_UDP_WRITE_4(sc, AT91_UDP_ICR, status & AT91_UDP_INT_BUS);
+
+		if (status & AT91_UDP_INT_END_BR) {
+			sc->sc_flags.status_bus_reset = 1;
+			sc->sc_flags.status_suspend = 0;
+			sc->sc_flags.change_suspend = 0;
+			sc->sc_flags.change_connect = 1;
+		}
+		/*
+	         * If RXRSM and RXSUSP is set at the same time we interpret
+	         * that like RESUME. Resume is set when there is at least 3
+	         * milliseconds of inactivity on the USB BUS.
+	         */
+		if (status & AT91_UDP_INT_RXRSM) {
+			if (sc->sc_flags.status_suspend) {
+				sc->sc_flags.status_suspend = 0;
+				sc->sc_flags.change_suspend = 1;
+			}
+		} else if (status & AT91_UDP_INT_RXSUSP) {
+			if (!sc->sc_flags.status_suspend) {
+				sc->sc_flags.status_suspend = 1;
+				sc->sc_flags.change_suspend = 1;
+			}
+		}
+		/* complete root HUB interrupt endpoint */
+
+		usbd_std_root_transfer(&(sc->sc_root_intr),
+		    &at9100_dci_root_intr_done);
+	}
+	/* check for any endpoint interrupts */
+
+	if (status & AT91_UDP_INT_EPS) {
+
+		DPRINTFN(4, "real endpoint interrupt 0x%08x\n", status);
+
+		at9100_dci_interrupt_poll(sc);
+	}
+	mtx_unlock(&(sc->sc_bus.mtx));
+
+	return;
+}
+
+static void
+at9100_dci_setup_standard_chain_sub(struct at9100_std_temp *temp)
+{
+	struct at9100_dci_td *td;
+
+	/* get current Transfer Descriptor */
+	td = temp->td_next;
+	temp->td = td;
+
+	/* prepare for next TD */
+	temp->td_next = td->obj_next;
+
+	/* fill out the Transfer Descriptor */
+	td->func = temp->func;
+	td->pc = temp->pc;
+	td->offset = temp->offset;
+	td->remainder = temp->len;
+	td->fifo_bank = 0;
+	td->error = 0;
+	td->did_multi_buffer = 0;
+	td->short_pkt = temp->short_pkt;
+	td->alt_next = temp->setup_alt_next;
+	return;
+}
+
+static void
+at9100_dci_setup_standard_chain(struct usbd_xfer *xfer)
+{
+	struct at9100_std_temp temp;
+	struct at9100_dci_softc *sc;
+	struct at9100_dci_td *td;
+	uint32_t x;
+	uint8_t need_sync;
+	uint8_t ep_no;
+
+	DPRINTFN(8, "addr=%d endpt=%d sumlen=%d speed=%d\n",
+	    xfer->address, UE_GET_ADDR(xfer->endpoint),
+	    xfer->sumlen, usbd_get_speed(xfer->udev));
+
+	temp.max_frame_size = xfer->max_frame_size;
+
+	xfer->td_transfer_first = xfer->td_start;
+	xfer->td_transfer_cache = xfer->td_start;
+
+	/* setup temp */
+
+	temp.td = NULL;
+	temp.td_next = xfer->td_start;
+	temp.setup_alt_next = xfer->flags_int.short_frames_ok;
+	temp.offset = 0;
+	temp.pc = NULL;
+
+	need_sync = 0;
+
+	/* check if we should prepend a setup message */
+
+	if (xfer->flags_int.control_xfr) {
+		if (xfer->flags_int.control_hdr) {
+
+			temp.func = &at9100_dci_setup_rx;
+			temp.len = xfer->frlengths[0];
+			temp.pc = xfer->frbuffers + 0;
+			temp.short_pkt = temp.len ? 1 : 0;
+
+			at9100_dci_setup_standard_chain_sub(&temp);
+		}
+		x = 1;
+	} else {
+		x = 0;
+	}
+
+	if (x != xfer->nframes) {
+		if (xfer->endpoint & UE_DIR_IN) {
+			temp.func = &at9100_dci_data_tx;
+			need_sync = 1;
+		} else {
+			temp.func = &at9100_dci_data_rx;
+		}
+	}
+	/* always setup a valid "pc" pointer */
+	temp.pc = xfer->frbuffers + x;
+
+	while (x != xfer->nframes) {
+
+		/* DATA0 / DATA1 message */
+
+		temp.len = xfer->frlengths[x];
+
+		x++;
+
+		if (x == xfer->nframes) {
+			temp.setup_alt_next = 0;
+		}
+		if (temp.len == 0) {
+
+			/* make sure that we send an USB packet */
+
+			temp.short_pkt = 0;
+
+		} else {
+
+			/* regular data transfer */
+
+			temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1;
+		}
+
+		at9100_dci_setup_standard_chain_sub(&temp);
+
+		if (xfer->flags_int.isochronous_xfr) {
+			temp.offset += temp.len;
+		} else {
+			/* get next Page Cache pointer */
+			temp.pc = xfer->frbuffers + x;
+		}
+	}
+
+	/* check if we should append a status stage */
+
+	if (xfer->flags_int.control_xfr &&
+	    !xfer->flags_int.control_act) {
+
 		/*
-		 * The poll thread should not read any status
-		 * registers that will clear interrupts!
+		 * Send a DATA1 message and invert the current
+		 * endpoint direction.
 		 */
-		goto check_fifo;
+		if (xfer->endpoint & UE_DIR_IN) {
+			temp.func = &at9100_dci_data_rx;
+		} else {
+			temp.func = &at9100_dci_data_tx;
+			need_sync = 1;
+		}
+		temp.len = 0;
+		temp.short_pkt = 0;
+
+		at9100_dci_setup_standard_chain_sub(&temp);
+	}
+	if (need_sync && !xfer->flags_int.isochronous_xfr) {
+
+		/* we need a SYNC point */
+		temp.func = &at9100_dci_data_tx_sync;
+		temp.len = 0;
+		temp.short_pkt = 0;
+
+		at9100_dci_setup_standard_chain_sub(&temp);
+
+		td = temp.td;
+	} else {
+		td = temp.td;
+	}
+
+	/* must have at least one frame! */
+	xfer->td_transfer_last = td;
+
+	sc = xfer->usb_sc;
+	ep_no = (xfer->pipe->edesc->bEndpointAddress & UE_ADDR);
+
+	/* setup the correct fifo bank */
+	if (sc->sc_ep_flags[ep_no].fifo_bank) {
+		td = xfer->td_transfer_first;
+		td->fifo_bank = 1;
+	}
+	/* setup the correct multi buffer flag */
+	if (sc->sc_ep_flags[ep_no].did_multi_buffer) {
+		td = xfer->td_transfer_first;
+		td->did_multi_buffer = 1;
+	}
+	return;
+}
+
+static void
+at9100_dci_timeout(struct usbd_xfer *xfer)
+{
+	struct at9100_dci_softc *sc = xfer->usb_sc;
+
+	DPRINTFN(0, "xfer=%p\n", xfer);
+
+	mtx_assert(&sc->sc_bus.mtx, MA_OWNED);
+
+	/* transfer is transferred */
+	at9100_dci_device_done(xfer, USBD_TIMEOUT);
+
+	/* queue callback for execution */
+	usbd_callback_wrapper(xfer, NULL, USBD_CONTEXT_CALLBACK);
+
+	mtx_unlock(&sc->sc_bus.mtx);
+
+	return;
+}
+
+static void
+at9100_dci_start_standard_chain(struct usbd_xfer *xfer)
+{
+	/* poll one time */
+	if (at9100_dci_xfer_do_fifo(xfer)) {
+		/* queue up transfer on interrupt list */
+		usbd_transfer_intr_enqueue(xfer);
+
+		/* setup a timeout, if any */
+		if (xfer->timeout && (!xfer->flags.use_polling)) {
+			usb_callout_reset(&xfer->timeout_handle,
+			    USBD_MS_TO_TICKS(xfer->timeout),
+			    (void *)&at9100_dci_timeout, xfer);
+		}
 	} else {
-		/* NULL is not a valid thread */
-		td = curthread;
+		/* queue callback for execution */
+		usbd_callback_wrapper(xfer, NULL,
+		    USBD_CONTEXT_CALLBACK);
+	}
+	return;
+}
+
+static void
+at9100_dci_root_intr_done(struct usbd_xfer *xfer,
+    struct usbd_std_root_transfer *std)
+{
+	struct at9100_dci_softc *sc = xfer->usb_sc;
+
+	mtx_assert(&sc->sc_bus.mtx, MA_OWNED);
+
+	if (std->state != USBD_STD_ROOT_TR_PRE_DATA) {
+		if (std->state == USBD_STD_ROOT_TR_PRE_CALLBACK) {
+			/* transfer transferred */
+			at9100_dci_device_done(xfer, std->err);
+		}
+		goto done;
+	}
+	/* setup buffer */
+	std->ptr = sc->sc_hub_idata;
+	std->len = sizeof(sc->sc_hub_idata);
+
+	/* set port bit */
+	sc->sc_hub_idata[0] = 0x02;	/* we only have one port */
+
+done:
+	return;
+}
+
+static usbd_status_t
+at9100_dci_standard_done_sub(struct usbd_xfer *xfer)
+{
+	struct at9100_dci_td *td;
+	uint32_t len;
+	uint8_t error;
+
+	td = xfer->td_transfer_cache;
+
+	do {
+		len = td->remainder;
+
+		if (xfer->aframes != xfer->nframes) {
+			/*
+		         * Verify the length and subtract
+		         * the remainder from "frlengths[]":
+		         */
+			if (len > xfer->frlengths[xfer->aframes]) {
+				td->error = 1;
+			} else {
+				xfer->frlengths[xfer->aframes] -= len;
+			}
+		}
+		/* Check for transfer error */
+		if (td->error) {
+			/* the transfer is finished */
+			error = 1;
+			td = NULL;
+			break;
+		}
+		/* Check for short transfer */
+		if (len > 0) {
+			if (xfer->flags_int.short_frames_ok) {
+				/* follow alt next */
+				if (td->alt_next) {
+					td = td->obj_next;
+				} else {
+					td = NULL;
+				}
+			} else {
+				/* the transfer is finished */
+				td = NULL;
+			}
+			error = 0;
+			break;
+		}
+		td = td->obj_next;
+
+		/* this USB frame is complete */
+		error = 0;
+		break;
+
+	} while (0);
+
+	/* update transfer cache */
+
+	xfer->td_transfer_cache = td;
+
+	return (error ?
+	    USBD_STALLED : USBD_NORMAL_COMPLETION);
+}
+
+static void
+at9100_dci_standard_done(struct usbd_xfer *xfer)
+{
+	usbd_status_t err = 0;
+
+	DPRINTFN(12, "xfer=%p pipe=%p transfer done\n",
+	    xfer, xfer->pipe);
+
+	/* reset scanner */
+
+	xfer->td_transfer_cache = xfer->td_transfer_first;
+
+	if (xfer->flags_int.control_xfr) {
+
+		if (xfer->flags_int.control_hdr) {
+
+			err = at9100_dci_standard_done_sub(xfer);
+		}
+		xfer->aframes = 1;
+
+		if (xfer->td_transfer_cache == NULL) {
+			goto done;
+		}
+	}
+	while (xfer->aframes != xfer->nframes) {
+
+		err = at9100_dci_standard_done_sub(xfer);
+		xfer->aframes++;
+
+		if (xfer->td_transfer_cache == NULL) {
+			goto done;
+		}
+	}
+
+	if (xfer->flags_int.control_xfr &&
+	    !xfer->flags_int.control_act) {
+
+		err = at9100_dci_standard_done_sub(xfer);
+	}
+done:
+	at9100_dci_device_done(xfer, err);
+	return;
+}
+
+/*------------------------------------------------------------------------*
+ *	at9100_dci_device_done
+ *
+ * NOTE: this function can be called more than one time on the
+ * same USB transfer!
+ *------------------------------------------------------------------------*/
+static void
+at9100_dci_device_done(struct usbd_xfer *xfer, usbd_status_t error)
+{
+	mtx_assert(&sc->sc_bus.mtx, MA_OWNED);
+
+	DPRINTFN(1, "xfer=%p, pipe=%p, error=%d\n",
+	    xfer, xfer->pipe, error);
+
+	/* dequeue transfer and start next transfer */
+	usbd_transfer_dequeue(xfer, error);
+	return;

>>> TRUNCATED FOR MAIL (1000 lines) <<<


More information about the p4-projects mailing list