alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] usbtv: split core and video implementation
@ 2014-01-05 15:38 Federico Simoncelli
  2014-01-05 15:38 ` [PATCH 2/2] usbtv: add audio support Federico Simoncelli
  2014-01-05 16:37 ` [PATCH 1/2] usbtv: split core and video implementation Lubomir Rintel
  0 siblings, 2 replies; 5+ messages in thread
From: Federico Simoncelli @ 2014-01-05 15:38 UTC (permalink / raw)
  To: alsa-devel; +Cc: lkundrak, Federico Simoncelli

Signed-off-by: Federico Simoncelli <fsimonce@redhat.com>
Reviewed-by: Lubomir Rintel <lkundrak@v3.sk>
---
 Makefile                 |   3 +
 usbtv-core.c             | 136 +++++++++++++++++++++++++++++++++++++++
 usbtv.c => usbtv-video.c | 163 +++--------------------------------------------
 usbtv.h                  |  98 ++++++++++++++++++++++++++++
 4 files changed, 246 insertions(+), 154 deletions(-)
 create mode 100644 usbtv-core.c
 rename usbtv.c => usbtv-video.c (82%)
 create mode 100644 usbtv.h

diff --git a/Makefile b/Makefile
index 28b872f..775316a 100644
--- a/Makefile
+++ b/Makefile
@@ -1 +1,4 @@
+usbtv-y := usbtv-core.o \
+	usbtv-video.o
+
 obj-$(CONFIG_VIDEO_USBTV) += usbtv.o
diff --git a/usbtv-core.c b/usbtv-core.c
new file mode 100644
index 0000000..e89e48b
--- /dev/null
+++ b/usbtv-core.c
@@ -0,0 +1,136 @@
+/*
+ * Fushicai USBTV007 Video Grabber Driver
+ *
+ * Product web site:
+ * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
+ *
+ * Following LWN articles were very useful in construction of this driver:
+ * Video4Linux2 API series: http://lwn.net/Articles/203924/
+ * videobuf2 API explanation: http://lwn.net/Articles/447435/
+ * Thanks go to Jonathan Corbet for providing this quality documentation.
+ * He is awesome.
+ *
+ * Copyright (c) 2013 Lubomir Rintel
+ * All rights reserved.
+ * No physical hardware was harmed running Windows during the
+ * reverse-engineering activity
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ */
+
+#include <linux/module.h>
+
+#include "usbtv.h"
+
+int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size)
+{
+	int ret;
+	int pipe = usb_rcvctrlpipe(usbtv->udev, 0);
+	int i;
+
+	for (i = 0; i < size; i++) {
+		u16 index = regs[i][0];
+		u16 value = regs[i][1];
+
+		ret = usb_control_msg(usbtv->udev, pipe, USBTV_REQUEST_REG,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			value, index, NULL, 0, 0);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int usbtv_probe(struct usb_interface *intf,
+	const struct usb_device_id *id)
+{
+	int ret;
+	int size;
+	struct device *dev = &intf->dev;
+	struct usbtv *usbtv;
+
+	/* Checks that the device is what we think it is. */
+	if (intf->num_altsetting != 2)
+		return -ENODEV;
+	if (intf->altsetting[1].desc.bNumEndpoints != 4)
+		return -ENODEV;
+
+	/* Packet size is split into 11 bits of base size and count of
+	 * extra multiplies of it.*/
+	size = usb_endpoint_maxp(&intf->altsetting[1].endpoint[0].desc);
+	size = (size & 0x07ff) * (((size & 0x1800) >> 11) + 1);
+
+	/* Device structure */
+	usbtv = kzalloc(sizeof(struct usbtv), GFP_KERNEL);
+	if (usbtv == NULL)
+		return -ENOMEM;
+	usbtv->dev = dev;
+	usbtv->udev = usb_get_dev(interface_to_usbdev(intf));
+
+	usbtv->iso_size = size;
+
+	usb_set_intfdata(intf, usbtv);
+
+	ret = usbtv_video_init(usbtv);
+	if (ret < 0)
+		goto usbtv_video_fail;
+
+	/* for simplicity we exploit the v4l2_device reference counting */
+	v4l2_device_get(&usbtv->v4l2_dev);
+
+	dev_info(dev, "Fushicai USBTV007 Video Grabber\n");
+	return 0;
+
+usbtv_video_fail:
+	kfree(usbtv);
+
+	return ret;
+}
+
+static void usbtv_disconnect(struct usb_interface *intf)
+{
+	struct usbtv *usbtv = usb_get_intfdata(intf);
+	usb_set_intfdata(intf, NULL);
+
+	if (!usbtv)
+		return;
+
+	usbtv_video_free(usbtv);
+
+	usb_put_dev(usbtv->udev);
+	usbtv->udev = NULL;
+
+	/* the usbtv structure will be deallocated when v4l2 will be
+	   done using it */
+	v4l2_device_put(&usbtv->v4l2_dev);
+}
+
+struct usb_device_id usbtv_id_table[] = {
+	{ USB_DEVICE(0x1b71, 0x3002) },
+	{}
+};
+MODULE_DEVICE_TABLE(usb, usbtv_id_table);
+
+MODULE_AUTHOR("Lubomir Rintel");
+MODULE_DESCRIPTION("Fushicai USBTV007 Video Grabber Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+struct usb_driver usbtv_usb_driver = {
+	.name = "usbtv",
+	.id_table = usbtv_id_table,
+	.probe = usbtv_probe,
+	.disconnect = usbtv_disconnect,
+};
+
+module_usb_driver(usbtv_usb_driver);
diff --git a/usbtv.c b/usbtv-video.c
similarity index 82%
rename from usbtv.c
rename to usbtv-video.c
index 6222a4a..496bc2e 100644
--- a/usbtv.c
+++ b/usbtv-video.c
@@ -28,45 +28,10 @@
  * GNU General Public License ("GPL").
  */
 
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-#include <linux/videodev2.h>
-
-#include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
 #include <media/videobuf2-core.h>
-#include <media/videobuf2-vmalloc.h>
-
-/* Hardware. */
-#define USBTV_VIDEO_ENDP	0x81
-#define USBTV_BASE		0xc000
-#define USBTV_REQUEST_REG	12
-
-/* Number of concurrent isochronous urbs submitted.
- * Higher numbers was seen to overly saturate the USB bus. */
-#define USBTV_ISOC_TRANSFERS	16
-#define USBTV_ISOC_PACKETS	8
-
-#define USBTV_CHUNK_SIZE	256
-#define USBTV_CHUNK		240
-
-/* Chunk header. */
-#define USBTV_MAGIC_OK(chunk)	((be32_to_cpu(chunk[0]) & 0xff000000) \
-							== 0x88000000)
-#define USBTV_FRAME_ID(chunk)	((be32_to_cpu(chunk[0]) & 0x00ff0000) >> 16)
-#define USBTV_ODD(chunk)	((be32_to_cpu(chunk[0]) & 0x0000f000) >> 15)
-#define USBTV_CHUNK_NO(chunk)	(be32_to_cpu(chunk[0]) & 0x00000fff)
-
-#define USBTV_TV_STD  (V4L2_STD_525_60 | V4L2_STD_PAL)
-
-/* parameters for supported TV norms */
-struct usbtv_norm_params {
-	v4l2_std_id norm;
-	int cap_width, cap_height;
-};
+
+#include "usbtv.h"
 
 static struct usbtv_norm_params norm_params[] = {
 	{
@@ -81,43 +46,6 @@ static struct usbtv_norm_params norm_params[] = {
 	}
 };
 
-/* A single videobuf2 frame buffer. */
-struct usbtv_buf {
-	struct vb2_buffer vb;
-	struct list_head list;
-};
-
-/* Per-device structure. */
-struct usbtv {
-	struct device *dev;
-	struct usb_device *udev;
-	struct v4l2_device v4l2_dev;
-	struct video_device vdev;
-	struct vb2_queue vb2q;
-	struct mutex v4l2_lock;
-	struct mutex vb2q_lock;
-
-	/* List of videobuf2 buffers protected by a lock. */
-	spinlock_t buflock;
-	struct list_head bufs;
-
-	/* Number of currently processed frame, useful find
-	 * out when a new one begins. */
-	u32 frame_id;
-	int chunks_done;
-
-	enum {
-		USBTV_COMPOSITE_INPUT,
-		USBTV_SVIDEO_INPUT,
-	} input;
-	v4l2_std_id norm;
-	int width, height;
-	int n_chunks;
-	int iso_size;
-	unsigned int sequence;
-	struct urb *isoc_urbs[USBTV_ISOC_TRANSFERS];
-};
-
 static int usbtv_configure_for_norm(struct usbtv *usbtv, v4l2_std_id norm)
 {
 	int i, ret = 0;
@@ -142,26 +70,6 @@ static int usbtv_configure_for_norm(struct usbtv *usbtv, v4l2_std_id norm)
 	return ret;
 }
 
-static int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size)
-{
-	int ret;
-	int pipe = usb_rcvctrlpipe(usbtv->udev, 0);
-	int i;
-
-	for (i = 0; i < size; i++) {
-		u16 index = regs[i][0];
-		u16 value = regs[i][1];
-
-		ret = usb_control_msg(usbtv->udev, pipe, USBTV_REQUEST_REG,
-			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-			value, index, NULL, 0, 0);
-		if (ret < 0)
-			return ret;
-	}
-
-	return 0;
-}
-
 static int usbtv_select_input(struct usbtv *usbtv, int input)
 {
 	int ret;
@@ -560,12 +468,6 @@ start_fail:
 	return ret;
 }
 
-struct usb_device_id usbtv_id_table[] = {
-	{ USB_DEVICE(0x1b71, 0x3002) },
-	{}
-};
-MODULE_DEVICE_TABLE(usb, usbtv_id_table);
-
 static int usbtv_querycap(struct file *file, void *priv,
 				struct v4l2_capability *cap)
 {
@@ -759,33 +661,9 @@ static void usbtv_release(struct v4l2_device *v4l2_dev)
 	kfree(usbtv);
 }
 
-static int usbtv_probe(struct usb_interface *intf,
-	const struct usb_device_id *id)
+int usbtv_video_init(struct usbtv *usbtv)
 {
 	int ret;
-	int size;
-	struct device *dev = &intf->dev;
-	struct usbtv *usbtv;
-
-	/* Checks that the device is what we think it is. */
-	if (intf->num_altsetting != 2)
-		return -ENODEV;
-	if (intf->altsetting[1].desc.bNumEndpoints != 4)
-		return -ENODEV;
-
-	/* Packet size is split into 11 bits of base size and count of
-	 * extra multiplies of it.*/
-	size = usb_endpoint_maxp(&intf->altsetting[1].endpoint[0].desc);
-	size = (size & 0x07ff) * (((size & 0x1800) >> 11) + 1);
-
-	/* Device structure */
-	usbtv = kzalloc(sizeof(struct usbtv), GFP_KERNEL);
-	if (usbtv == NULL)
-		return -ENOMEM;
-	usbtv->dev = dev;
-	usbtv->udev = usb_get_dev(interface_to_usbdev(intf));
-
-	usbtv->iso_size = size;
 
 	(void)usbtv_configure_for_norm(usbtv, V4L2_STD_525_60);
 
@@ -805,20 +683,18 @@ static int usbtv_probe(struct usb_interface *intf,
 	usbtv->vb2q.lock = &usbtv->vb2q_lock;
 	ret = vb2_queue_init(&usbtv->vb2q);
 	if (ret < 0) {
-		dev_warn(dev, "Could not initialize videobuf2 queue\n");
-		goto usbtv_fail;
+		dev_warn(usbtv->dev, "Could not initialize videobuf2 queue\n");
+		return ret;
 	}
 
 	/* v4l2 structure */
 	usbtv->v4l2_dev.release = usbtv_release;
-	ret = v4l2_device_register(dev, &usbtv->v4l2_dev);
+	ret = v4l2_device_register(usbtv->dev, &usbtv->v4l2_dev);
 	if (ret < 0) {
-		dev_warn(dev, "Could not register v4l2 device\n");
+		dev_warn(usbtv->dev, "Could not register v4l2 device\n");
 		goto v4l2_fail;
 	}
 
-	usb_set_intfdata(intf, usbtv);
-
 	/* Video structure */
 	strlcpy(usbtv->vdev.name, "usbtv", sizeof(usbtv->vdev.name));
 	usbtv->vdev.v4l2_dev = &usbtv->v4l2_dev;
@@ -832,52 +708,31 @@ static int usbtv_probe(struct usb_interface *intf,
 	video_set_drvdata(&usbtv->vdev, usbtv);
 	ret = video_register_device(&usbtv->vdev, VFL_TYPE_GRABBER, -1);
 	if (ret < 0) {
-		dev_warn(dev, "Could not register video device\n");
+		dev_warn(usbtv->dev, "Could not register video device\n");
 		goto vdev_fail;
 	}
 
-	dev_info(dev, "Fushicai USBTV007 Video Grabber\n");
 	return 0;
 
 vdev_fail:
 	v4l2_device_unregister(&usbtv->v4l2_dev);
 v4l2_fail:
 	vb2_queue_release(&usbtv->vb2q);
-usbtv_fail:
-	kfree(usbtv);
 
 	return ret;
 }
 
-static void usbtv_disconnect(struct usb_interface *intf)
+void usbtv_video_free(struct usbtv *usbtv)
 {
-	struct usbtv *usbtv = usb_get_intfdata(intf);
-
 	mutex_lock(&usbtv->vb2q_lock);
 	mutex_lock(&usbtv->v4l2_lock);
 
 	usbtv_stop(usbtv);
-	usb_set_intfdata(intf, NULL);
 	video_unregister_device(&usbtv->vdev);
 	v4l2_device_disconnect(&usbtv->v4l2_dev);
-	usb_put_dev(usbtv->udev);
-	usbtv->udev = NULL;
 
 	mutex_unlock(&usbtv->v4l2_lock);
 	mutex_unlock(&usbtv->vb2q_lock);
 
 	v4l2_device_put(&usbtv->v4l2_dev);
 }
-
-MODULE_AUTHOR("Lubomir Rintel");
-MODULE_DESCRIPTION("Fushicai USBTV007 Video Grabber Driver");
-MODULE_LICENSE("Dual BSD/GPL");
-
-struct usb_driver usbtv_usb_driver = {
-	.name = "usbtv",
-	.id_table = usbtv_id_table,
-	.probe = usbtv_probe,
-	.disconnect = usbtv_disconnect,
-};
-
-module_usb_driver(usbtv_usb_driver);
diff --git a/usbtv.h b/usbtv.h
new file mode 100644
index 0000000..536343d
--- /dev/null
+++ b/usbtv.h
@@ -0,0 +1,98 @@
+/*
+ * Fushicai USBTV007 Video Grabber Driver
+ *
+ * Copyright (c) 2013 Lubomir Rintel
+ * All rights reserved.
+ * No physical hardware was harmed running Windows during the
+ * reverse-engineering activity
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ */
+
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include <media/v4l2-device.h>
+#include <media/videobuf2-vmalloc.h>
+
+/* Hardware. */
+#define USBTV_VIDEO_ENDP	0x81
+#define USBTV_BASE		0xc000
+#define USBTV_REQUEST_REG	12
+
+/* Number of concurrent isochronous urbs submitted.
+ * Higher numbers was seen to overly saturate the USB bus. */
+#define USBTV_ISOC_TRANSFERS	16
+#define USBTV_ISOC_PACKETS	8
+
+#define USBTV_CHUNK_SIZE	256
+#define USBTV_CHUNK		240
+
+/* Chunk header. */
+#define USBTV_MAGIC_OK(chunk)	((be32_to_cpu(chunk[0]) & 0xff000000) \
+							== 0x88000000)
+#define USBTV_FRAME_ID(chunk)	((be32_to_cpu(chunk[0]) & 0x00ff0000) >> 16)
+#define USBTV_ODD(chunk)	((be32_to_cpu(chunk[0]) & 0x0000f000) >> 15)
+#define USBTV_CHUNK_NO(chunk)	(be32_to_cpu(chunk[0]) & 0x00000fff)
+
+#define USBTV_TV_STD  (V4L2_STD_525_60 | V4L2_STD_PAL)
+
+/* parameters for supported TV norms */
+struct usbtv_norm_params {
+	v4l2_std_id norm;
+	int cap_width, cap_height;
+};
+
+/* A single videobuf2 frame buffer. */
+struct usbtv_buf {
+	struct vb2_buffer vb;
+	struct list_head list;
+};
+
+/* Per-device structure. */
+struct usbtv {
+	struct device *dev;
+	struct usb_device *udev;
+
+	/* video */
+	struct v4l2_device v4l2_dev;
+	struct video_device vdev;
+	struct vb2_queue vb2q;
+	struct mutex v4l2_lock;
+	struct mutex vb2q_lock;
+
+	/* List of videobuf2 buffers protected by a lock. */
+	spinlock_t buflock;
+	struct list_head bufs;
+
+	/* Number of currently processed frame, useful find
+	 * out when a new one begins. */
+	u32 frame_id;
+	int chunks_done;
+
+	enum {
+		USBTV_COMPOSITE_INPUT,
+		USBTV_SVIDEO_INPUT,
+	} input;
+	v4l2_std_id norm;
+	int width, height;
+	int n_chunks;
+	int iso_size;
+	unsigned int sequence;
+	struct urb *isoc_urbs[USBTV_ISOC_TRANSFERS];
+};
+
+int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size);
+
+int usbtv_video_init(struct usbtv *usbtv);
+void usbtv_video_free(struct usbtv *usbtv);
-- 
1.8.4.2

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] usbtv: add audio support
  2014-01-05 15:38 [PATCH 1/2] usbtv: split core and video implementation Federico Simoncelli
@ 2014-01-05 15:38 ` Federico Simoncelli
  2014-01-05 16:43   ` Lubomir Rintel
  2014-01-05 16:37 ` [PATCH 1/2] usbtv: split core and video implementation Lubomir Rintel
  1 sibling, 1 reply; 5+ messages in thread
From: Federico Simoncelli @ 2014-01-05 15:38 UTC (permalink / raw)
  To: alsa-devel; +Cc: lkundrak, Federico Simoncelli

Signed-off-by: Federico Simoncelli <fsimonce@redhat.com>
Tested-by: Lubomir Rintel <lkundrak@v3.sk>
---
 Makefile      |   3 +-
 usbtv-audio.c | 381 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 usbtv-core.c  |  16 ++-
 usbtv-video.c |   9 +-
 usbtv.h       |  21 +++-
 5 files changed, 420 insertions(+), 10 deletions(-)
 create mode 100644 usbtv-audio.c

diff --git a/Makefile b/Makefile
index 775316a..f555cf8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,5 @@
 usbtv-y := usbtv-core.o \
-	usbtv-video.o
+	usbtv-video.o \
+	usbtv-audio.o
 
 obj-$(CONFIG_VIDEO_USBTV) += usbtv.o
diff --git a/usbtv-audio.c b/usbtv-audio.c
new file mode 100644
index 0000000..edc4cd7
--- /dev/null
+++ b/usbtv-audio.c
@@ -0,0 +1,381 @@
+/*
+ * Fushicai USBTV007 Audio-Video Grabber Driver
+ *
+ * Product web site:
+ * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
+ *
+ * Copyright (c) 2013 Federico Simoncelli
+ * All rights reserved.
+ * No physical hardware was harmed running Windows during the
+ * reverse-engineering activity
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ */
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/ac97_codec.h>
+#include <sound/pcm_params.h>
+
+#include "usbtv.h"
+
+static struct snd_pcm_hardware snd_usbtv_digital_hw = {
+	.info = SNDRV_PCM_INFO_BATCH |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
+	.rate_min = 48000,
+	.rate_max = 48000,
+	.channels_min = 2,
+	.channels_max = 2,
+	.period_bytes_min = 64,
+	.period_bytes_max = 12544,
+	.periods_min = 2,
+	.periods_max = 98,
+	.buffer_bytes_max = 62720 * 8, /* value in usbaudio.c */
+};
+
+static int snd_usbtv_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct usbtv *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	chip->snd_substream = substream;
+	runtime->hw = snd_usbtv_digital_hw;
+
+	return 0;
+}
+
+static int snd_usbtv_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct usbtv *chip = snd_pcm_substream_chip(substream);
+
+	if (atomic_read(&chip->snd_stream)) {
+		atomic_set(&chip->snd_stream, 0);
+		schedule_work(&chip->snd_trigger);
+	}
+
+	return 0;
+}
+
+static int snd_usbtv_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *hw_params)
+{
+	int rv;
+	struct usbtv *chip = snd_pcm_substream_chip(substream);
+
+	rv = snd_pcm_lib_malloc_pages(substream,
+		params_buffer_bytes(hw_params));
+
+	if (rv < 0) {
+		dev_warn(chip->dev, "pcm audio buffer allocation failure %i\n",
+			rv);
+		return rv;
+	}
+
+	return 0;
+}
+
+static int snd_usbtv_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int snd_usbtv_prepare(struct snd_pcm_substream *substream)
+{
+	struct usbtv *chip = snd_pcm_substream_chip(substream);
+
+	chip->snd_buffer_pos = 0;
+	chip->snd_period_pos = 0;
+
+	return 0;
+}
+
+static void usbtv_audio_urb_received(struct urb *urb)
+{
+	struct usbtv *chip = urb->context;
+	struct snd_pcm_substream *substream = chip->snd_substream;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	size_t i, frame_bytes, chunk_length, buffer_pos, period_pos;
+	int period_elapsed;
+	void *urb_current;
+
+	switch (urb->status) {
+	case 0:
+	case -ETIMEDOUT:
+		break;
+	case -ENOENT:
+	case -EPROTO:
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		return;
+	default:
+		dev_warn(chip->dev, "unknown audio urb status %i\n",
+			urb->status);
+	}
+
+	if (!atomic_read(&chip->snd_stream))
+		return;
+
+	frame_bytes = runtime->frame_bits >> 3;
+	chunk_length = USBTV_CHUNK / frame_bytes;
+
+	buffer_pos = chip->snd_buffer_pos;
+	period_pos = chip->snd_period_pos;
+	period_elapsed = 0;
+
+	for (i = 0; i < urb->actual_length; i += USBTV_CHUNK_SIZE) {
+		urb_current = urb->transfer_buffer + i + USBTV_AUDIO_HDRSIZE;
+
+		if (buffer_pos + chunk_length >= runtime->buffer_size) {
+			size_t cnt = (runtime->buffer_size - buffer_pos) *
+				frame_bytes;
+			memcpy(runtime->dma_area + buffer_pos * frame_bytes,
+				urb_current, cnt);
+			memcpy(runtime->dma_area, urb_current + cnt,
+				chunk_length * frame_bytes - cnt);
+		} else {
+			memcpy(runtime->dma_area + buffer_pos * frame_bytes,
+				urb_current, chunk_length * frame_bytes);
+		}
+
+		buffer_pos += chunk_length;
+		period_pos += chunk_length;
+
+		if (buffer_pos >= runtime->buffer_size)
+			buffer_pos -= runtime->buffer_size;
+
+		if (period_pos >= runtime->period_size) {
+			period_pos -= runtime->period_size;
+			period_elapsed = 1;
+		}
+	}
+
+	snd_pcm_stream_lock(substream);
+
+	chip->snd_buffer_pos = buffer_pos;
+	chip->snd_period_pos = period_pos;
+
+	snd_pcm_stream_unlock(substream);
+
+	if (period_elapsed)
+		snd_pcm_period_elapsed(substream);
+
+	usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static int usbtv_audio_start(struct usbtv *chip)
+{
+	unsigned int pipe;
+	static const u16 setup[][2] = {
+		/* These seem to enable the device. */
+		{ USBTV_BASE + 0x0008, 0x0001 },
+		{ USBTV_BASE + 0x01d0, 0x00ff },
+		{ USBTV_BASE + 0x01d9, 0x0002 },
+
+		{ USBTV_BASE + 0x01da, 0x0013 },
+		{ USBTV_BASE + 0x01db, 0x0012 },
+		{ USBTV_BASE + 0x01e9, 0x0002 },
+		{ USBTV_BASE + 0x01ec, 0x006c },
+		{ USBTV_BASE + 0x0294, 0x0020 },
+		{ USBTV_BASE + 0x0255, 0x00cf },
+		{ USBTV_BASE + 0x0256, 0x0020 },
+		{ USBTV_BASE + 0x01eb, 0x0030 },
+		{ USBTV_BASE + 0x027d, 0x00a6 },
+		{ USBTV_BASE + 0x0280, 0x0011 },
+		{ USBTV_BASE + 0x0281, 0x0040 },
+		{ USBTV_BASE + 0x0282, 0x0011 },
+		{ USBTV_BASE + 0x0283, 0x0040 },
+		{ 0xf891, 0x0010 },
+
+		/* this sets the input from composite */
+		{ USBTV_BASE + 0x0284, 0x00aa },
+	};
+
+	chip->snd_bulk_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (chip->snd_bulk_urb == NULL)
+		goto err_alloc_urb;
+
+	pipe = usb_rcvbulkpipe(chip->udev, USBTV_AUDIO_ENDP);
+
+	chip->snd_bulk_urb->transfer_buffer = kzalloc(
+		USBTV_AUDIO_URBSIZE, GFP_KERNEL);
+	if (chip->snd_bulk_urb->transfer_buffer == NULL)
+		goto err_transfer_buffer;
+
+	usb_fill_bulk_urb(chip->snd_bulk_urb, chip->udev, pipe,
+		chip->snd_bulk_urb->transfer_buffer, USBTV_AUDIO_URBSIZE,
+		usbtv_audio_urb_received, chip);
+
+	/* starting the stream */
+	usbtv_set_regs(chip, setup, ARRAY_SIZE(setup));
+
+	usb_clear_halt(chip->udev, pipe);
+	usb_submit_urb(chip->snd_bulk_urb, GFP_ATOMIC);
+
+	return 0;
+
+err_transfer_buffer:
+	usb_free_urb(chip->snd_bulk_urb);
+	chip->snd_bulk_urb = NULL;
+
+err_alloc_urb:
+	return -ENOMEM;
+}
+
+static int usbtv_audio_stop(struct usbtv *chip)
+{
+	static const u16 setup[][2] = {
+/*		{ USBTV_BASE + 0x00a2, 0x0013 }, */
+		{ USBTV_BASE + 0x027d, 0x0000 },
+		{ USBTV_BASE + 0x0280, 0x0010 },
+		{ USBTV_BASE + 0x0282, 0x0010 },
+	};
+
+	if (chip->snd_bulk_urb) {
+		usb_kill_urb(chip->snd_bulk_urb);
+		kfree(chip->snd_bulk_urb->transfer_buffer);
+		usb_free_urb(chip->snd_bulk_urb);
+		chip->snd_bulk_urb = NULL;
+	}
+
+	usbtv_set_regs(chip, setup, ARRAY_SIZE(setup));
+
+	return 0;
+}
+
+void usbtv_audio_suspend(struct usbtv *usbtv)
+{
+	if (atomic_read(&usbtv->snd_stream) && usbtv->snd_bulk_urb)
+		usb_kill_urb(usbtv->snd_bulk_urb);
+}
+
+void usbtv_audio_resume(struct usbtv *usbtv)
+{
+	if (atomic_read(&usbtv->snd_stream) && usbtv->snd_bulk_urb)
+		usb_submit_urb(usbtv->snd_bulk_urb, GFP_ATOMIC);
+}
+
+static void snd_usbtv_trigger(struct work_struct *work)
+{
+	struct usbtv *chip = container_of(work, struct usbtv, snd_trigger);
+
+	if (atomic_read(&chip->snd_stream))
+		usbtv_audio_start(chip);
+	else
+		usbtv_audio_stop(chip);
+}
+
+static int snd_usbtv_card_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct usbtv *chip = snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		atomic_set(&chip->snd_stream, 1);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		atomic_set(&chip->snd_stream, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	schedule_work(&chip->snd_trigger);
+
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_usbtv_pointer(struct snd_pcm_substream *substream)
+{
+	struct usbtv *chip = snd_pcm_substream_chip(substream);
+	return chip->snd_buffer_pos;
+}
+
+static struct snd_pcm_ops snd_usbtv_pcm_ops = {
+	.open = snd_usbtv_pcm_open,
+	.close = snd_usbtv_pcm_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = snd_usbtv_hw_params,
+	.hw_free = snd_usbtv_hw_free,
+	.prepare = snd_usbtv_prepare,
+	.trigger = snd_usbtv_card_trigger,
+	.pointer = snd_usbtv_pointer,
+};
+
+int usbtv_audio_init(struct usbtv *usbtv)
+{
+	int rv;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+
+	INIT_WORK(&usbtv->snd_trigger, snd_usbtv_trigger);
+	atomic_set(&usbtv->snd_stream, 0);
+
+	rv = snd_card_create(SNDRV_DEFAULT_IDX1, "usbtv", THIS_MODULE, 0,
+		&card);
+	if (rv < 0)
+		return rv;
+
+	strncpy(card->driver, usbtv->dev->driver->name,
+		sizeof(card->driver) - 1);
+	strncpy(card->shortname, "usbtv", sizeof(card->shortname) - 1);
+	snprintf(card->longname, sizeof(card->longname),
+		"USBTV Audio at bus %d device %d", usbtv->udev->bus->busnum,
+		usbtv->udev->devnum);
+
+	snd_card_set_dev(card, usbtv->dev);
+
+	usbtv->snd = card;
+
+	rv = snd_pcm_new(card, "USBTV Audio", 0, 0, 1, &pcm);
+	if (rv < 0)
+		goto err;
+
+	strncpy(pcm->name, "USBTV Audio Input", sizeof(pcm->name) - 1);
+	pcm->info_flags = 0;
+	pcm->private_data = usbtv;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usbtv_pcm_ops);
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+		snd_dma_continuous_data(GFP_KERNEL), USBTV_AUDIO_BUFFER,
+		USBTV_AUDIO_BUFFER);
+
+	rv = snd_card_register(card);
+	if (rv)
+		goto err;
+
+	return 0;
+
+err:
+	usbtv->snd = NULL;
+	snd_card_free(card);
+
+	return rv;
+}
+
+void usbtv_audio_free(struct usbtv *usbtv)
+{
+	if (usbtv->snd && usbtv->udev) {
+		snd_card_free(usbtv->snd);
+		usbtv->snd = NULL;
+	}
+}
diff --git a/usbtv-core.c b/usbtv-core.c
index e89e48b..bdc920c 100644
--- a/usbtv-core.c
+++ b/usbtv-core.c
@@ -1,5 +1,5 @@
 /*
- * Fushicai USBTV007 Video Grabber Driver
+ * Fushicai USBTV007 Audio-Video Grabber Driver
  *
  * Product web site:
  * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
@@ -86,12 +86,19 @@ static int usbtv_probe(struct usb_interface *intf,
 	if (ret < 0)
 		goto usbtv_video_fail;
 
+	ret = usbtv_audio_init(usbtv);
+	if (ret < 0)
+		goto usbtv_audio_fail;
+
 	/* for simplicity we exploit the v4l2_device reference counting */
 	v4l2_device_get(&usbtv->v4l2_dev);
 
-	dev_info(dev, "Fushicai USBTV007 Video Grabber\n");
+	dev_info(dev, "Fushicai USBTV007 Audio-Video Grabber\n");
 	return 0;
 
+usbtv_audio_fail:
+	usbtv_video_free(usbtv);
+
 usbtv_video_fail:
 	kfree(usbtv);
 
@@ -106,6 +113,7 @@ static void usbtv_disconnect(struct usb_interface *intf)
 	if (!usbtv)
 		return;
 
+	usbtv_audio_free(usbtv);
 	usbtv_video_free(usbtv);
 
 	usb_put_dev(usbtv->udev);
@@ -122,8 +130,8 @@ struct usb_device_id usbtv_id_table[] = {
 };
 MODULE_DEVICE_TABLE(usb, usbtv_id_table);
 
-MODULE_AUTHOR("Lubomir Rintel");
-MODULE_DESCRIPTION("Fushicai USBTV007 Video Grabber Driver");
+MODULE_AUTHOR("Lubomir Rintel, Federico Simoncelli");
+MODULE_DESCRIPTION("Fushicai USBTV007 Audio-Video Grabber Driver");
 MODULE_LICENSE("Dual BSD/GPL");
 
 struct usb_driver usbtv_usb_driver = {
diff --git a/usbtv-video.c b/usbtv-video.c
index 496bc2e..da604fa 100644
--- a/usbtv-video.c
+++ b/usbtv-video.c
@@ -1,5 +1,5 @@
 /*
- * Fushicai USBTV007 Video Grabber Driver
+ * Fushicai USBTV007 Audio-Video Grabber Driver
  *
  * Product web site:
  * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
@@ -79,7 +79,6 @@ static int usbtv_select_input(struct usbtv *usbtv, int input)
 		{ USBTV_BASE + 0x011f, 0x00f2 },
 		{ USBTV_BASE + 0x0127, 0x0060 },
 		{ USBTV_BASE + 0x00ae, 0x0010 },
-		{ USBTV_BASE + 0x0284, 0x00aa },
 		{ USBTV_BASE + 0x0239, 0x0060 },
 	};
 
@@ -88,7 +87,6 @@ static int usbtv_select_input(struct usbtv *usbtv, int input)
 		{ USBTV_BASE + 0x011f, 0x00ff },
 		{ USBTV_BASE + 0x0127, 0x0060 },
 		{ USBTV_BASE + 0x00ae, 0x0030 },
-		{ USBTV_BASE + 0x0284, 0x0088 },
 		{ USBTV_BASE + 0x0239, 0x0060 },
 	};
 
@@ -225,7 +223,6 @@ static int usbtv_setup_capture(struct usbtv *usbtv)
 		{ USBTV_BASE + 0x0159, 0x0006 },
 		{ USBTV_BASE + 0x015d, 0x0000 },
 
-		{ USBTV_BASE + 0x0284, 0x0088 },
 		{ USBTV_BASE + 0x0003, 0x0004 },
 		{ USBTV_BASE + 0x0100, 0x00d3 },
 		{ USBTV_BASE + 0x0115, 0x0015 },
@@ -434,6 +431,8 @@ static int usbtv_start(struct usbtv *usbtv)
 	int i;
 	int ret;
 
+	usbtv_audio_suspend(usbtv);
+
 	ret = usb_set_interface(usbtv->udev, 0, 0);
 	if (ret < 0)
 		return ret;
@@ -446,6 +445,8 @@ static int usbtv_start(struct usbtv *usbtv)
 	if (ret < 0)
 		return ret;
 
+	usbtv_audio_resume(usbtv);
+
 	for (i = 0; i < USBTV_ISOC_TRANSFERS; i++) {
 		struct urb *ip;
 
diff --git a/usbtv.h b/usbtv.h
index 536343d..8cb69ea 100644
--- a/usbtv.h
+++ b/usbtv.h
@@ -1,5 +1,5 @@
 /*
- * Fushicai USBTV007 Video Grabber Driver
+ * Fushicai USBTV007 Audio-Video Grabber Driver
  *
  * Copyright (c) 2013 Lubomir Rintel
  * All rights reserved.
@@ -27,6 +27,7 @@
 
 /* Hardware. */
 #define USBTV_VIDEO_ENDP	0x81
+#define USBTV_AUDIO_ENDP	0x83
 #define USBTV_BASE		0xc000
 #define USBTV_REQUEST_REG	12
 
@@ -38,6 +39,10 @@
 #define USBTV_CHUNK_SIZE	256
 #define USBTV_CHUNK		240
 
+#define USBTV_AUDIO_URBSIZE	20480
+#define USBTV_AUDIO_HDRSIZE	4
+#define USBTV_AUDIO_BUFFER	65536
+
 /* Chunk header. */
 #define USBTV_MAGIC_OK(chunk)	((be32_to_cpu(chunk[0]) & 0xff000000) \
 							== 0x88000000)
@@ -90,9 +95,23 @@ struct usbtv {
 	int iso_size;
 	unsigned int sequence;
 	struct urb *isoc_urbs[USBTV_ISOC_TRANSFERS];
+
+	/* audio */
+	struct snd_card *snd;
+	struct snd_pcm_substream *snd_substream;
+	atomic_t snd_stream;
+	struct work_struct snd_trigger;
+	struct urb *snd_bulk_urb;
+	size_t snd_buffer_pos;
+	size_t snd_period_pos;
 };
 
 int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size);
 
 int usbtv_video_init(struct usbtv *usbtv);
 void usbtv_video_free(struct usbtv *usbtv);
+
+int usbtv_audio_init(struct usbtv *usbtv);
+void usbtv_audio_free(struct usbtv *usbtv);
+void usbtv_audio_suspend(struct usbtv *usbtv);
+void usbtv_audio_resume(struct usbtv *usbtv);
-- 
1.8.4.2

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] usbtv: split core and video implementation
  2014-01-05 15:38 [PATCH 1/2] usbtv: split core and video implementation Federico Simoncelli
  2014-01-05 15:38 ` [PATCH 2/2] usbtv: add audio support Federico Simoncelli
@ 2014-01-05 16:37 ` Lubomir Rintel
  1 sibling, 0 replies; 5+ messages in thread
From: Lubomir Rintel @ 2014-01-05 16:37 UTC (permalink / raw)
  To: Federico Simoncelli; +Cc: alsa-devel, Federico Simoncelli

Hi Federico,

please apply this patch (and I guess the other one too) against the
Linux kernel tree (so that the filenames are not usbtv.c, but
drivers/media/usb/usbtv.c instead). They will likely get into mainline
via the media tree (the other one still needs to be cc-ed to alsa list)
and therefore need to be send to the linux media list too.

Once you apply the patches to the Linux tree, scripts/get_maintainer.pl
is very helpful in finding out where to send the patches.

Thank you!
Lubo

On Sun, 2014-01-05 at 16:38 +0100, Federico Simoncelli wrote:
> Signed-off-by: Federico Simoncelli <fsimonce@redhat.com>
> Reviewed-by: Lubomir Rintel <lkundrak@v3.sk>
> ---
>  Makefile                 |   3 +
>  usbtv-core.c             | 136 +++++++++++++++++++++++++++++++++++++++
>  usbtv.c => usbtv-video.c | 163 +++--------------------------------------------
>  usbtv.h                  |  98 ++++++++++++++++++++++++++++
>  4 files changed, 246 insertions(+), 154 deletions(-)
>  create mode 100644 usbtv-core.c
>  rename usbtv.c => usbtv-video.c (82%)
>  create mode 100644 usbtv.h
> 
> diff --git a/Makefile b/Makefile
> index 28b872f..775316a 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1 +1,4 @@
> +usbtv-y := usbtv-core.o \
> +	usbtv-video.o
> +
>  obj-$(CONFIG_VIDEO_USBTV) += usbtv.o
> diff --git a/usbtv-core.c b/usbtv-core.c
> new file mode 100644
> index 0000000..e89e48b
> --- /dev/null
> +++ b/usbtv-core.c
> @@ -0,0 +1,136 @@
> +/*
> + * Fushicai USBTV007 Video Grabber Driver
> + *
> + * Product web site:
> + * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
> + *
> + * Following LWN articles were very useful in construction of this driver:
> + * Video4Linux2 API series: http://lwn.net/Articles/203924/
> + * videobuf2 API explanation: http://lwn.net/Articles/447435/
> + * Thanks go to Jonathan Corbet for providing this quality documentation.
> + * He is awesome.
> + *
> + * Copyright (c) 2013 Lubomir Rintel
> + * All rights reserved.
> + * No physical hardware was harmed running Windows during the
> + * reverse-engineering activity
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + * 1. Redistributions of source code must retain the above copyright
> + *    notice, this list of conditions, and the following disclaimer,
> + *    without modification.
> + * 2. The name of the author may not be used to endorse or promote products
> + *    derived from this software without specific prior written permission.
> + *
> + * Alternatively, this software may be distributed under the terms of the
> + * GNU General Public License ("GPL").
> + */
> +
> +#include <linux/module.h>
> +
> +#include "usbtv.h"
> +
> +int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size)
> +{
> +	int ret;
> +	int pipe = usb_rcvctrlpipe(usbtv->udev, 0);
> +	int i;
> +
> +	for (i = 0; i < size; i++) {
> +		u16 index = regs[i][0];
> +		u16 value = regs[i][1];
> +
> +		ret = usb_control_msg(usbtv->udev, pipe, USBTV_REQUEST_REG,
> +			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +			value, index, NULL, 0, 0);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int usbtv_probe(struct usb_interface *intf,
> +	const struct usb_device_id *id)
> +{
> +	int ret;
> +	int size;
> +	struct device *dev = &intf->dev;
> +	struct usbtv *usbtv;
> +
> +	/* Checks that the device is what we think it is. */
> +	if (intf->num_altsetting != 2)
> +		return -ENODEV;
> +	if (intf->altsetting[1].desc.bNumEndpoints != 4)
> +		return -ENODEV;
> +
> +	/* Packet size is split into 11 bits of base size and count of
> +	 * extra multiplies of it.*/
> +	size = usb_endpoint_maxp(&intf->altsetting[1].endpoint[0].desc);
> +	size = (size & 0x07ff) * (((size & 0x1800) >> 11) + 1);
> +
> +	/* Device structure */
> +	usbtv = kzalloc(sizeof(struct usbtv), GFP_KERNEL);
> +	if (usbtv == NULL)
> +		return -ENOMEM;
> +	usbtv->dev = dev;
> +	usbtv->udev = usb_get_dev(interface_to_usbdev(intf));
> +
> +	usbtv->iso_size = size;
> +
> +	usb_set_intfdata(intf, usbtv);
> +
> +	ret = usbtv_video_init(usbtv);
> +	if (ret < 0)
> +		goto usbtv_video_fail;
> +
> +	/* for simplicity we exploit the v4l2_device reference counting */
> +	v4l2_device_get(&usbtv->v4l2_dev);
> +
> +	dev_info(dev, "Fushicai USBTV007 Video Grabber\n");
> +	return 0;
> +
> +usbtv_video_fail:
> +	kfree(usbtv);
> +
> +	return ret;
> +}
> +
> +static void usbtv_disconnect(struct usb_interface *intf)
> +{
> +	struct usbtv *usbtv = usb_get_intfdata(intf);
> +	usb_set_intfdata(intf, NULL);
> +
> +	if (!usbtv)
> +		return;
> +
> +	usbtv_video_free(usbtv);
> +
> +	usb_put_dev(usbtv->udev);
> +	usbtv->udev = NULL;
> +
> +	/* the usbtv structure will be deallocated when v4l2 will be
> +	   done using it */
> +	v4l2_device_put(&usbtv->v4l2_dev);
> +}
> +
> +struct usb_device_id usbtv_id_table[] = {
> +	{ USB_DEVICE(0x1b71, 0x3002) },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(usb, usbtv_id_table);
> +
> +MODULE_AUTHOR("Lubomir Rintel");
> +MODULE_DESCRIPTION("Fushicai USBTV007 Video Grabber Driver");
> +MODULE_LICENSE("Dual BSD/GPL");
> +
> +struct usb_driver usbtv_usb_driver = {
> +	.name = "usbtv",
> +	.id_table = usbtv_id_table,
> +	.probe = usbtv_probe,
> +	.disconnect = usbtv_disconnect,
> +};
> +
> +module_usb_driver(usbtv_usb_driver);
> diff --git a/usbtv.c b/usbtv-video.c
> similarity index 82%
> rename from usbtv.c
> rename to usbtv-video.c
> index 6222a4a..496bc2e 100644
> --- a/usbtv.c
> +++ b/usbtv-video.c
> @@ -28,45 +28,10 @@
>   * GNU General Public License ("GPL").
>   */
>  
> -#include <linux/init.h>
> -#include <linux/list.h>
> -#include <linux/module.h>
> -#include <linux/slab.h>
> -#include <linux/usb.h>
> -#include <linux/videodev2.h>
> -
> -#include <media/v4l2-device.h>
>  #include <media/v4l2-ioctl.h>
>  #include <media/videobuf2-core.h>
> -#include <media/videobuf2-vmalloc.h>
> -
> -/* Hardware. */
> -#define USBTV_VIDEO_ENDP	0x81
> -#define USBTV_BASE		0xc000
> -#define USBTV_REQUEST_REG	12
> -
> -/* Number of concurrent isochronous urbs submitted.
> - * Higher numbers was seen to overly saturate the USB bus. */
> -#define USBTV_ISOC_TRANSFERS	16
> -#define USBTV_ISOC_PACKETS	8
> -
> -#define USBTV_CHUNK_SIZE	256
> -#define USBTV_CHUNK		240
> -
> -/* Chunk header. */
> -#define USBTV_MAGIC_OK(chunk)	((be32_to_cpu(chunk[0]) & 0xff000000) \
> -							== 0x88000000)
> -#define USBTV_FRAME_ID(chunk)	((be32_to_cpu(chunk[0]) & 0x00ff0000) >> 16)
> -#define USBTV_ODD(chunk)	((be32_to_cpu(chunk[0]) & 0x0000f000) >> 15)
> -#define USBTV_CHUNK_NO(chunk)	(be32_to_cpu(chunk[0]) & 0x00000fff)
> -
> -#define USBTV_TV_STD  (V4L2_STD_525_60 | V4L2_STD_PAL)
> -
> -/* parameters for supported TV norms */
> -struct usbtv_norm_params {
> -	v4l2_std_id norm;
> -	int cap_width, cap_height;
> -};
> +
> +#include "usbtv.h"
>  
>  static struct usbtv_norm_params norm_params[] = {
>  	{
> @@ -81,43 +46,6 @@ static struct usbtv_norm_params norm_params[] = {
>  	}
>  };
>  
> -/* A single videobuf2 frame buffer. */
> -struct usbtv_buf {
> -	struct vb2_buffer vb;
> -	struct list_head list;
> -};
> -
> -/* Per-device structure. */
> -struct usbtv {
> -	struct device *dev;
> -	struct usb_device *udev;
> -	struct v4l2_device v4l2_dev;
> -	struct video_device vdev;
> -	struct vb2_queue vb2q;
> -	struct mutex v4l2_lock;
> -	struct mutex vb2q_lock;
> -
> -	/* List of videobuf2 buffers protected by a lock. */
> -	spinlock_t buflock;
> -	struct list_head bufs;
> -
> -	/* Number of currently processed frame, useful find
> -	 * out when a new one begins. */
> -	u32 frame_id;
> -	int chunks_done;
> -
> -	enum {
> -		USBTV_COMPOSITE_INPUT,
> -		USBTV_SVIDEO_INPUT,
> -	} input;
> -	v4l2_std_id norm;
> -	int width, height;
> -	int n_chunks;
> -	int iso_size;
> -	unsigned int sequence;
> -	struct urb *isoc_urbs[USBTV_ISOC_TRANSFERS];
> -};
> -
>  static int usbtv_configure_for_norm(struct usbtv *usbtv, v4l2_std_id norm)
>  {
>  	int i, ret = 0;
> @@ -142,26 +70,6 @@ static int usbtv_configure_for_norm(struct usbtv *usbtv, v4l2_std_id norm)
>  	return ret;
>  }
>  
> -static int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size)
> -{
> -	int ret;
> -	int pipe = usb_rcvctrlpipe(usbtv->udev, 0);
> -	int i;
> -
> -	for (i = 0; i < size; i++) {
> -		u16 index = regs[i][0];
> -		u16 value = regs[i][1];
> -
> -		ret = usb_control_msg(usbtv->udev, pipe, USBTV_REQUEST_REG,
> -			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> -			value, index, NULL, 0, 0);
> -		if (ret < 0)
> -			return ret;
> -	}
> -
> -	return 0;
> -}
> -
>  static int usbtv_select_input(struct usbtv *usbtv, int input)
>  {
>  	int ret;
> @@ -560,12 +468,6 @@ start_fail:
>  	return ret;
>  }
>  
> -struct usb_device_id usbtv_id_table[] = {
> -	{ USB_DEVICE(0x1b71, 0x3002) },
> -	{}
> -};
> -MODULE_DEVICE_TABLE(usb, usbtv_id_table);
> -
>  static int usbtv_querycap(struct file *file, void *priv,
>  				struct v4l2_capability *cap)
>  {
> @@ -759,33 +661,9 @@ static void usbtv_release(struct v4l2_device *v4l2_dev)
>  	kfree(usbtv);
>  }
>  
> -static int usbtv_probe(struct usb_interface *intf,
> -	const struct usb_device_id *id)
> +int usbtv_video_init(struct usbtv *usbtv)
>  {
>  	int ret;
> -	int size;
> -	struct device *dev = &intf->dev;
> -	struct usbtv *usbtv;
> -
> -	/* Checks that the device is what we think it is. */
> -	if (intf->num_altsetting != 2)
> -		return -ENODEV;
> -	if (intf->altsetting[1].desc.bNumEndpoints != 4)
> -		return -ENODEV;
> -
> -	/* Packet size is split into 11 bits of base size and count of
> -	 * extra multiplies of it.*/
> -	size = usb_endpoint_maxp(&intf->altsetting[1].endpoint[0].desc);
> -	size = (size & 0x07ff) * (((size & 0x1800) >> 11) + 1);
> -
> -	/* Device structure */
> -	usbtv = kzalloc(sizeof(struct usbtv), GFP_KERNEL);
> -	if (usbtv == NULL)
> -		return -ENOMEM;
> -	usbtv->dev = dev;
> -	usbtv->udev = usb_get_dev(interface_to_usbdev(intf));
> -
> -	usbtv->iso_size = size;
>  
>  	(void)usbtv_configure_for_norm(usbtv, V4L2_STD_525_60);
>  
> @@ -805,20 +683,18 @@ static int usbtv_probe(struct usb_interface *intf,
>  	usbtv->vb2q.lock = &usbtv->vb2q_lock;
>  	ret = vb2_queue_init(&usbtv->vb2q);
>  	if (ret < 0) {
> -		dev_warn(dev, "Could not initialize videobuf2 queue\n");
> -		goto usbtv_fail;
> +		dev_warn(usbtv->dev, "Could not initialize videobuf2 queue\n");
> +		return ret;
>  	}
>  
>  	/* v4l2 structure */
>  	usbtv->v4l2_dev.release = usbtv_release;
> -	ret = v4l2_device_register(dev, &usbtv->v4l2_dev);
> +	ret = v4l2_device_register(usbtv->dev, &usbtv->v4l2_dev);
>  	if (ret < 0) {
> -		dev_warn(dev, "Could not register v4l2 device\n");
> +		dev_warn(usbtv->dev, "Could not register v4l2 device\n");
>  		goto v4l2_fail;
>  	}
>  
> -	usb_set_intfdata(intf, usbtv);
> -
>  	/* Video structure */
>  	strlcpy(usbtv->vdev.name, "usbtv", sizeof(usbtv->vdev.name));
>  	usbtv->vdev.v4l2_dev = &usbtv->v4l2_dev;
> @@ -832,52 +708,31 @@ static int usbtv_probe(struct usb_interface *intf,
>  	video_set_drvdata(&usbtv->vdev, usbtv);
>  	ret = video_register_device(&usbtv->vdev, VFL_TYPE_GRABBER, -1);
>  	if (ret < 0) {
> -		dev_warn(dev, "Could not register video device\n");
> +		dev_warn(usbtv->dev, "Could not register video device\n");
>  		goto vdev_fail;
>  	}
>  
> -	dev_info(dev, "Fushicai USBTV007 Video Grabber\n");
>  	return 0;
>  
>  vdev_fail:
>  	v4l2_device_unregister(&usbtv->v4l2_dev);
>  v4l2_fail:
>  	vb2_queue_release(&usbtv->vb2q);
> -usbtv_fail:
> -	kfree(usbtv);
>  
>  	return ret;
>  }
>  
> -static void usbtv_disconnect(struct usb_interface *intf)
> +void usbtv_video_free(struct usbtv *usbtv)
>  {
> -	struct usbtv *usbtv = usb_get_intfdata(intf);
> -
>  	mutex_lock(&usbtv->vb2q_lock);
>  	mutex_lock(&usbtv->v4l2_lock);
>  
>  	usbtv_stop(usbtv);
> -	usb_set_intfdata(intf, NULL);
>  	video_unregister_device(&usbtv->vdev);
>  	v4l2_device_disconnect(&usbtv->v4l2_dev);
> -	usb_put_dev(usbtv->udev);
> -	usbtv->udev = NULL;
>  
>  	mutex_unlock(&usbtv->v4l2_lock);
>  	mutex_unlock(&usbtv->vb2q_lock);
>  
>  	v4l2_device_put(&usbtv->v4l2_dev);
>  }
> -
> -MODULE_AUTHOR("Lubomir Rintel");
> -MODULE_DESCRIPTION("Fushicai USBTV007 Video Grabber Driver");
> -MODULE_LICENSE("Dual BSD/GPL");
> -
> -struct usb_driver usbtv_usb_driver = {
> -	.name = "usbtv",
> -	.id_table = usbtv_id_table,
> -	.probe = usbtv_probe,
> -	.disconnect = usbtv_disconnect,
> -};
> -
> -module_usb_driver(usbtv_usb_driver);
> diff --git a/usbtv.h b/usbtv.h
> new file mode 100644
> index 0000000..536343d
> --- /dev/null
> +++ b/usbtv.h
> @@ -0,0 +1,98 @@
> +/*
> + * Fushicai USBTV007 Video Grabber Driver
> + *
> + * Copyright (c) 2013 Lubomir Rintel
> + * All rights reserved.
> + * No physical hardware was harmed running Windows during the
> + * reverse-engineering activity
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + * 1. Redistributions of source code must retain the above copyright
> + *    notice, this list of conditions, and the following disclaimer,
> + *    without modification.
> + * 2. The name of the author may not be used to endorse or promote products
> + *    derived from this software without specific prior written permission.
> + *
> + * Alternatively, this software may be distributed under the terms of the
> + * GNU General Public License ("GPL").
> + */
> +
> +#include <linux/slab.h>
> +#include <linux/usb.h>
> +
> +#include <media/v4l2-device.h>
> +#include <media/videobuf2-vmalloc.h>
> +
> +/* Hardware. */
> +#define USBTV_VIDEO_ENDP	0x81
> +#define USBTV_BASE		0xc000
> +#define USBTV_REQUEST_REG	12
> +
> +/* Number of concurrent isochronous urbs submitted.
> + * Higher numbers was seen to overly saturate the USB bus. */
> +#define USBTV_ISOC_TRANSFERS	16
> +#define USBTV_ISOC_PACKETS	8
> +
> +#define USBTV_CHUNK_SIZE	256
> +#define USBTV_CHUNK		240
> +
> +/* Chunk header. */
> +#define USBTV_MAGIC_OK(chunk)	((be32_to_cpu(chunk[0]) & 0xff000000) \
> +							== 0x88000000)
> +#define USBTV_FRAME_ID(chunk)	((be32_to_cpu(chunk[0]) & 0x00ff0000) >> 16)
> +#define USBTV_ODD(chunk)	((be32_to_cpu(chunk[0]) & 0x0000f000) >> 15)
> +#define USBTV_CHUNK_NO(chunk)	(be32_to_cpu(chunk[0]) & 0x00000fff)
> +
> +#define USBTV_TV_STD  (V4L2_STD_525_60 | V4L2_STD_PAL)
> +
> +/* parameters for supported TV norms */
> +struct usbtv_norm_params {
> +	v4l2_std_id norm;
> +	int cap_width, cap_height;
> +};
> +
> +/* A single videobuf2 frame buffer. */
> +struct usbtv_buf {
> +	struct vb2_buffer vb;
> +	struct list_head list;
> +};
> +
> +/* Per-device structure. */
> +struct usbtv {
> +	struct device *dev;
> +	struct usb_device *udev;
> +
> +	/* video */
> +	struct v4l2_device v4l2_dev;
> +	struct video_device vdev;
> +	struct vb2_queue vb2q;
> +	struct mutex v4l2_lock;
> +	struct mutex vb2q_lock;
> +
> +	/* List of videobuf2 buffers protected by a lock. */
> +	spinlock_t buflock;
> +	struct list_head bufs;
> +
> +	/* Number of currently processed frame, useful find
> +	 * out when a new one begins. */
> +	u32 frame_id;
> +	int chunks_done;
> +
> +	enum {
> +		USBTV_COMPOSITE_INPUT,
> +		USBTV_SVIDEO_INPUT,
> +	} input;
> +	v4l2_std_id norm;
> +	int width, height;
> +	int n_chunks;
> +	int iso_size;
> +	unsigned int sequence;
> +	struct urb *isoc_urbs[USBTV_ISOC_TRANSFERS];
> +};
> +
> +int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size);
> +
> +int usbtv_video_init(struct usbtv *usbtv);
> +void usbtv_video_free(struct usbtv *usbtv);

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] usbtv: add audio support
  2014-01-05 15:38 ` [PATCH 2/2] usbtv: add audio support Federico Simoncelli
@ 2014-01-05 16:43   ` Lubomir Rintel
  0 siblings, 0 replies; 5+ messages in thread
From: Lubomir Rintel @ 2014-01-05 16:43 UTC (permalink / raw)
  To: Federico Simoncelli; +Cc: alsa-devel, Federico Simoncelli

On Sun, 2014-01-05 at 16:38 +0100, Federico Simoncelli wrote:
...
> +	static const u16 setup[][2] = {
> +/*		{ USBTV_BASE + 0x00a2, 0x0013 }, */

A minor style issue here, please avoid useless comments.


> +	strncpy(card->driver, usbtv->dev->driver->name,
> +		sizeof(card->driver) - 1);
> +	strncpy(card->shortname, "usbtv", sizeof(card->shortname) - 1);

I suggest you use the strlcpy() instead, as is used in the rest of the
driver. Aside from that it is more consistent, it would make it a bit
easier to verify that the string is properly terminated without having
to take a look at snd_card_create() to see whether zeroes out the
structure for you.

> +	strncpy(pcm->name, "USBTV Audio Input", sizeof(pcm->name) - 1);

Ditto.

Thank you!
Lubo

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/2] usbtv: split core and video implementation
@ 2014-01-07 22:13 Federico Simoncelli
  0 siblings, 0 replies; 5+ messages in thread
From: Federico Simoncelli @ 2014-01-07 22:13 UTC (permalink / raw)
  To: linux-media, alsa-devel
  Cc: m.chehab, lkundrak, hans.verkuil, Federico Simoncelli

From: Federico Simoncelli <fsimonce@redhat.com>

Signed-off-by: Federico Simoncelli <fsimonce@redhat.com>
Reviewed-by: Lubomir Rintel <lkundrak@v3.sk>
---
 drivers/media/usb/usbtv/Makefile                   |   3 +
 drivers/media/usb/usbtv/usbtv-core.c               | 136 +++++++++++++++++
 drivers/media/usb/usbtv/{usbtv.c => usbtv-video.c} | 163 ++-------------------
 drivers/media/usb/usbtv/usbtv.h                    |  98 +++++++++++++
 4 files changed, 246 insertions(+), 154 deletions(-)
 create mode 100644 drivers/media/usb/usbtv/usbtv-core.c
 rename drivers/media/usb/usbtv/{usbtv.c => usbtv-video.c} (82%)
 create mode 100644 drivers/media/usb/usbtv/usbtv.h

diff --git a/drivers/media/usb/usbtv/Makefile b/drivers/media/usb/usbtv/Makefile
index 28b872f..775316a 100644
--- a/drivers/media/usb/usbtv/Makefile
+++ b/drivers/media/usb/usbtv/Makefile
@@ -1 +1,4 @@
+usbtv-y := usbtv-core.o \
+	usbtv-video.o
+
 obj-$(CONFIG_VIDEO_USBTV) += usbtv.o
diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c
new file mode 100644
index 0000000..e89e48b
--- /dev/null
+++ b/drivers/media/usb/usbtv/usbtv-core.c
@@ -0,0 +1,136 @@
+/*
+ * Fushicai USBTV007 Video Grabber Driver
+ *
+ * Product web site:
+ * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
+ *
+ * Following LWN articles were very useful in construction of this driver:
+ * Video4Linux2 API series: http://lwn.net/Articles/203924/
+ * videobuf2 API explanation: http://lwn.net/Articles/447435/
+ * Thanks go to Jonathan Corbet for providing this quality documentation.
+ * He is awesome.
+ *
+ * Copyright (c) 2013 Lubomir Rintel
+ * All rights reserved.
+ * No physical hardware was harmed running Windows during the
+ * reverse-engineering activity
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ */
+
+#include <linux/module.h>
+
+#include "usbtv.h"
+
+int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size)
+{
+	int ret;
+	int pipe = usb_rcvctrlpipe(usbtv->udev, 0);
+	int i;
+
+	for (i = 0; i < size; i++) {
+		u16 index = regs[i][0];
+		u16 value = regs[i][1];
+
+		ret = usb_control_msg(usbtv->udev, pipe, USBTV_REQUEST_REG,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			value, index, NULL, 0, 0);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int usbtv_probe(struct usb_interface *intf,
+	const struct usb_device_id *id)
+{
+	int ret;
+	int size;
+	struct device *dev = &intf->dev;
+	struct usbtv *usbtv;
+
+	/* Checks that the device is what we think it is. */
+	if (intf->num_altsetting != 2)
+		return -ENODEV;
+	if (intf->altsetting[1].desc.bNumEndpoints != 4)
+		return -ENODEV;
+
+	/* Packet size is split into 11 bits of base size and count of
+	 * extra multiplies of it.*/
+	size = usb_endpoint_maxp(&intf->altsetting[1].endpoint[0].desc);
+	size = (size & 0x07ff) * (((size & 0x1800) >> 11) + 1);
+
+	/* Device structure */
+	usbtv = kzalloc(sizeof(struct usbtv), GFP_KERNEL);
+	if (usbtv == NULL)
+		return -ENOMEM;
+	usbtv->dev = dev;
+	usbtv->udev = usb_get_dev(interface_to_usbdev(intf));
+
+	usbtv->iso_size = size;
+
+	usb_set_intfdata(intf, usbtv);
+
+	ret = usbtv_video_init(usbtv);
+	if (ret < 0)
+		goto usbtv_video_fail;
+
+	/* for simplicity we exploit the v4l2_device reference counting */
+	v4l2_device_get(&usbtv->v4l2_dev);
+
+	dev_info(dev, "Fushicai USBTV007 Video Grabber\n");
+	return 0;
+
+usbtv_video_fail:
+	kfree(usbtv);
+
+	return ret;
+}
+
+static void usbtv_disconnect(struct usb_interface *intf)
+{
+	struct usbtv *usbtv = usb_get_intfdata(intf);
+	usb_set_intfdata(intf, NULL);
+
+	if (!usbtv)
+		return;
+
+	usbtv_video_free(usbtv);
+
+	usb_put_dev(usbtv->udev);
+	usbtv->udev = NULL;
+
+	/* the usbtv structure will be deallocated when v4l2 will be
+	   done using it */
+	v4l2_device_put(&usbtv->v4l2_dev);
+}
+
+struct usb_device_id usbtv_id_table[] = {
+	{ USB_DEVICE(0x1b71, 0x3002) },
+	{}
+};
+MODULE_DEVICE_TABLE(usb, usbtv_id_table);
+
+MODULE_AUTHOR("Lubomir Rintel");
+MODULE_DESCRIPTION("Fushicai USBTV007 Video Grabber Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+struct usb_driver usbtv_usb_driver = {
+	.name = "usbtv",
+	.id_table = usbtv_id_table,
+	.probe = usbtv_probe,
+	.disconnect = usbtv_disconnect,
+};
+
+module_usb_driver(usbtv_usb_driver);
diff --git a/drivers/media/usb/usbtv/usbtv.c b/drivers/media/usb/usbtv/usbtv-video.c
similarity index 82%
rename from drivers/media/usb/usbtv/usbtv.c
rename to drivers/media/usb/usbtv/usbtv-video.c
index 6222a4a..496bc2e 100644
--- a/drivers/media/usb/usbtv/usbtv.c
+++ b/drivers/media/usb/usbtv/usbtv-video.c
@@ -28,45 +28,10 @@
  * GNU General Public License ("GPL").
  */
 
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-#include <linux/videodev2.h>
-
-#include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
 #include <media/videobuf2-core.h>
-#include <media/videobuf2-vmalloc.h>
-
-/* Hardware. */
-#define USBTV_VIDEO_ENDP	0x81
-#define USBTV_BASE		0xc000
-#define USBTV_REQUEST_REG	12
-
-/* Number of concurrent isochronous urbs submitted.
- * Higher numbers was seen to overly saturate the USB bus. */
-#define USBTV_ISOC_TRANSFERS	16
-#define USBTV_ISOC_PACKETS	8
-
-#define USBTV_CHUNK_SIZE	256
-#define USBTV_CHUNK		240
-
-/* Chunk header. */
-#define USBTV_MAGIC_OK(chunk)	((be32_to_cpu(chunk[0]) & 0xff000000) \
-							== 0x88000000)
-#define USBTV_FRAME_ID(chunk)	((be32_to_cpu(chunk[0]) & 0x00ff0000) >> 16)
-#define USBTV_ODD(chunk)	((be32_to_cpu(chunk[0]) & 0x0000f000) >> 15)
-#define USBTV_CHUNK_NO(chunk)	(be32_to_cpu(chunk[0]) & 0x00000fff)
-
-#define USBTV_TV_STD  (V4L2_STD_525_60 | V4L2_STD_PAL)
-
-/* parameters for supported TV norms */
-struct usbtv_norm_params {
-	v4l2_std_id norm;
-	int cap_width, cap_height;
-};
+
+#include "usbtv.h"
 
 static struct usbtv_norm_params norm_params[] = {
 	{
@@ -81,43 +46,6 @@ static struct usbtv_norm_params norm_params[] = {
 	}
 };
 
-/* A single videobuf2 frame buffer. */
-struct usbtv_buf {
-	struct vb2_buffer vb;
-	struct list_head list;
-};
-
-/* Per-device structure. */
-struct usbtv {
-	struct device *dev;
-	struct usb_device *udev;
-	struct v4l2_device v4l2_dev;
-	struct video_device vdev;
-	struct vb2_queue vb2q;
-	struct mutex v4l2_lock;
-	struct mutex vb2q_lock;
-
-	/* List of videobuf2 buffers protected by a lock. */
-	spinlock_t buflock;
-	struct list_head bufs;
-
-	/* Number of currently processed frame, useful find
-	 * out when a new one begins. */
-	u32 frame_id;
-	int chunks_done;
-
-	enum {
-		USBTV_COMPOSITE_INPUT,
-		USBTV_SVIDEO_INPUT,
-	} input;
-	v4l2_std_id norm;
-	int width, height;
-	int n_chunks;
-	int iso_size;
-	unsigned int sequence;
-	struct urb *isoc_urbs[USBTV_ISOC_TRANSFERS];
-};
-
 static int usbtv_configure_for_norm(struct usbtv *usbtv, v4l2_std_id norm)
 {
 	int i, ret = 0;
@@ -142,26 +70,6 @@ static int usbtv_configure_for_norm(struct usbtv *usbtv, v4l2_std_id norm)
 	return ret;
 }
 
-static int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size)
-{
-	int ret;
-	int pipe = usb_rcvctrlpipe(usbtv->udev, 0);
-	int i;
-
-	for (i = 0; i < size; i++) {
-		u16 index = regs[i][0];
-		u16 value = regs[i][1];
-
-		ret = usb_control_msg(usbtv->udev, pipe, USBTV_REQUEST_REG,
-			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-			value, index, NULL, 0, 0);
-		if (ret < 0)
-			return ret;
-	}
-
-	return 0;
-}
-
 static int usbtv_select_input(struct usbtv *usbtv, int input)
 {
 	int ret;
@@ -560,12 +468,6 @@ start_fail:
 	return ret;
 }
 
-struct usb_device_id usbtv_id_table[] = {
-	{ USB_DEVICE(0x1b71, 0x3002) },
-	{}
-};
-MODULE_DEVICE_TABLE(usb, usbtv_id_table);
-
 static int usbtv_querycap(struct file *file, void *priv,
 				struct v4l2_capability *cap)
 {
@@ -759,33 +661,9 @@ static void usbtv_release(struct v4l2_device *v4l2_dev)
 	kfree(usbtv);
 }
 
-static int usbtv_probe(struct usb_interface *intf,
-	const struct usb_device_id *id)
+int usbtv_video_init(struct usbtv *usbtv)
 {
 	int ret;
-	int size;
-	struct device *dev = &intf->dev;
-	struct usbtv *usbtv;
-
-	/* Checks that the device is what we think it is. */
-	if (intf->num_altsetting != 2)
-		return -ENODEV;
-	if (intf->altsetting[1].desc.bNumEndpoints != 4)
-		return -ENODEV;
-
-	/* Packet size is split into 11 bits of base size and count of
-	 * extra multiplies of it.*/
-	size = usb_endpoint_maxp(&intf->altsetting[1].endpoint[0].desc);
-	size = (size & 0x07ff) * (((size & 0x1800) >> 11) + 1);
-
-	/* Device structure */
-	usbtv = kzalloc(sizeof(struct usbtv), GFP_KERNEL);
-	if (usbtv == NULL)
-		return -ENOMEM;
-	usbtv->dev = dev;
-	usbtv->udev = usb_get_dev(interface_to_usbdev(intf));
-
-	usbtv->iso_size = size;
 
 	(void)usbtv_configure_for_norm(usbtv, V4L2_STD_525_60);
 
@@ -805,20 +683,18 @@ static int usbtv_probe(struct usb_interface *intf,
 	usbtv->vb2q.lock = &usbtv->vb2q_lock;
 	ret = vb2_queue_init(&usbtv->vb2q);
 	if (ret < 0) {
-		dev_warn(dev, "Could not initialize videobuf2 queue\n");
-		goto usbtv_fail;
+		dev_warn(usbtv->dev, "Could not initialize videobuf2 queue\n");
+		return ret;
 	}
 
 	/* v4l2 structure */
 	usbtv->v4l2_dev.release = usbtv_release;
-	ret = v4l2_device_register(dev, &usbtv->v4l2_dev);
+	ret = v4l2_device_register(usbtv->dev, &usbtv->v4l2_dev);
 	if (ret < 0) {
-		dev_warn(dev, "Could not register v4l2 device\n");
+		dev_warn(usbtv->dev, "Could not register v4l2 device\n");
 		goto v4l2_fail;
 	}
 
-	usb_set_intfdata(intf, usbtv);
-
 	/* Video structure */
 	strlcpy(usbtv->vdev.name, "usbtv", sizeof(usbtv->vdev.name));
 	usbtv->vdev.v4l2_dev = &usbtv->v4l2_dev;
@@ -832,52 +708,31 @@ static int usbtv_probe(struct usb_interface *intf,
 	video_set_drvdata(&usbtv->vdev, usbtv);
 	ret = video_register_device(&usbtv->vdev, VFL_TYPE_GRABBER, -1);
 	if (ret < 0) {
-		dev_warn(dev, "Could not register video device\n");
+		dev_warn(usbtv->dev, "Could not register video device\n");
 		goto vdev_fail;
 	}
 
-	dev_info(dev, "Fushicai USBTV007 Video Grabber\n");
 	return 0;
 
 vdev_fail:
 	v4l2_device_unregister(&usbtv->v4l2_dev);
 v4l2_fail:
 	vb2_queue_release(&usbtv->vb2q);
-usbtv_fail:
-	kfree(usbtv);
 
 	return ret;
 }
 
-static void usbtv_disconnect(struct usb_interface *intf)
+void usbtv_video_free(struct usbtv *usbtv)
 {
-	struct usbtv *usbtv = usb_get_intfdata(intf);
-
 	mutex_lock(&usbtv->vb2q_lock);
 	mutex_lock(&usbtv->v4l2_lock);
 
 	usbtv_stop(usbtv);
-	usb_set_intfdata(intf, NULL);
 	video_unregister_device(&usbtv->vdev);
 	v4l2_device_disconnect(&usbtv->v4l2_dev);
-	usb_put_dev(usbtv->udev);
-	usbtv->udev = NULL;
 
 	mutex_unlock(&usbtv->v4l2_lock);
 	mutex_unlock(&usbtv->vb2q_lock);
 
 	v4l2_device_put(&usbtv->v4l2_dev);
 }
-
-MODULE_AUTHOR("Lubomir Rintel");
-MODULE_DESCRIPTION("Fushicai USBTV007 Video Grabber Driver");
-MODULE_LICENSE("Dual BSD/GPL");
-
-struct usb_driver usbtv_usb_driver = {
-	.name = "usbtv",
-	.id_table = usbtv_id_table,
-	.probe = usbtv_probe,
-	.disconnect = usbtv_disconnect,
-};
-
-module_usb_driver(usbtv_usb_driver);
diff --git a/drivers/media/usb/usbtv/usbtv.h b/drivers/media/usb/usbtv/usbtv.h
new file mode 100644
index 0000000..536343d
--- /dev/null
+++ b/drivers/media/usb/usbtv/usbtv.h
@@ -0,0 +1,98 @@
+/*
+ * Fushicai USBTV007 Video Grabber Driver
+ *
+ * Copyright (c) 2013 Lubomir Rintel
+ * All rights reserved.
+ * No physical hardware was harmed running Windows during the
+ * reverse-engineering activity
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ */
+
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include <media/v4l2-device.h>
+#include <media/videobuf2-vmalloc.h>
+
+/* Hardware. */
+#define USBTV_VIDEO_ENDP	0x81
+#define USBTV_BASE		0xc000
+#define USBTV_REQUEST_REG	12
+
+/* Number of concurrent isochronous urbs submitted.
+ * Higher numbers was seen to overly saturate the USB bus. */
+#define USBTV_ISOC_TRANSFERS	16
+#define USBTV_ISOC_PACKETS	8
+
+#define USBTV_CHUNK_SIZE	256
+#define USBTV_CHUNK		240
+
+/* Chunk header. */
+#define USBTV_MAGIC_OK(chunk)	((be32_to_cpu(chunk[0]) & 0xff000000) \
+							== 0x88000000)
+#define USBTV_FRAME_ID(chunk)	((be32_to_cpu(chunk[0]) & 0x00ff0000) >> 16)
+#define USBTV_ODD(chunk)	((be32_to_cpu(chunk[0]) & 0x0000f000) >> 15)
+#define USBTV_CHUNK_NO(chunk)	(be32_to_cpu(chunk[0]) & 0x00000fff)
+
+#define USBTV_TV_STD  (V4L2_STD_525_60 | V4L2_STD_PAL)
+
+/* parameters for supported TV norms */
+struct usbtv_norm_params {
+	v4l2_std_id norm;
+	int cap_width, cap_height;
+};
+
+/* A single videobuf2 frame buffer. */
+struct usbtv_buf {
+	struct vb2_buffer vb;
+	struct list_head list;
+};
+
+/* Per-device structure. */
+struct usbtv {
+	struct device *dev;
+	struct usb_device *udev;
+
+	/* video */
+	struct v4l2_device v4l2_dev;
+	struct video_device vdev;
+	struct vb2_queue vb2q;
+	struct mutex v4l2_lock;
+	struct mutex vb2q_lock;
+
+	/* List of videobuf2 buffers protected by a lock. */
+	spinlock_t buflock;
+	struct list_head bufs;
+
+	/* Number of currently processed frame, useful find
+	 * out when a new one begins. */
+	u32 frame_id;
+	int chunks_done;
+
+	enum {
+		USBTV_COMPOSITE_INPUT,
+		USBTV_SVIDEO_INPUT,
+	} input;
+	v4l2_std_id norm;
+	int width, height;
+	int n_chunks;
+	int iso_size;
+	unsigned int sequence;
+	struct urb *isoc_urbs[USBTV_ISOC_TRANSFERS];
+};
+
+int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size);
+
+int usbtv_video_init(struct usbtv *usbtv);
+void usbtv_video_free(struct usbtv *usbtv);
-- 
1.8.4.2

^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2014-01-07 22:13 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-01-05 15:38 [PATCH 1/2] usbtv: split core and video implementation Federico Simoncelli
2014-01-05 15:38 ` [PATCH 2/2] usbtv: add audio support Federico Simoncelli
2014-01-05 16:43   ` Lubomir Rintel
2014-01-05 16:37 ` [PATCH 1/2] usbtv: split core and video implementation Lubomir Rintel
  -- strict thread matches above, loose matches on Subject: below --
2014-01-07 22:13 Federico Simoncelli

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).