V4L/DVB (7602): em28xx: generalise URB setup code

Move the URB setup and management code to em28xx-core.c and generalise
it slighlty so that the DVB code can use it.

Signed-off-by: Aidan Thornton <makosoft@googlemail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index 841c7c2..cb9f721 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -53,6 +53,12 @@
 module_param(alt, int, 0644);
 MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
 
+/* FIXME */
+#define em28xx_isocdbg(fmt, arg...) do {\
+	if (core_debug) \
+		printk(KERN_INFO "%s %s :"fmt, \
+			 dev->name, __func__ , ##arg); } while (0)
+
 /*
  * em28xx_read_reg_req()
  * reads data from the usb device specifying bRequest
@@ -455,3 +461,178 @@
 	}
 	return 0;
 }
+
+/* ------------------------------------------------------------------
+	URB control
+   ------------------------------------------------------------------*/
+
+/*
+ * IRQ callback, called by URB callback
+ */
+static void em28xx_irq_callback(struct urb *urb)
+{
+	struct em28xx_dmaqueue  *dma_q = urb->context;
+	struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);
+	int rc, i;
+
+	/* Copy data from URB */
+	spin_lock(&dev->slock);
+	rc = dev->isoc_ctl.isoc_copy(dev, urb);
+	spin_unlock(&dev->slock);
+
+	/* Reset urb buffers */
+	for (i = 0; i < urb->number_of_packets; i++) {
+		urb->iso_frame_desc[i].status = 0;
+		urb->iso_frame_desc[i].actual_length = 0;
+	}
+	urb->status = 0;
+
+	urb->status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (urb->status) {
+		em28xx_err("urb resubmit failed (error=%i)\n",
+			urb->status);
+	}
+}
+
+/*
+ * Stop and Deallocate URBs
+ */
+void em28xx_uninit_isoc(struct em28xx *dev)
+{
+	struct urb *urb;
+	int i;
+
+	em28xx_isocdbg("em28xx: called em28xx_uninit_isoc\n");
+
+	dev->isoc_ctl.nfields = -1;
+	for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
+		urb = dev->isoc_ctl.urb[i];
+		if (urb) {
+			usb_kill_urb(urb);
+			usb_unlink_urb(urb);
+			if (dev->isoc_ctl.transfer_buffer[i]) {
+				usb_buffer_free(dev->udev,
+						urb->transfer_buffer_length,
+						dev->isoc_ctl.transfer_buffer[i],
+						urb->transfer_dma);
+			}
+			usb_free_urb(urb);
+			dev->isoc_ctl.urb[i] = NULL;
+		}
+		dev->isoc_ctl.transfer_buffer[i] = NULL;
+	}
+
+	kfree(dev->isoc_ctl.urb);
+	kfree(dev->isoc_ctl.transfer_buffer);
+
+	dev->isoc_ctl.urb = NULL;
+	dev->isoc_ctl.transfer_buffer = NULL;
+	dev->isoc_ctl.num_bufs = 0;
+
+	em28xx_capture_start(dev, 0);
+}
+EXPORT_SYMBOL_GPL(em28xx_uninit_isoc);
+
+/*
+ * Allocate URBs and start IRQ
+ */
+int em28xx_init_isoc(struct em28xx *dev, int max_packets,
+		     int num_bufs, int max_pkt_size,
+		     int (*isoc_copy) (struct em28xx *dev, struct urb *urb),
+		     int cap_type)
+{
+	struct em28xx_dmaqueue *dma_q = &dev->vidq;
+	int i;
+	int sb_size, pipe;
+	struct urb *urb;
+	int j, k;
+	int rc;
+
+	em28xx_isocdbg("em28xx: called em28xx_prepare_isoc\n");
+
+	/* De-allocates all pending stuff */
+	em28xx_uninit_isoc(dev);
+
+	dev->isoc_ctl.isoc_copy = isoc_copy;
+	dev->isoc_ctl.num_bufs = num_bufs;
+
+	dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs,  GFP_KERNEL);
+	if (!dev->isoc_ctl.urb) {
+		em28xx_errdev("cannot alloc memory for usb buffers\n");
+		return -ENOMEM;
+	}
+
+	dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
+					      GFP_KERNEL);
+	if (!dev->isoc_ctl.urb) {
+		em28xx_errdev("cannot allocate memory for usbtransfer\n");
+		kfree(dev->isoc_ctl.urb);
+		return -ENOMEM;
+	}
+
+	dev->isoc_ctl.max_pkt_size = max_pkt_size;
+	dev->isoc_ctl.buf = NULL;
+
+	sb_size = max_packets * dev->isoc_ctl.max_pkt_size;
+
+	/* allocate urbs and transfer buffers */
+	for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
+		urb = usb_alloc_urb(max_packets, GFP_KERNEL);
+		if (!urb) {
+			em28xx_err("cannot alloc isoc_ctl.urb %i\n", i);
+			em28xx_uninit_isoc(dev);
+			return -ENOMEM;
+		}
+		dev->isoc_ctl.urb[i] = urb;
+
+		dev->isoc_ctl.transfer_buffer[i] = usb_buffer_alloc(dev->udev,
+			sb_size, GFP_KERNEL, &urb->transfer_dma);
+		if (!dev->isoc_ctl.transfer_buffer[i]) {
+			em28xx_err("unable to allocate %i bytes for transfer"
+					" buffer %i%s\n",
+					sb_size, i,
+					in_interrupt()?" while in int":"");
+			em28xx_uninit_isoc(dev);
+			return -ENOMEM;
+		}
+		memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
+
+		/* FIXME: this is a hack - should be
+			'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK'
+			should also be using 'desc.bInterval'
+		 */
+		pipe = usb_rcvisocpipe(dev->udev, cap_type == EM28XX_ANALOG_CAPTURE ? 0x82 : 0x84);
+		usb_fill_int_urb(urb, dev->udev, pipe,
+				 dev->isoc_ctl.transfer_buffer[i], sb_size,
+				 em28xx_irq_callback, dma_q, 1);
+
+		urb->number_of_packets = max_packets;
+		urb->transfer_flags = URB_ISO_ASAP;
+
+		k = 0;
+		for (j = 0; j < max_packets; j++) {
+			urb->iso_frame_desc[j].offset = k;
+			urb->iso_frame_desc[j].length =
+						dev->isoc_ctl.max_pkt_size;
+			k += dev->isoc_ctl.max_pkt_size;
+		}
+	}
+
+	init_waitqueue_head(&dma_q->wq);
+
+	em28xx_capture_start(dev, cap_type);
+
+	/* submit urbs and enables IRQ */
+	for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
+		rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC);
+		if (rc) {
+			em28xx_err("submit of urb %i failed (error=%i)\n", i,
+				   rc);
+			em28xx_uninit_isoc(dev);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(em28xx_init_isoc);
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 72a7633..1a0b097 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -285,11 +285,10 @@
 /*
  * Controls the isoc copy of each urb packet
  */
-static inline int em28xx_isoc_copy(struct urb *urb)
+static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
 {
 	struct em28xx_buffer    *buf;
 	struct em28xx_dmaqueue  *dma_q = urb->context;
-	struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);
 	unsigned char *outp = NULL;
 	int i, len = 0, rc = 1;
 	unsigned char *p;
@@ -371,188 +370,6 @@
 }
 
 /* ------------------------------------------------------------------
-	URB control
-   ------------------------------------------------------------------*/
-
-/*
- * IRQ callback, called by URB callback
- */
-static void em28xx_irq_callback(struct urb *urb)
-{
-	struct em28xx_dmaqueue  *dma_q = urb->context;
-	struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);
-	int rc, i;
-
-	/* Copy data from URB */
-	spin_lock(&dev->slock);
-	rc = em28xx_isoc_copy(urb);
-	spin_unlock(&dev->slock);
-
-	/* Reset urb buffers */
-	for (i = 0; i < urb->number_of_packets; i++) {
-		urb->iso_frame_desc[i].status = 0;
-		urb->iso_frame_desc[i].actual_length = 0;
-	}
-	urb->status = 0;
-
-	urb->status = usb_submit_urb(urb, GFP_ATOMIC);
-	if (urb->status) {
-		em28xx_err("urb resubmit failed (error=%i)\n",
-			urb->status);
-	}
-}
-
-/*
- * Stop and Deallocate URBs
- */
-static void em28xx_uninit_isoc(struct em28xx *dev)
-{
-	struct urb *urb;
-	int i;
-
-	em28xx_isocdbg("em28xx: called em28xx_uninit_isoc\n");
-
-	dev->isoc_ctl.nfields = -1;
-	for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
-		urb = dev->isoc_ctl.urb[i];
-		if (urb) {
-			usb_kill_urb(urb);
-			usb_unlink_urb(urb);
-			if (dev->isoc_ctl.transfer_buffer[i]) {
-				usb_buffer_free(dev->udev,
-						urb->transfer_buffer_length,
-						dev->isoc_ctl.transfer_buffer[i],
-						urb->transfer_dma);
-			}
-			usb_free_urb(urb);
-			dev->isoc_ctl.urb[i] = NULL;
-		}
-		dev->isoc_ctl.transfer_buffer[i] = NULL;
-	}
-
-	kfree(dev->isoc_ctl.urb);
-	kfree(dev->isoc_ctl.transfer_buffer);
-	dev->isoc_ctl.urb = NULL;
-	dev->isoc_ctl.transfer_buffer = NULL;
-
-	dev->isoc_ctl.num_bufs = 0;
-
-	em28xx_capture_start(dev, 0);
-}
-
-/*
- * Allocate URBs and start IRQ
- */
-static int em28xx_prepare_isoc(struct em28xx *dev, int max_packets,
-			       int num_bufs)
-{
-	struct em28xx_dmaqueue *dma_q = &dev->vidq;
-	int i;
-	int sb_size, pipe;
-	struct urb *urb;
-	int j, k;
-
-	em28xx_isocdbg("em28xx: called em28xx_prepare_isoc\n");
-
-	/* De-allocates all pending stuff */
-	em28xx_uninit_isoc(dev);
-
-	dev->isoc_ctl.num_bufs = num_bufs;
-
-	dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs,  GFP_KERNEL);
-	if (!dev->isoc_ctl.urb) {
-		em28xx_errdev("cannot alloc memory for usb buffers\n");
-		return -ENOMEM;
-	}
-
-	dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
-					      GFP_KERNEL);
-	if (!dev->isoc_ctl.urb) {
-		em28xx_errdev("cannot allocate memory for usbtransfer\n");
-		kfree(dev->isoc_ctl.urb);
-		return -ENOMEM;
-	}
-
-	dev->isoc_ctl.max_pkt_size = dev->max_pkt_size;
-	dev->isoc_ctl.buf = NULL;
-
-	sb_size = max_packets * dev->isoc_ctl.max_pkt_size;
-
-	/* allocate urbs and transfer buffers */
-	for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
-		urb = usb_alloc_urb(max_packets, GFP_KERNEL);
-		if (!urb) {
-			em28xx_err("cannot alloc isoc_ctl.urb %i\n", i);
-			em28xx_uninit_isoc(dev);
-			return -ENOMEM;
-		}
-		dev->isoc_ctl.urb[i] = urb;
-
-		dev->isoc_ctl.transfer_buffer[i] = usb_buffer_alloc(dev->udev,
-			sb_size, GFP_KERNEL, &urb->transfer_dma);
-		if (!dev->isoc_ctl.transfer_buffer[i]) {
-			em28xx_err("unable to allocate %i bytes for transfer"
-					" buffer %i%s\n",
-					sb_size, i,
-					in_interrupt()?" while in int":"");
-			em28xx_uninit_isoc(dev);
-			return -ENOMEM;
-		}
-		memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
-
-		/* FIXME: this is a hack - should be
-			'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK'
-			should also be using 'desc.bInterval'
-		 */
-		pipe = usb_rcvisocpipe(dev->udev, 0x82);
-		usb_fill_int_urb(urb, dev->udev, pipe,
-				 dev->isoc_ctl.transfer_buffer[i], sb_size,
-				 em28xx_irq_callback, dma_q, 1);
-
-		urb->number_of_packets = max_packets;
-		urb->transfer_flags = URB_ISO_ASAP;
-
-		k = 0;
-		for (j = 0; j < max_packets; j++) {
-			urb->iso_frame_desc[j].offset = k;
-			urb->iso_frame_desc[j].length =
-						dev->isoc_ctl.max_pkt_size;
-			k += dev->isoc_ctl.max_pkt_size;
-		}
-	}
-
-	return 0;
-}
-
-static int em28xx_start_thread(struct em28xx_dmaqueue  *dma_q)
-{
-	struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);
-	int i, rc = 0;
-
-	em28xx_videodbg("Called em28xx_start_thread\n");
-
-	init_waitqueue_head(&dma_q->wq);
-
-	em28xx_capture_start(dev, 1);
-
-	/* submit urbs and enables IRQ */
-	for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
-		rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC);
-		if (rc) {
-			em28xx_err("submit of urb %i failed (error=%i)\n", i,
-				   rc);
-			em28xx_uninit_isoc(dev);
-			return rc;
-		}
-	}
-
-	if (rc < 0)
-		return rc;
-
-	return 0;
-}
-
-/* ------------------------------------------------------------------
 	Videobuf operations
    ------------------------------------------------------------------*/
 
@@ -615,7 +432,6 @@
 	struct em28xx_fh     *fh  = vq->priv_data;
 	struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
 	struct em28xx        *dev = fh->dev;
-	struct em28xx_dmaqueue *vidq = &dev->vidq;
 	int                  rc = 0, urb_init = 0;
 
 	/* FIXME: It assumes depth = 16 */
@@ -639,12 +455,9 @@
 		urb_init = 1;
 
 	if (urb_init) {
-		rc = em28xx_prepare_isoc(dev, EM28XX_NUM_PACKETS,
-					 EM28XX_NUM_BUFS);
-		if (rc < 0)
-			goto fail;
-
-		rc = em28xx_start_thread(vidq);
+		rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
+				      EM28XX_NUM_BUFS, dev->max_pkt_size,
+				      em28xx_isoc_copy, EM28XX_ANALOG_CAPTURE);
 		if (rc < 0)
 			goto fail;
 	}
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 3786dd8..151bc57 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -119,6 +119,8 @@
 	STREAM_ON,
 };
 
+struct em28xx;
+
 struct em28xx_usb_isoc_ctl {
 		/* max packet size of isoc transaction */
 	int				max_pkt_size;
@@ -148,6 +150,10 @@
 
 		/* Stores the number of received fields */
 	int				nfields;
+
+		/* isoc urb callback */
+	int (*isoc_copy) (struct em28xx *dev, struct urb *urb);
+
 };
 
 struct em28xx_fmt {
@@ -279,6 +285,12 @@
 	DEV_MISCONFIGURED = 0x04,
 };
 
+enum em28xx_capture_mode {
+	EM28XX_CAPTURE_OFF = 0,
+	EM28XX_ANALOG_CAPTURE,
+	EM28XX_DIGITAL_CAPTURE,
+};
+
 #define EM28XX_AUDIO_BUFS 5
 #define EM28XX_NUM_AUDIO_PACKETS 64
 #define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */
@@ -452,6 +464,11 @@
 int em28xx_outfmt_set_yuv422(struct em28xx *dev);
 int em28xx_resolution_set(struct em28xx *dev);
 int em28xx_set_alternate(struct em28xx *dev);
+int em28xx_init_isoc(struct em28xx *dev, int max_packets,
+		     int num_bufs, int max_pkt_size,
+		     int (*isoc_copy) (struct em28xx *dev, struct urb *urb),
+		     int cap_type);
+void em28xx_uninit_isoc(struct em28xx *dev);
 
 /* Provided by em28xx-video.c */
 int em28xx_register_extension(struct em28xx_ops *dev);