Index: sys/dev/usb/uvideo.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/uvideo.c,v
retrieving revision 1.14
diff -u -u -r1.14 uvideo.c
--- sys/dev/usb/uvideo.c	20 Sep 2008 15:55:38 -0000	1.14
+++ sys/dev/usb/uvideo.c	20 Sep 2008 17:19:16 -0000
@@ -64,6 +64,7 @@
 #include <sys/vnode.h>
 #include <sys/poll.h>
 #include <sys/queue.h>	/* SLIST */
+#include <sys/kthread.h>
 
 #include <sys/videoio.h>
 #include <dev/video_if.h>
@@ -80,6 +81,7 @@
 #endif
 
 #define UVIDEO_NXFERS	3
+#define PRI_UVIDEO	PRI_BIO
 
 /* #define UVIDEO_DISABLE_MJPEG */
 
@@ -243,7 +245,11 @@
 	char			*sc_devname;
 
 	device_ptr_t		sc_videodev;
+	char			sc_running;
 	
+	kcondvar_t		sc_cv;
+	kmutex_t		sc_mtx;
+
 	int			sc_dying;
 	uvideo_state		sc_state;
 
@@ -684,6 +690,64 @@
 	return rv;
 }
 
+static void
+uvideo_transfer_thread(void *opaque)
+{
+	struct uvideo_stream *vs = opaque;
+	struct uvideo_softc *sc = vs->vs_parent;
+	struct uvideo_bulk_xfer *bx = &vs->vs_xfer.bulk;
+	const uvideo_payload_header_t *hdr;
+	int error, frameno;
+	struct video_payload payload;
+	uint32_t len = bx->bx_buflen;
+
+	frameno = -1;
+
+	printf("uvideo_stream_recv_bulk_start");
+	while (sc->sc_running) {
+		/* 
+		 * The payload can only be smaller than
+		 * bx_buflen on the last payload. If this is the
+		 * last one then we are done.
+		 */
+		if (len < bx->bx_buflen)
+			continue;
+
+		error = usbd_bulk_transfer(bx->bx_xfer, bx->bx_pipe,
+			    USBD_SHORT_XFER_OK|USBD_NO_COPY, 50000,
+			    bx->bx_buffer, &len, "uvideorb");
+		if (len >= sizeof(uvideo_payload_header_t) &&
+		    error == USBD_NORMAL_COMPLETION) {
+
+			hdr = (const uvideo_payload_header_t *)&bx->bx_buffer;
+			if (hdr->bHeaderLength == 0)
+				continue;
+
+			payload.data = bx->bx_buffer + hdr->bHeaderLength;
+			payload.size = bx->bx_buflen - hdr->bHeaderLength;
+			payload.frameno = hdr->bmHeaderInfo & UV_FRAME_ID;
+			payload.end_of_frame =
+			    hdr->bmHeaderInfo & UV_END_OF_FRAME;
+			
+			if (frameno == -1)
+				frameno = payload.frameno;
+
+			video_submit_payload(sc->sc_videodev, &payload);
+			
+			if (frameno != payload.frameno ||
+			    payload.end_of_frame)
+				break;
+		} else
+			break;
+	}
+
+	mutex_enter(&sc->sc_mtx);
+	cv_broadcast(&sc->sc_cv);
+	mutex_exit(&sc->sc_mtx);
+
+	kthread_exit(0);
+}
+
 /* Search the stream list for a stream matching the interface number.
  * This is an O(n) search, but most devices should have only one or at
  * most two streams. */
@@ -1110,7 +1174,13 @@
 					bx->bx_endpt =
 					    GET(usb_endpoint_descriptor_t,
 						desc, bEndpointAddress);
+					bx->bx_buflen = 
+					    UGETW(GET(usb_endpoint_descriptor_t,
+						desc, wMaxPacketSize));
+					printf("wMaxPacketSize = %d\n", bx->bx_buflen);
+
 				}
+				
 			} else if (xfer_type == UE_ISOCHRONOUS) {
 				ix = &vs->vs_xfer.isoc;
 				for (i = 0; i < UVIDEO_NXFERS; i++) {
@@ -1428,14 +1498,49 @@
 	uint32_t vframe_len;	/* rough bytes per video frame */
 	uint32_t uframe_len;	/* bytes per usb frame (TODO: or microframe?) */
 	uint32_t nframes;	/* number of usb frames (TODO: or microframs?) */
-	int i;
+	int i, ret;
 
 	struct uvideo_alternate *alt, *alt_maybe;
 	usbd_status err;
 
 	switch (vs->vs_xfer_type) {
 	case UE_BULK:
+		ret = 0;
 		bx = &vs->vs_xfer.bulk;
+		err = usbd_open_pipe(vs->vs_iface, bx->bx_endpt,
+				     USBD_EXCLUSIVE_USE, &bx->bx_pipe);
+		if (err != USBD_NORMAL_COMPLETION) {
+			DPRINTF(("uvideo: error opening pipe: %s (%d)\n",
+				 usbd_errstr(err), err));
+			return EIO;
+		}
+
+		bx->bx_xfer = usbd_alloc_xfer(sc->sc_udev);
+		if (bx->bx_xfer == NULL) {
+			DPRINTF(("uvideo: failed to alloc xfer: %s"
+			 " (%d)\n",
+			 usbd_errstr(err), err));
+			return ENOMEM;
+		}
+
+		bx->bx_buffer = usbd_alloc_buffer(bx->bx_xfer,
+				    bx->bx_buflen);
+		if (bx->bx_xfer == NULL) {
+			DPRINTF(("uvideo: failed to alloc buf: %s"
+			 " (%d)\n",
+			 usbd_errstr(err), err));
+			return ENOMEM;
+		}
+
+		mutex_enter(&sc->sc_mtx);
+		if (sc->sc_running == 0) {
+			sc->sc_running = 1;
+			ret = kthread_create(PRI_UVIDEO, 0, NULL, uvideo_transfer_thread,
+		    		vs, NULL, device_xname(sc->sc_dev));
+		} else
+			aprint_error_dev(sc->sc_dev, "transfer already in progress\n");
+		mutex_exit(&sc->sc_mtx);
+
 		return 0;
 	case UE_ISOCHRONOUS:
 		ix = &vs->vs_xfer.isoc;
@@ -1548,6 +1653,7 @@
 static int
 uvideo_stream_stop_xfer(struct uvideo_stream *vs)
 {
+	struct uvideo_softc *sc = vs->vs_parent;
 	struct uvideo_bulk_xfer *bx;
 	struct uvideo_isoc_xfer *ix;
 	usbd_status err;
@@ -1556,6 +1662,15 @@
 	switch (vs->vs_xfer_type) {
 	case UE_BULK:
 		bx = &vs->vs_xfer.bulk;
+
+		mutex_enter(&sc->sc_mtx);
+		if (sc->sc_running) {
+			sc->sc_running = 0;
+			cv_wait_sig(&sc->sc_cv, &sc->sc_mtx);
+		}
+		mutex_exit(&sc->sc_mtx);
+
+		/* TODO: Free resources */
 		return 0;
 	case UE_ISOCHRONOUS:
 		ix = &vs->vs_xfer.isoc;