2013-03-14 14:10:54

by Jon Arne Jørgensen

[permalink] [raw]
Subject: [RFC V1 0/8] Add a driver for somagic smi2021

This patch-set will add a driver for the Somagic SMI2021 chip.

This chip is found inside different usb video-capture devices.
Most of them are branded as EasyCap, but there also seems to be
some other brands selling devices with this chip.

This driver is split into two modules, where one is called smi2021-bootloader,
and the other is just called smi2021.

The bootloader is responsible for the upload of a firmware that is needed by some
versions of the devices.

All Somagic devices that need firmware seems to identify themselves
with the usb product id 0x0007. There is no way for the kernel to know
what firmware to upload to the device without user interaction.

If there is only one firmware present on the computer, the kernel
will upload that firmware to any device that identifies as 0x0007.
If there are multiple Somagic firmwares present, the user will have to pass
a module parameter to the smi2021-bootloader module to tell what firmware to use.


2013-03-14 14:10:56

by Jon Arne Jørgensen

[permalink] [raw]
Subject: [RFC V1 8/8] smi2021: Add Kconfig and Makefiles


Signed-off-by: Jon Arne Jørgensen <[email protected]>
---
drivers/media/usb/Kconfig | 1 +
drivers/media/usb/Makefile | 1 +
drivers/media/usb/smi2021/Kconfig | 18 ++++++++++++++++++
drivers/media/usb/smi2021/Makefile | 12 ++++++++++++
4 files changed, 32 insertions(+)
create mode 100644 drivers/media/usb/smi2021/Kconfig
create mode 100644 drivers/media/usb/smi2021/Makefile

diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig
index 0a7d520..dec0383 100644
--- a/drivers/media/usb/Kconfig
+++ b/drivers/media/usb/Kconfig
@@ -26,6 +26,7 @@ source "drivers/media/usb/hdpvr/Kconfig"
source "drivers/media/usb/tlg2300/Kconfig"
source "drivers/media/usb/usbvision/Kconfig"
source "drivers/media/usb/stk1160/Kconfig"
+source "drivers/media/usb/smi2021/Kconfig"
endif

if (MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT)
diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile
index 7f51d7e..932a6ba 100644
--- a/drivers/media/usb/Makefile
+++ b/drivers/media/usb/Makefile
@@ -20,3 +20,4 @@ obj-$(CONFIG_VIDEO_STK1160) += stk1160/
obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
obj-$(CONFIG_VIDEO_TM6000) += tm6000/
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
+obj-$(CONFIG_VIDEO_SMI2021) += smi2021/
diff --git a/drivers/media/usb/smi2021/Kconfig b/drivers/media/usb/smi2021/Kconfig
new file mode 100644
index 0000000..311375f
--- /dev/null
+++ b/drivers/media/usb/smi2021/Kconfig
@@ -0,0 +1,18 @@
+config VIDEO_SMI2021
+ tristate "Somagic SMI2021 USB video/audio capture support"
+ depends on VIDEO_DEV && I2C && SND && USB
+ select VIDEOBUF2_VMALLOC
+ select VIDEO_SAA711X
+ select SND_PCM
+ help
+ This is a video4linux driver for SMI2021 based video capture devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called smi2021
+
+config VIDEO_SMI2021_BOOTLOADER
+ bool "Somagic SMI2021 Bootloader"
+ depends on VIDEO_SMI2021
+
+ ---help---
+ Needed for most SMI2021 devices to work.
diff --git a/drivers/media/usb/smi2021/Makefile b/drivers/media/usb/smi2021/Makefile
new file mode 100644
index 0000000..4f28027
--- /dev/null
+++ b/drivers/media/usb/smi2021/Makefile
@@ -0,0 +1,12 @@
+smi2021-bootloader-y := smi2021_bl.o
+obj-$(CONFIG_VIDEO_SMI2021_BOOTLOADER) += smi2021-bootloader.o
+
+smi2021-y := smi2021_main.o \
+ smi2021_v4l2.o \
+ smi2021_video.o \
+ smi2021_i2c.o \
+ smi2021_audio.o \
+
+obj-$(CONFIG_VIDEO_SMI2021) += smi2021.o
+
+ccflags-y += -Idrivers/media/i2c
--
1.8.1.1

2013-03-14 14:11:14

by Jon Arne Jørgensen

[permalink] [raw]
Subject: [RFC V1 2/8] smi2021: Add smi2021_main.c

This is the core of the smi2021 module.
It will register the module with the kernel, and register the
usb probe function.

Signed-off-by: Jon Arne Jørgensen <[email protected]>
---
drivers/media/usb/smi2021/smi2021_main.c | 339 +++++++++++++++++++++++++++++++
1 file changed, 339 insertions(+)
create mode 100644 drivers/media/usb/smi2021/smi2021_main.c

diff --git a/drivers/media/usb/smi2021/smi2021_main.c b/drivers/media/usb/smi2021/smi2021_main.c
new file mode 100644
index 0000000..cc600e7
--- /dev/null
+++ b/drivers/media/usb/smi2021/smi2021_main.c
@@ -0,0 +1,339 @@
+/*******************************************************************************
+ * smi2021_main.c *
+ * *
+ * USB Driver for SMI2021 - EasyCAP *
+ * USB ID 1c88:003c *
+ * *
+ * *****************************************************************************
+ *
+ * Copyright 2011-2013 Jon Arne Jørgensen
+ * <jonjon.arnearne--a.t--gmail.com>
+ *
+ * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
+ *
+ * This file is part of SMI2021
+ * http://code.google.com/p/easycap-somagic-linux/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This driver is heavily influensed by the STK1160 driver.
+ * Copyright (C) 2012 Ezequiel Garcia
+ * <elezegarcia--a.t--gmail.com>
+ *
+ */
+
+#include "smi2021.h"
+
+#define VENDOR_ID 0x1c88
+
+static unsigned int imput;
+module_param(imput, int, 0644);
+MODULE_PARM_DESC(input, "Set default input");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jon Arne Jørgensen <jonjon.arnearne--a.t--gmail.com>");
+MODULE_DESCRIPTION("SMI2021 - EasyCap");
+MODULE_VERSION(SMI2021_DRIVER_VERSION);
+
+
+struct usb_device_id smi2021_usb_device_id_table[] = {
+ { USB_DEVICE(VENDOR_ID, 0x003c) },
+ { USB_DEVICE(VENDOR_ID, 0x003d) },
+ { USB_DEVICE(VENDOR_ID, 0x003e) },
+ { USB_DEVICE(VENDOR_ID, 0x003f) },
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, smi2021_usb_device_id_table);
+
+static unsigned short saa7113_addrs[] = {
+ 0x4a,
+ I2C_CLIENT_END
+};
+
+/******************************************************************************/
+/* */
+/* Write to saa7113 */
+/* */
+/******************************************************************************/
+
+inline int transfer_usb_ctrl(struct smi2021_dev *dev, u8 *data, int len)
+{
+ return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
+ 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0b, 0x00,
+ data, len, 1000);
+}
+
+int smi2021_write_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 val)
+{
+ int rc;
+ u8 snd_data[8];
+
+ memset(snd_data, 0x00, 8);
+
+ snd_data[SMI2021_CTRL_HEAD] = 0x0b;
+ snd_data[SMI2021_CTRL_ADDR] = addr;
+ snd_data[SMI2021_CTRL_DATA_SIZE] = 0x01;
+
+ if (addr) {
+ /* This is I2C data for the saa7113 chip */
+ snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0xc0;
+ snd_data[SMI2021_CTRL_BM_DATA_OFFSET] = 0x01;
+
+ snd_data[SMI2021_CTRL_I2C_REG] = reg;
+ snd_data[SMI2021_CTRL_I2C_VAL] = val;
+ } else {
+ /* This is register settings for the smi2021 chip */
+ snd_data[SMI2021_CTRL_BM_DATA_OFFSET] = 0x82;
+
+ snd_data[SMI2021_CTRL_REG_HI] = __cpu_to_be16(reg) >> 8;
+ snd_data[SMI2021_CTRL_REG_LO] = __cpu_to_be16(reg);
+
+ }
+
+ rc = transfer_usb_ctrl(dev, snd_data, 8);
+ if (rc < 0) {
+ smi2021_warn("write failed on register 0x%x, errno: %d\n",
+ reg, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+int smi2021_read_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 *val)
+{
+ int rc;
+ u8 rcv_data[13];
+ u8 snd_data[8];
+ memset(rcv_data, 0x00, 13);
+ memset(snd_data, 0x00, 8);
+
+ snd_data[SMI2021_CTRL_HEAD] = 0x0b;
+ snd_data[SMI2021_CTRL_ADDR] = addr;
+ snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0x84;
+ snd_data[SMI2021_CTRL_DATA_SIZE] = 0x01;
+ snd_data[SMI2021_CTRL_I2C_REG] = reg;
+
+ *val = 0;
+
+ rc = transfer_usb_ctrl(dev, snd_data, 8);
+ if (rc < 0) {
+ smi2021_warn(
+ "1st pass failing to read reg 0x%x, usb-errno: %d\n",
+ reg, rc);
+ return rc;
+ }
+
+ snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0xa0;
+ rc = transfer_usb_ctrl(dev, snd_data, 8);
+ if (rc < 0) {
+ smi2021_warn(
+ "2nd pass failing to read reg 0x%x, usb-errno: %d\n",
+ reg, rc);
+ return rc;
+ }
+
+ rc = usb_control_msg(dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0x80), 0x01,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0b, 0x00, rcv_data, 13, 1000);
+ if (rc < 0) {
+ smi2021_warn("Failed to read reg 0x%x, usb-errno: %d\n",
+ reg, rc);
+ return rc;
+ }
+
+ *val = rcv_data[SMI2021_CTRL_I2C_RCV_VAL];
+ return 0;
+}
+
+static void smi2021_reset_device(struct smi2021_dev *dev)
+{
+ smi2021_write_reg(dev, 0, 0x3a, 0x80);
+ smi2021_write_reg(dev, 0, 0x3b, 0x80);
+ smi2021_write_reg(dev, 0, 0x3b, 0x00);
+}
+
+static void release_v4l2_dev(struct v4l2_device *v4l2_dev)
+{
+ struct smi2021_dev *dev = container_of(v4l2_dev, struct smi2021_dev,
+ v4l2_dev);
+ smi2021_dbg("Releasing all resources\n");
+
+ smi2021_i2c_unregister(dev);
+
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ kfree(dev);
+}
+
+#define hb_mult(w_max_packet_size) (1 + (((w_max_packet_size) >> 11) & 0x03))
+
+static int smi2021_scan_usb(struct usb_interface *intf, struct usb_device *udev)
+{
+ int i, e, ifnum, sizedescr, size;
+ const struct usb_endpoint_descriptor *desc;
+ ifnum = intf->altsetting[0].desc.bInterfaceNumber;
+
+ for (i = 0; i < intf->num_altsetting; i++) {
+ for (e = 0; e < intf->altsetting[i].desc.bNumEndpoints; e++) {
+ desc = &intf->altsetting[i].endpoint[e].desc;
+ sizedescr = le16_to_cpu(desc->wMaxPacketSize);
+ size = sizedescr & 0x7ff;
+
+ if (udev->speed == USB_SPEED_HIGH)
+ size = size * hb_mult(sizedescr);
+ }
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/* */
+/* DEVICE - PROBE & DISCONNECT */
+/* */
+/******************************************************************************/
+static int smi2021_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *devid)
+{
+ int rc = 0;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct smi2021_dev *dev;
+
+ if (udev == (struct usb_device *)NULL) {
+ smi2021_err("device is NULL\n");
+ return -EFAULT;
+ }
+
+ smi2021_scan_usb(intf, udev);
+
+ dev = kzalloc(sizeof(struct smi2021_dev), GFP_KERNEL);
+ if (dev == NULL)
+ return -ENOMEM;
+
+ dev->udev = udev;
+ dev->dev = &intf->dev;
+ usb_set_intfdata(intf, dev);
+
+ /* Initialize videobuf2 stuff */
+ rc = smi2021_vb2_setup(dev);
+ if (rc < 0)
+ goto free_err;
+
+ spin_lock_init(&dev->buf_lock);
+ mutex_init(&dev->v4l2_lock);
+ mutex_init(&dev->vb_queue_lock);
+
+ rc = v4l2_ctrl_handler_init(&dev->ctrl_handler, 0);
+ if (rc) {
+ smi2021_err("v4l2_ctrl_handler_init failed with: %d\n", rc);
+ goto free_err;
+ }
+
+ dev->v4l2_dev.release = release_v4l2_dev;
+ dev->v4l2_dev.ctrl_handler = &dev->ctrl_handler;
+ rc = v4l2_device_register(dev->dev, &dev->v4l2_dev);
+ if (rc) {
+ smi2021_err("v4l2_device_register failed with %d\n", rc);
+ goto free_ctrl;
+ }
+
+ smi2021_reset_device(dev);
+
+ rc = smi2021_i2c_register(dev);
+ if (rc < 0)
+ goto unreg_v4l2;
+
+ dev->sd_saa7113 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "saa7113", 0, saa7113_addrs);
+
+ smi2021_dbg("Driver version %s successfully loaded\n",
+ SMI2021_DRIVER_VERSION);
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
+
+ rc = smi2021_snd_register(dev);
+ if (rc < 0)
+ goto unreg_i2c;
+
+ rc = smi2021_video_register(dev);
+ if (rc < 0)
+ goto unreg_snd;
+
+ return 0;
+
+unreg_snd:
+ smi2021_snd_unregister(dev);
+unreg_i2c:
+ smi2021_i2c_unregister(dev);
+unreg_v4l2:
+ v4l2_device_unregister(&dev->v4l2_dev);
+free_ctrl:
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
+free_err:
+ kfree(dev);
+
+ return rc;
+}
+
+static void smi2021_usb_disconnect(struct usb_interface *intf)
+{
+ struct smi2021_dev *dev = usb_get_intfdata(intf);
+
+ smi2021_dbg("Going for release!\n");
+
+ usb_set_intfdata(intf, NULL);
+
+ mutex_lock(&dev->vb_queue_lock);
+ mutex_lock(&dev->v4l2_lock);
+
+ smi2021_uninit_isoc(dev);
+ smi2021_clear_queue(dev);
+
+ video_unregister_device(&dev->vdev);
+ v4l2_device_disconnect(&dev->v4l2_dev);
+
+ /* This way current users can detect device is gone */
+ dev->udev = NULL;
+
+ mutex_unlock(&dev->v4l2_lock);
+ mutex_unlock(&dev->vb_queue_lock);
+
+ smi2021_snd_unregister(dev);
+
+ /*
+ * This calls release_v4l2_dev if it's the last reference.
+ * Otherwise, the release is postponed until there are no users left.
+ */
+ v4l2_device_put(&dev->v4l2_dev);
+}
+
+/******************************************************************************/
+/* */
+/* MODULE - INIT & EXIT */
+/* */
+/******************************************************************************/
+
+struct usb_driver smi2021_usb_driver = {
+ .name = "smi2021",
+ .id_table = smi2021_usb_device_id_table,
+ .probe = smi2021_usb_probe,
+ .disconnect = smi2021_usb_disconnect
+};
+
+module_usb_driver(smi2021_usb_driver);
+
--
1.8.1.1

2013-03-14 14:11:11

by Jon Arne Jørgensen

[permalink] [raw]
Subject: [RFC V1 7/8] smi2021: Add smi2021_bl.c

This is the smi2021-bootloader module.
This module will upload the firmware for the different somagic devices.

Signed-off-by: Jon Arne Jørgensen <[email protected]>
---
drivers/media/usb/smi2021/smi2021_bl.c | 254 +++++++++++++++++++++++++++++++++
1 file changed, 254 insertions(+)
create mode 100644 drivers/media/usb/smi2021/smi2021_bl.c

diff --git a/drivers/media/usb/smi2021/smi2021_bl.c b/drivers/media/usb/smi2021/smi2021_bl.c
new file mode 100644
index 0000000..025b06c
--- /dev/null
+++ b/drivers/media/usb/smi2021/smi2021_bl.c
@@ -0,0 +1,254 @@
+/*******************************************************************************
+ * smi2021_bl.c *
+ * *
+ * USB Driver for SMI2021 - EasyCAP *
+ * USB ID 1c88:003c *
+ * *
+ * *****************************************************************************
+ *
+ * Copyright 2011-2013 Jon Arne Jørgensen
+ * <jonjon.arnearne--a.t--gmail.com>
+ *
+ * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
+ *
+ * This file is part of SMI2021
+ * http://code.google.com/p/easycap-somagic-linux/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This driver is heavily influensed by the STK1160 driver.
+ * Copyright (C) 2012 Ezequiel Garcia
+ * <elezegarcia--a.t--gmail.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+
+#define FIRMWARE_CHUNK_SIZE 62
+#define FIRMWARE_HEADER_SIZE 2
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jon Arne Jørgensen <jonjon.arnearne--a.t--gmail.com>");
+MODULE_DESCRIPTION("SMI2021 - Bootloader");
+MODULE_VERSION("0.1");
+
+static unsigned int firmware_version;
+module_param(firmware_version, int, 0644);
+MODULE_PARM_DESC(firmware_version,
+ "Firmware version to be uploaded to device\n"
+ "if there are more than one firmware present");
+
+struct usb_device_id smi2021_bootloader_id_table[] = {
+ { USB_DEVICE(0x1c88, 0x0007) },
+ { }
+};
+
+struct smi2021_firmware {
+ int id;
+ const char *name;
+ int found;
+};
+
+struct smi2021_firmware available_fw[] = {
+ {
+ .id = 0x3c,
+ .name = "smi2021_3c.bin",
+ },
+ {
+ .id = 0x3e,
+ .name = "smi2021_3e.bin",
+ },
+ {
+ .id = 0x3f,
+ .name = "smi2021_3f.bin",
+ }
+};
+
+static const struct firmware *firmware[ARRAY_SIZE(available_fw)];
+static int firmwares = -1;
+
+static int smi2021_load_firmware(struct usb_device *udev,
+ const struct firmware *firmware)
+{
+ int i, size, rc = 0;
+ u8 *chunk;
+ u16 ack = 0x0000;
+
+ if (udev == NULL)
+ goto end_out;
+
+ size = FIRMWARE_CHUNK_SIZE + FIRMWARE_HEADER_SIZE;
+ chunk = kzalloc(size, GFP_KERNEL);
+ chunk[0] = 0x05;
+ chunk[1] = 0xff;
+
+ if (chunk == NULL) {
+ dev_err(&udev->dev,
+ "could not allocate space for firmware chunk\n");
+ goto end_out;
+ }
+
+ if (firmware == NULL) {
+ dev_err(&udev->dev, "firmware is NULL\n");
+ rc = -ENODEV;
+ goto free_out;
+ }
+
+ if (firmware->size % FIRMWARE_CHUNK_SIZE) {
+ dev_err(&udev->dev, "firmware has wrong size\n");
+ rc = -ENODEV;
+ goto free_out;
+ }
+
+ rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0x80), 0x01,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0001, 0x0000, &ack, sizeof(ack), 1000);
+
+ if (rc < 0) {
+ dev_err(&udev->dev, "could not prepare device for upload: %d\n",
+ rc);
+ goto free_out;
+ }
+ if (__cpu_to_be16(ack) != 0x0107) {
+ dev_err(&udev->dev, "could not prepare device for upload: %d\n",
+ rc);
+ goto free_out;
+ }
+
+ for (i = 0; i < firmware->size / FIRMWARE_CHUNK_SIZE; i++) {
+ memcpy(chunk + FIRMWARE_HEADER_SIZE,
+ firmware->data + (i * FIRMWARE_CHUNK_SIZE),
+ FIRMWARE_CHUNK_SIZE);
+
+ rc = usb_control_msg(udev, usb_sndctrlpipe(udev, 0x00), 0x01,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0005, 0x0000, chunk, size, 1000);
+ if (rc < 0) {
+ dev_err(&udev->dev, "firmware upload failed: %d\n",
+ rc);
+ goto free_out;
+ }
+ }
+
+ ack = __cpu_to_le16(0x0007);
+ rc = usb_control_msg(udev, usb_sndctrlpipe(udev, 0x00), 0x01,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0007, 0x0000, &ack, sizeof(ack), 1000);
+
+ if (rc < 0) {
+ dev_err(&udev->dev, "device failed to ack firmware: %d\n", rc);
+ goto free_out;
+ }
+
+ rc = 0;
+
+free_out:
+ kfree(chunk);
+end_out:
+ return rc;
+}
+
+static int smi2021_choose_firmware(struct usb_device *udev)
+{
+ int i, found, id;
+ for (i = 0; i < ARRAY_SIZE(available_fw); i++) {
+ found = available_fw[i].found;
+ id = available_fw[i].id;
+ if (firmware_version == id && found >= 0) {
+ dev_info(&udev->dev, "uploading firmware for 0x%x\n",
+ id);
+ return smi2021_load_firmware(udev, firmware[found]);
+ }
+ }
+
+ dev_info(&udev->dev,
+ "could not decide what firmware to upload, user action required\n");
+ return 0;
+}
+
+static int smi2021_bootloader_probe(struct usb_interface *intf,
+ const struct usb_device_id *devid)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ int rc, i;
+
+ /* Check what firmwares are available in the system */
+ for (i = 0; i < ARRAY_SIZE(available_fw); i++) {
+ dev_info(&udev->dev, "Looking for: %s\n", available_fw[i].name);
+ rc = request_firmware(&firmware[firmwares + 1],
+ available_fw[i].name, &udev->dev);
+
+ if (rc == 0) {
+ firmwares++;
+ available_fw[i].found = firmwares;
+ dev_info(&udev->dev, "Found firmware for 0x00%x\n",
+ available_fw[i].id);
+ } else if (rc == -ENOENT) {
+ available_fw[i].found = -1;
+ } else {
+ dev_err(&udev->dev,
+ "request_firmware failed with: %d\n", rc);
+ goto err_out;
+ }
+ }
+
+ if (firmwares < 0) {
+ dev_err(&udev->dev,
+ "could not find any firmware for this device\n");
+ goto no_dev;
+ } else if (firmwares == 0) {
+ rc = smi2021_load_firmware(udev, firmware[0]);
+ if (rc < 0)
+ goto err_out;
+ } else {
+ smi2021_choose_firmware(udev);
+ }
+
+ return 0;
+
+no_dev:
+ rc = -ENODEV;
+err_out:
+ return rc;
+}
+
+static void smi2021_bootloader_disconnect(struct usb_interface *intf)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(available_fw); i++) {
+ if (available_fw[i].found >= 0) {
+ dev_info(&udev->dev, "Releasing firmware for 0x00%x\n",
+ available_fw[i].id);
+ release_firmware(firmware[available_fw[i].found]);
+ firmware[available_fw[i].found] = NULL;
+ available_fw[i].found = -1;
+ }
+ }
+ firmwares = -1;
+
+}
+
+struct usb_driver smi2021_bootloader_driver = {
+ .name = "smi2021_bootloader",
+ .id_table = smi2021_bootloader_id_table,
+ .probe = smi2021_bootloader_probe,
+ .disconnect = smi2021_bootloader_disconnect
+};
+
+module_usb_driver(smi2021_bootloader_driver);
--
1.8.1.1

2013-03-14 14:12:04

by Jon Arne Jørgensen

[permalink] [raw]
Subject: [RFC V1 5/8] smi2021: Add smi2021_video.c

This file is responsible for all communication with the video hardware
and also starting and stopping the capture.

It also contains the setup and handling of the usb ISOCHRONOUS transfers.

Signed-off-by: Jon Arne Jørgensen <[email protected]>
---
drivers/media/usb/smi2021/smi2021_video.c | 543 ++++++++++++++++++++++++++++++
1 file changed, 543 insertions(+)
create mode 100644 drivers/media/usb/smi2021/smi2021_video.c

diff --git a/drivers/media/usb/smi2021/smi2021_video.c b/drivers/media/usb/smi2021/smi2021_video.c
new file mode 100644
index 0000000..48f3e80
--- /dev/null
+++ b/drivers/media/usb/smi2021/smi2021_video.c
@@ -0,0 +1,543 @@
+/*******************************************************************************
+ * smi2021_video.c *
+ * *
+ * USB Driver for SMI2021 - EasyCAP *
+ * USB ID 1c88:003c *
+ * *
+ * *****************************************************************************
+ *
+ * Copyright 2011-2013 Jon Arne Jørgensen
+ * <jonjon.arnearne--a.t--gmail.com>
+ *
+ * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
+ *
+ * This file is part of SMI2021x
+ * http://code.google.com/p/easycap-somagic-linux/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This driver is heavily influensed by the STK1160 driver.
+ * Copyright (C) 2012 Ezequiel Garcia
+ * <elezegarcia--a.t--gmail.com>
+ *
+ */
+
+#include "smi2021.h"
+
+static void print_usb_err(struct smi2021_dev *dev, int packet, int status)
+{
+ char *errmsg;
+
+ switch (status) {
+ case -ENOENT:
+ errmsg = "unlinked synchronuously";
+ break;
+ case -ECONNRESET:
+ errmsg = "unlinked asynchronuously";
+ break;
+ case -ENOSR:
+ errmsg = "Buffer error (overrun)";
+ break;
+ case -EPIPE:
+ errmsg = "Stalled (device not responding)";
+ break;
+ case -EOVERFLOW:
+ errmsg = "Babble (bad cable?)";
+ break;
+ case -EPROTO:
+ errmsg = "Bit-stuff error (bad cable?)";
+ break;
+ case -EILSEQ:
+ errmsg = "CRC/Timeout (could be anything)";
+ break;
+ case -ETIME:
+ errmsg = "Device does not respond";
+ break;
+ default:
+ errmsg = "Unknown";
+ }
+
+ if (packet < 0) {
+ printk_ratelimited(KERN_WARNING "Urb status %d [%s]\n",
+ status, errmsg);
+ } else {
+ printk_ratelimited(KERN_INFO "URB packet %d, status %d [%s]\n",
+ packet, status, errmsg);
+ }
+}
+
+static struct smi2021_buffer *smi2021_next_buffer(struct smi2021_dev *dev)
+{
+ struct smi2021_buffer *buf = NULL;
+ unsigned long flags = 0;
+
+ BUG_ON(dev->isoc_ctl.buf);
+
+ spin_lock_irqsave(&dev->buf_lock, flags);
+ if (!list_empty(&dev->avail_bufs)) {
+ buf = list_first_entry(&dev->avail_bufs, struct smi2021_buffer,
+ list);
+ list_del(&buf->list);
+ }
+ spin_unlock_irqrestore(&dev->buf_lock, flags);
+
+ return buf;
+}
+
+static void smi2021_buffer_done(struct smi2021_dev *dev)
+{
+ struct smi2021_buffer *buf = dev->isoc_ctl.buf;
+
+ dev->buf_count++;
+
+ buf->vb.v4l2_buf.sequence = dev->buf_count >> 1;
+ buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
+ buf->vb.v4l2_buf.bytesused = buf->pos;
+ do_gettimeofday(&buf->vb.v4l2_buf.timestamp);
+
+ vb2_set_plane_payload(&buf->vb, 0, buf->pos);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
+
+ dev->isoc_ctl.buf = NULL;
+}
+
+static void copy_video(struct smi2021_dev *dev, u8 p)
+{
+ struct smi2021_buffer *buf = dev->isoc_ctl.buf;
+
+ int lines_per_field = dev->height / 2;
+ int line = 0;
+ int pos_in_line = 0;
+ unsigned int offset = 0;
+ u8 *dst;
+
+ if (buf == NULL)
+ return;
+
+ if (buf->in_blank)
+ return;
+
+ if (buf->pos >= buf->length) {
+ if (buf->second_field == 0) {
+ /* We are probably trying to capture from
+ * a unconnected input
+ */
+ smi2021_buffer_done(dev);
+ } else {
+ printk_ratelimited(KERN_WARNING
+ "Buffer overflow!, max: %d bytes, av_lines_found: %d, second_field: %d\n",
+ buf->length, buf->trc_av,
+ buf->second_field);
+ }
+ return;
+ }
+
+ pos_in_line = buf->pos % SMI2021_BYTES_PER_LINE;
+ line = buf->pos / SMI2021_BYTES_PER_LINE;
+ if (line >= lines_per_field)
+ line -= lines_per_field;
+
+ if (line != buf->trc_av - 1) {
+ /* Keep video synchronized.
+ * The device will sometimes give us to many bytes
+ * for a line, before we get a new TRC.
+ * We just drop these bytes */
+ return;
+ }
+
+ if (buf->second_field)
+ offset += SMI2021_BYTES_PER_LINE;
+
+ offset += (SMI2021_BYTES_PER_LINE * line * 2) + pos_in_line;
+
+ /* Will this ever happen? */
+ if (offset >= buf->length) {
+ printk_ratelimited(KERN_INFO
+ "Offset calculation error, field: %d, line: %d, pos_in_line: %d\n",
+ buf->second_field, line, pos_in_line);
+ return;
+ }
+
+ dst = buf->mem + offset;
+ *dst = p;
+ buf->pos++;
+}
+
+#define is_sav(trc) \
+ ((trc & SMI2021_TRC_EAV) == 0x00)
+#define is_field2(trc) \
+ ((trc & SMI2021_TRC_FIELD_2) == SMI2021_TRC_FIELD_2)
+#define is_active_video(trc) \
+ ((trc & SMI2021_TRC_VBI) == 0x00)
+/*
+ * Parse the TRC.
+ * Grab a new buffer from the queue if don't have one
+ * and we are recieving the start of a video frame.
+ *
+ * Mark video buffers as done if we have one full frame.
+ */
+static void parse_trc(struct smi2021_dev *dev, u8 trc)
+{
+ struct smi2021_buffer *buf = dev->isoc_ctl.buf;
+ int lines_per_field = dev->height / 2;
+ int line = 0;
+
+ if (buf == NULL) {
+ if (!is_sav(trc))
+ return;
+
+ if (!is_active_video(trc))
+ return;
+
+ if (is_field2(trc))
+ return;
+
+ buf = smi2021_next_buffer(dev);
+ if (buf == NULL)
+ return;
+
+ dev->isoc_ctl.buf = buf;
+ }
+
+ if (is_sav(trc)) {
+ /* Start of VBI or ACTIVE VIDEO */
+ if (is_active_video(trc)) {
+ buf->in_blank = false;
+ buf->trc_av++;
+ } else {
+ /* VBI */
+ buf->in_blank = true;
+ }
+
+ if (!buf->second_field && is_field2(trc)) {
+ line = buf->pos / SMI2021_BYTES_PER_LINE;
+ if (line < lines_per_field)
+ goto buf_done;
+
+ buf->second_field = true;
+ buf->trc_av = 0;
+ }
+
+ if (buf->second_field && !is_field2(trc))
+ goto buf_done;
+ } else {
+ /* End of VBI or ACTIVE VIDEO */
+ buf->in_blank = true;
+ }
+
+ return;
+
+buf_done:
+ smi2021_buffer_done(dev);
+}
+
+/*
+ * Scan the saa7113 Active video data.
+ * This data is:
+ * 4 bytes header (0xff 0x00 0x00 [TRC/SAV])
+ * 1440 bytes of UYUV Video data
+ * 4 bytes footer (0xff 0x00 0x00 [TRC/EAV])
+ *
+ * TRC = Time Reference Code.
+ * SAV = Start Active Video.
+ * EAV = End Active Video.
+ * This is described in the saa7113 datasheet.
+ */
+static void parse_video(struct smi2021_dev *dev, u8 *p, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ switch (dev->sync_state) {
+ case HSYNC:
+ if (p[i] == 0xff)
+ dev->sync_state = SYNCZ1;
+ else
+ copy_video(dev, p[i]);
+ break;
+ case SYNCZ1:
+ if (p[i] == 0x00) {
+ dev->sync_state = SYNCZ2;
+ } else {
+ dev->sync_state = HSYNC;
+ copy_video(dev, 0xff);
+ copy_video(dev, p[i]);
+ }
+ break;
+ case SYNCZ2:
+ if (p[i] == 0x00) {
+ dev->sync_state = TRC;
+ } else {
+ dev->sync_state = HSYNC;
+ copy_video(dev, 0xff);
+ copy_video(dev, 0x00);
+ copy_video(dev, p[i]);
+ }
+ break;
+ case TRC:
+ dev->sync_state = HSYNC;
+ parse_trc(dev, p[i]);
+ break;
+ }
+ }
+
+}
+/*
+ *
+ * The device delivers data in chunks of 0x400 bytes.
+ * The four first bytes is a magic header to identify the chunks.
+ * 0xaa 0xaa 0x00 0x00 = saa7113 Active Video Data
+ * 0xaa 0xaa 0x00 0x01 = PCM - 24Bit 2 Channel audio data
+ */
+static void process_packet(struct smi2021_dev *dev, u8 *p, int len)
+{
+ int i;
+ u32 *header;
+
+ if (len % 0x400 != 0) {
+ printk_ratelimited(KERN_INFO "smi2021::%s: len: %d\n",
+ __func__, len);
+ return;
+ }
+
+ for (i = 0; i < len; i += 0x400) {
+ header = (u32 *)(p + i);
+ switch (__cpu_to_be32(*header)) {
+ case 0xaaaa0000: {
+ parse_video(dev, p+i+4, 0x400-4);
+ break;
+ }
+ case 0xaaaa0001: {
+ smi2021_audio(dev, p+i+4, 0x400-4);
+ break;
+ }
+ default: {
+ /* Nothing */
+ }
+ }
+ }
+}
+
+/*
+ * Interrupt called by URB callback
+ */
+static void smi2021_isoc_isr(struct urb *urb)
+{
+ int i, rc, status, len;
+ struct smi2021_dev *dev = urb->context;
+ u8 *p;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ECONNRESET: /* kill */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* uvc driver frees the queue here */
+ return;
+ default:
+ smi2021_err("urb error! status %d\n", urb->status);
+ return;
+ }
+
+ if (urb->status < 0)
+ print_usb_err(dev, -1, status);
+
+ if (dev == NULL) {
+ smi2021_warn("called with null device\n");
+ return;
+ }
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+
+ status = urb->iso_frame_desc[i].status;
+ if (status == -18) {
+ /* This seems to happen when the device
+ * trying to stream from an unconnected input
+ * */
+ continue;
+ }
+
+ if (status < 0) {
+ print_usb_err(dev, i, status);
+ continue;
+ }
+
+ p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ len = urb->iso_frame_desc[i].actual_length;
+ process_packet(dev, p, len);
+ }
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length = 0;
+ }
+
+ rc = usb_submit_urb(urb, GFP_ATOMIC);
+ if (rc)
+ smi2021_err("urb re-submit failed (%d)\n", rc);
+}
+
+/*
+ * Cancel urbs
+ * This function can not be called in atomic context
+ */
+void smi2021_cancel_isoc(struct smi2021_dev *dev)
+{
+ int i, num_bufs = dev->isoc_ctl.num_bufs;
+ if (!num_bufs)
+ return;
+
+ smi2021_dbg("killing %d urbs...\n", num_bufs);
+
+ for (i = 0; i < num_bufs; i++)
+ usb_kill_urb(dev->isoc_ctl.urb[i]);
+
+ smi2021_dbg("all urbs killed\n");
+
+}
+
+/*
+ * Releases urb and transfer buffers
+ * Obviously, associated urb must be killed before releasing it
+ */
+void smi2021_free_isoc(struct smi2021_dev *dev)
+{
+ struct urb *urb;
+ int i, num_bufs = dev->isoc_ctl.num_bufs;
+
+ smi2021_dbg("freeing %d urb buffers...\n", num_bufs);
+
+ for (i = 0; i < num_bufs; i++) {
+ urb = dev->isoc_ctl.urb[i];
+ if (urb) {
+ if (dev->isoc_ctl.transfer_buffer[i]) {
+#ifndef CONFIG_DMA_NONCOHERENT
+ usb_free_coherent(dev->udev,
+ urb->transfer_buffer_length,
+ dev->isoc_ctl.transfer_buffer[i],
+ urb->transfer_dma);
+#else
+ kfree(dev->isoc_ctl.transfer_buffer[i]);
+#endif
+ }
+ 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;
+
+ smi2021_dbg("all urb buffers freed\n");
+}
+
+/*
+ * Helper for canceling and freeing urbs
+ * This function can not be called in atomic context
+ */
+void smi2021_uninit_isoc(struct smi2021_dev *dev)
+{
+ smi2021_cancel_isoc(dev);
+ smi2021_free_isoc(dev);
+}
+
+
+int smi2021_alloc_isoc(struct smi2021_dev *dev)
+{
+ struct urb *urb;
+ int i, j, k, sb_size, max_packets, num_bufs;
+
+ if (dev->isoc_ctl.num_bufs)
+ smi2021_uninit_isoc(dev);
+
+ num_bufs = SMI2021_ISOC_BUFS;
+ max_packets = SMI2021_ISOC_PACKETS;
+ sb_size = max_packets * SMI2021_MAX_PKT_SIZE;
+
+ dev->isoc_ctl.buf = NULL;
+ dev->isoc_ctl.max_pkt_size = SMI2021_MAX_PKT_SIZE;
+ dev->isoc_ctl.urb = kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
+ if (!dev->isoc_ctl.urb) {
+ smi2021_err("out of memory for urb array\n");
+ return -ENOMEM;
+ }
+
+ dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *) * num_bufs,
+ GFP_KERNEL);
+ if (!dev->isoc_ctl.transfer_buffer) {
+ smi2021_err("out of memory for usb transfer\n");
+ kfree(dev->isoc_ctl.urb);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < num_bufs; i++) {
+ urb = usb_alloc_urb(max_packets, GFP_KERNEL);
+ if (!urb) {
+ smi2021_err("connot allocate urb[%d]\n", i);
+ goto free_i_bufs;
+ }
+ dev->isoc_ctl.urb[i] = urb;
+#ifndef CONFIG_DMA_NONCOHERENT
+ dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(
+ dev->udev, sb_size, GFP_KERNEL,
+ &urb->transfer_dma);
+#else
+ dev->isoc_ctl.transfer_buffer[i] = kmalloc(sb_size,
+ GFP_KERNEL);
+#endif
+ if (!dev->isoc_ctl.transfer_buffer[i]) {
+ smi2021_err("cannot alloc %d bytes for tx[%d] buffer",
+ sb_size, i);
+ goto free_i_bufs;
+ }
+ /* Do not leak kernel data */
+ memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
+
+ urb->dev = dev->udev;
+ urb->pipe = usb_rcvisocpipe(dev->udev, SMI2021_ISOC_EP);
+ urb->transfer_buffer = dev->isoc_ctl.transfer_buffer[i];
+ urb->transfer_buffer_length = sb_size;
+ urb->complete = smi2021_isoc_isr;
+ urb->context = dev;
+ urb->interval = 1;
+ urb->start_frame = 0;
+ urb->number_of_packets = max_packets;
+#ifndef CONFIG_DMA_NONCOHERENT
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+#else
+ urb->transfer_flags = URB_ISO_ASAP;
+#endif
+ 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;
+ }
+ }
+ smi2021_dbg("urbs allocated\n");
+ dev->isoc_ctl.num_bufs = num_bufs;
+ return 0;
+
+free_i_bufs:
+ dev->isoc_ctl.num_bufs = i+1;
+ smi2021_free_isoc(dev);
+ return -ENOMEM;
+}
--
1.8.1.1

2013-03-14 14:12:02

by Jon Arne Jørgensen

[permalink] [raw]
Subject: [RFC V1 6/8] smi2021: Add smi2021_audio.c

This file handles all setup of a new snd_card instance with one pcm_substream.
It's also responsible for parsing the raw pcm data received in the isoc transfers,
and passing it to the alsa buffers.

Signed-off-by: Jon Arne Jørgensen <[email protected]>
---
drivers/media/usb/smi2021/smi2021_audio.c | 385 ++++++++++++++++++++++++++++++
1 file changed, 385 insertions(+)
create mode 100644 drivers/media/usb/smi2021/smi2021_audio.c

diff --git a/drivers/media/usb/smi2021/smi2021_audio.c b/drivers/media/usb/smi2021/smi2021_audio.c
new file mode 100644
index 0000000..cd20181
--- /dev/null
+++ b/drivers/media/usb/smi2021/smi2021_audio.c
@@ -0,0 +1,385 @@
+/*******************************************************************************
+ * smi2021_audio.c *
+ * *
+ * USB Driver for SMI2021 - EasyCap *
+ * USB ID 1c88:003c *
+ * *
+ * *****************************************************************************
+ *
+ * Copyright 2011-2013 Jon Arne Jørgensen
+ * <jonjon.arnearne--a.t--gmail.com>
+ *
+ * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
+ *
+ * This file is part of SMI2021
+ * http://code.google.com/p/easycap-somagic-linux/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This driver is heavily influensed by the STK1160 driver.
+ * Copyright (C) 2012 Ezequiel Garcia
+ * <elezegarcia--a.t--gmail.com>
+ *
+ */
+
+#include "smi2021.h"
+
+static void pcm_buffer_free(struct snd_pcm_substream *substream)
+{
+ vfree(substream->runtime->dma_area);
+ substream->runtime->dma_area = NULL;
+ substream->runtime->dma_bytes = 0;
+}
+
+static int pcm_buffer_alloc(struct snd_pcm_substream *substream, int size)
+{
+ if (substream->runtime->dma_area) {
+ if (substream->runtime->dma_bytes > size)
+ return 0;
+ pcm_buffer_free(substream);
+ }
+
+ substream->runtime->dma_area = vmalloc(size);
+ if (substream->runtime->dma_area == NULL)
+ return -ENOMEM;
+
+ substream->runtime->dma_bytes = size;
+
+ return 0;
+}
+
+static const struct snd_pcm_hardware smi2021_pcm_hw = {
+ .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH,
+
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .period_bytes_min = 992, /* 32640 */ /* 15296 */
+ .period_bytes_max = 15872, /* 65280 */
+ .periods_min = 1, /* 1 */
+ .periods_max = 16, /* 2 */
+ .buffer_bytes_max = 65280, /* 65280 */
+};
+
+static int smi2021_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct smi2021_dev *dev = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int rc;
+
+ rc = snd_pcm_hw_constraint_pow2(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (rc < 0)
+ return rc;
+
+ dev->pcm_substream = substream;
+
+ runtime->hw = smi2021_pcm_hw;
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+ smi2021_dbg("PCM device open!\n");
+
+ return 0;
+}
+
+static int smi2021_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct smi2021_dev *dev = snd_pcm_substream_chip(substream);
+ smi2021_dbg("PCM device closing\n");
+
+ if (atomic_read(&dev->adev_capturing)) {
+ atomic_set(&dev->adev_capturing, 0);
+ schedule_work(&dev->adev_capture_trigger);
+ }
+ return 0;
+
+}
+
+
+static int smi2021_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ int size, rc;
+ size = params_period_bytes(hw_params) * params_periods(hw_params);
+
+ rc = pcm_buffer_alloc(substream, size);
+ if (rc < 0)
+ return rc;
+
+
+ return 0;
+}
+
+static int smi2021_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct smi2021_dev *dev = snd_pcm_substream_chip(substream);
+
+ if (atomic_read(&dev->adev_capturing)) {
+ atomic_set(&dev->adev_capturing, 0);
+ schedule_work(&dev->adev_capture_trigger);
+ }
+
+ pcm_buffer_free(substream);
+ return 0;
+}
+
+static int smi2021_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct smi2021_dev *dev = snd_pcm_substream_chip(substream);
+
+ dev->pcm_complete_samples = 0;
+ dev->pcm_read_offset = 0;
+ dev->pcm_write_ptr = 0;
+
+ return 0;
+}
+
+static void capture_trigger(struct work_struct *work)
+{
+ struct smi2021_dev *dev = container_of(work, struct smi2021_dev,
+ adev_capture_trigger);
+
+ if (atomic_read(&dev->adev_capturing))
+ smi2021_write_reg(dev, 0, 0x1740, 0x1d);
+ else
+ smi2021_write_reg(dev, 0, 0x1740, 0x00);
+}
+
+/* This callback is ATOMIC, must not sleep */
+static int smi2021_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct smi2021_dev *dev = snd_pcm_substream_chip(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_START:
+ atomic_set(&dev->adev_capturing, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ atomic_set(&dev->adev_capturing, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ schedule_work(&dev->adev_capture_trigger);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t smi2021_pcm_pointer(
+ struct snd_pcm_substream *substream)
+{
+ struct smi2021_dev *dev = snd_pcm_substream_chip(substream);
+ return dev->pcm_write_ptr / 8;
+}
+
+static struct page *smi2021_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
+ unsigned long offset)
+{
+ void *pageptr = subs->runtime->dma_area + offset;
+
+ return vmalloc_to_page(pageptr);
+}
+
+static struct snd_pcm_ops smi2021_pcm_ops = {
+ .open = smi2021_pcm_open,
+ .close = smi2021_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = smi2021_pcm_hw_params,
+ .hw_free = smi2021_pcm_hw_free,
+ .prepare = smi2021_pcm_prepare,
+ .trigger = smi2021_pcm_trigger,
+ .pointer = smi2021_pcm_pointer,
+ .page = smi2021_pcm_get_vmalloc_page,
+};
+
+int smi2021_snd_register(struct smi2021_dev *dev)
+{
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ int rc = 0;
+
+ rc = snd_card_create(SNDRV_DEFAULT_IDX1, "smi2021 Audio", THIS_MODULE,
+ 0, &card);
+ if (rc < 0)
+ return rc;
+
+ rc = snd_pcm_new(card, "smi2021 Audio", 0, 0, 1, &pcm);
+ if (rc < 0)
+ goto err_free_card;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &smi2021_pcm_ops);
+ pcm->info_flags = 0;
+ pcm->private_data = dev;
+ strcpy(pcm->name, "Somagic smi2021 Capture");
+
+ strcpy(card->driver, "smi2021-Audio");
+ strcpy(card->shortname, "smi2021 Audio");
+ strcpy(card->longname, "Somagic smi2021 Audio");
+
+ INIT_WORK(&dev->adev_capture_trigger, capture_trigger);
+
+ rc = snd_card_register(card);
+ if (rc < 0)
+ goto err_free_card;
+
+ dev->snd_card = card;
+
+ return 0;
+
+err_free_card:
+ snd_card_free(card);
+ return rc;
+}
+
+void smi2021_snd_unregister(struct smi2021_dev *dev)
+{
+ if (!dev)
+ return;
+
+ if (!dev->snd_card)
+ return;
+
+ snd_card_free(dev->snd_card);
+ dev->snd_card = NULL;
+}
+
+void smi2021_audio(struct smi2021_dev *dev, u8 *data, int len)
+{
+ struct snd_pcm_runtime *runtime;
+ u8 offset;
+ int new_offset = 0;
+
+ int skip;
+ unsigned int stride, oldptr, headptr;
+
+ int diff = 0;
+ int samples = 0;
+ bool period_elapsed = false;
+
+
+ if (!dev->udev)
+ return;
+
+ if (atomic_read(&dev->adev_capturing) == 0)
+ return;
+
+ if (!dev->pcm_substream)
+ return;
+
+ runtime = dev->pcm_substream->runtime;
+ if (!runtime || !runtime->dma_area)
+ return;
+
+ offset = dev->pcm_read_offset;
+ stride = runtime->frame_bits >> 3;
+
+ if (stride == 0)
+ return;
+
+ diff = dev->pcm_write_ptr;
+
+ /* Check that the end of the last buffer was correct.
+ * If not correct, we mark any partial frames in buffer as complete
+ */
+ headptr = dev->pcm_write_ptr - offset - 4;
+ if (dev->pcm_write_ptr > 10 && runtime->dma_area[headptr] != 0x00) {
+ skip = stride - (dev->pcm_write_ptr % stride);
+ snd_pcm_stream_lock(dev->pcm_substream);
+ dev->pcm_write_ptr += skip;
+
+ if (dev->pcm_write_ptr >= runtime->dma_bytes)
+ dev->pcm_write_ptr -= runtime->dma_bytes;
+
+ snd_pcm_stream_unlock(dev->pcm_substream);
+ offset = dev->pcm_read_offset = 0;
+ }
+ /* The device is actually sending 24Bit pcm data
+ * with 0x00 as the header byte before each sample.
+ * We look for this byte to make sure we did not
+ * loose any bytes during transfer.
+ */
+ while (len > stride && (data[offset] != 0x00 ||
+ data[offset + (stride / 2)] != 0x00)) {
+ new_offset++;
+ data++;
+ len--;
+ }
+
+ if (len <= stride) {
+ /* We exhausted the buffer looking for 0x00 */
+ dev->pcm_read_offset = 0;
+ return;
+ }
+ if (new_offset != 0) {
+ /* This buffer can not be appended to the current buffer,
+ * so we mark any partial frames in the buffer as complete.
+ */
+ skip = stride - (dev->pcm_write_ptr % stride);
+ snd_pcm_stream_lock(dev->pcm_substream);
+ dev->pcm_write_ptr += skip;
+
+ if (dev->pcm_write_ptr >= runtime->dma_bytes)
+ dev->pcm_write_ptr -= runtime->dma_bytes;
+
+ snd_pcm_stream_unlock(dev->pcm_substream);
+
+ offset = dev->pcm_read_offset = new_offset % (stride / 2);
+
+ }
+
+ oldptr = dev->pcm_write_ptr;
+ if (oldptr + len >= runtime->dma_bytes) {
+ unsigned int cnt = runtime->dma_bytes - oldptr;
+ memcpy(runtime->dma_area + oldptr, data, cnt);
+ memcpy(runtime->dma_area, data + cnt, len - cnt);
+ } else {
+ memcpy(runtime->dma_area + oldptr, data, len);
+ }
+
+ snd_pcm_stream_lock(dev->pcm_substream);
+ dev->pcm_write_ptr += len;
+
+ if (dev->pcm_write_ptr >= runtime->dma_bytes)
+ dev->pcm_write_ptr -= runtime->dma_bytes;
+
+ samples = dev->pcm_write_ptr - diff;
+ if (samples < 0)
+ samples += runtime->dma_bytes;
+
+ samples /= (stride / 2);
+
+ dev->pcm_complete_samples += samples;
+ if (dev->pcm_complete_samples / 2 >= runtime->period_size) {
+ dev->pcm_complete_samples -= runtime->period_size * 2;
+ period_elapsed = true;
+ }
+ snd_pcm_stream_unlock(dev->pcm_substream);
+
+ if (period_elapsed)
+ snd_pcm_period_elapsed(dev->pcm_substream);
+
+}
--
1.8.1.1

2013-03-14 14:12:52

by Jon Arne Jørgensen

[permalink] [raw]
Subject: [RFC V1 1/8] smi2021: Add the header file

This is the header file for the smi2021 module.

Signed-off-by: Jon Arne Jørgensen <[email protected]>
---
drivers/media/usb/smi2021/smi2021.h | 228 ++++++++++++++++++++++++++++++++++++
1 file changed, 228 insertions(+)
create mode 100644 drivers/media/usb/smi2021/smi2021.h

diff --git a/drivers/media/usb/smi2021/smi2021.h b/drivers/media/usb/smi2021/smi2021.h
new file mode 100644
index 0000000..eba0fef
--- /dev/null
+++ b/drivers/media/usb/smi2021/smi2021.h
@@ -0,0 +1,228 @@
+/*******************************************************************************
+ * smi2021.h *
+ * *
+ * USB Driver for SMI2021 - EasyCap *
+ * USB ID 1c88:003c *
+ * *
+ * *****************************************************************************
+ *
+ * Copyright 2011-2013 Jon Arne Jørgensen
+ * <jonjon.arnearne--a.t--gmail.com>
+ *
+ * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
+ *
+ * This file is part of SMI2021
+ * http://code.google.com/p/easycap-somagic-linux/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This driver is heavily influensed by the STK1160 driver.
+ * Copyright (C) 2012 Ezequiel Garcia
+ * <elezegarcia--a.t--gmail.com>
+ *
+ */
+
+#ifndef SMI2021_H
+#define SMI2021_H
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/types.h>
+#include <linux/spinlock_types.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+
+#include <media/v4l2-event.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/saa7115.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+
+#define SMI2021_DRIVER_VERSION "0.1"
+
+/* For ISOC */
+#define SMI2021_MAX_PKT_SIZE 3072
+#define SMI2021_ISOC_PACKETS 10 /* 64 */
+#define SMI2021_ISOC_BUFS 4 /* 16 */
+#define SMI2021_ISOC_EP 0x82
+
+/* The structure of the array we use to send i2c settings to the device */
+#define SMI2021_CTRL_HEAD 0x00
+#define SMI2021_CTRL_ADDR 0x01
+#define SMI2021_CTRL_BM_DATA_TYPE 0x02
+#define SMI2021_CTRL_BM_DATA_OFFSET 0x03
+#define SMI2021_CTRL_DATA_SIZE 0x04
+
+#define SMI2021_CTRL_REG_HI 0x05
+#define SMI2021_CTRL_REG_LO 0x06
+#define SMI2021_CTRL_REG_VAL 0x07
+
+#define SMI2021_CTRL_I2C_REG 0x05
+#define SMI2021_CTRL_I2C_VAL 0x06
+#define SMI2021_CTRL_I2C_RCV_VAL 0x05
+
+/* General video constants */
+#define SMI2021_BYTES_PER_LINE 1440
+#define SMI2021_PAL_LINES 576
+#define SMI2021_NTSC_LINES 486
+
+/* Timing Referance Codes, see saa7113 datasheet */
+#define SMI2021_TRC_EAV 0x10
+#define SMI2021_TRC_VBI 0x20
+#define SMI2021_TRC_FIELD_2 0x40
+#define SMI2021_TRC 0x80
+
+#define DEBUG
+#ifdef DEBUG
+#define smi2021_dbg(fmt, args...) \
+ pr_debug("smi2021::%s: " fmt, __func__, \
+ ##args)
+#else
+#define smi2021_dbg(fmt, args...)
+#endif
+
+#define smi2021_info(fmt, args...) \
+ pr_info("smi2021::%s: " fmt, \
+ __func__, ##args)
+
+#define smi2021_warn(fmt, args...) \
+ pr_warn("smi2021::%s: " fmt, \
+ __func__, ##args)
+
+#define smi2021_err(fmt, args...) \
+ pr_err("smi2021::%s: " fmt, \
+ __func__, ##args)
+
+enum smi2021_sync {
+ HSYNC,
+ SYNCZ1,
+ SYNCZ2,
+ TRC
+};
+
+/* Buffer for one video frame */
+struct smi2021_buffer {
+ /* Common vb2 stuff, must be first */
+ struct vb2_buffer vb;
+ struct list_head list;
+
+ void *mem;
+ unsigned int length;
+
+ bool second_field;
+ bool in_blank;
+ unsigned int pos;
+
+ /* ActiveVideo - Line counter */
+ u16 trc_av;
+};
+
+struct smi2021_isoc_ctl {
+ int max_pkt_size;
+ int num_bufs;
+ struct urb **urb;
+ char **transfer_buffer;
+ struct smi2021_buffer *buf;
+};
+
+
+struct smi2021_fmt {
+ char *name;
+ u32 fourcc;
+ int depth;
+};
+
+struct smi2021_input {
+ char *name;
+ int type;
+};
+
+struct smi2021_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device vdev;
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ struct v4l2_subdev *sd_saa7113;
+
+ struct usb_device *udev;
+ struct device *dev;
+
+ /* Capture buffer queue */
+ struct vb2_queue vb_vidq;
+
+ /* ISOC control struct */
+ struct list_head avail_bufs;
+ struct smi2021_isoc_ctl isoc_ctl;
+
+ int width; /* frame width */
+ int height; /* frame height */
+ unsigned int ctl_input; /* selected input */
+ v4l2_std_id norm; /* current norm */
+ struct smi2021_fmt *fmt; /* selected format */
+ unsigned int buf_count; /* for video buffers */
+
+ /* i2c i/o */
+ struct i2c_adapter i2c_adap;
+ struct i2c_client i2c_client;
+
+ struct mutex v4l2_lock;
+ struct mutex vb_queue_lock;
+ spinlock_t buf_lock;
+
+ enum smi2021_sync sync_state;
+
+ /* audio */
+ struct snd_card *snd_card;
+ struct snd_pcm_substream *pcm_substream;
+
+ unsigned int pcm_write_ptr;
+ unsigned int pcm_complete_samples;
+
+ u8 pcm_read_offset;
+ struct work_struct adev_capture_trigger;
+ atomic_t adev_capturing;
+};
+
+/* Provided by smi2021_main.c */
+int smi2021_write_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 val);
+int smi2021_read_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 *val);
+
+/* Provided by smi2021_v4l2.c */
+int smi2021_vb2_setup(struct smi2021_dev *dev);
+int smi2021_video_register(struct smi2021_dev *dev);
+void smi2021_clear_queue(struct smi2021_dev *dev);
+
+/* Provided by smi2021_video.c */
+int smi2021_alloc_isoc(struct smi2021_dev *dev);
+void smi2021_free_isoc(struct smi2021_dev *dev);
+void smi2021_cancel_isoc(struct smi2021_dev *dev);
+void smi2021_uninit_isoc(struct smi2021_dev *dev);
+
+/* Provided by smi2021_i2c.c */
+int smi2021_i2c_register(struct smi2021_dev *dev);
+int smi2021_i2c_unregister(struct smi2021_dev *dev);
+
+/* Provided by smi2021_audio.c */
+int smi2021_snd_register(struct smi2021_dev *dev);
+void smi2021_snd_unregister(struct smi2021_dev *dev);
+void smi2021_audio(struct smi2021_dev *dev, u8 *data, int len);
+#endif /* SMI2021_H */
--
1.8.1.1

2013-03-14 14:12:50

by Jon Arne Jørgensen

[permalink] [raw]
Subject: [RFC V1 3/8] smi2021: Add smi2021_i2c.c

This file is responsible for registering the device
with the kernel i2c subsystem.
v4l2 talks to the saa7113 chip of the device via i2c.

Signed-off-by: Jon Arne Jørgensen <[email protected]>
---
drivers/media/usb/smi2021/smi2021_i2c.c | 160 ++++++++++++++++++++++++++++++++
1 file changed, 160 insertions(+)
create mode 100644 drivers/media/usb/smi2021/smi2021_i2c.c

diff --git a/drivers/media/usb/smi2021/smi2021_i2c.c b/drivers/media/usb/smi2021/smi2021_i2c.c
new file mode 100644
index 0000000..5b6f3f5
--- /dev/null
+++ b/drivers/media/usb/smi2021/smi2021_i2c.c
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * smi2021_i2c.c *
+ * *
+ * USB Driver for SMI2021 - EasyCAP *
+ * USB ID 1c88:003c *
+ * *
+ * *****************************************************************************
+ *
+ * Copyright 2011-2013 Jon Arne Jørgensen
+ * <jonjon.arnearne--a.t--gmail.com>
+ *
+ * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
+ *
+ * This file is part of SMI2021
+ * http://code.google.com/p/easycap-somagic-linux/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This driver is heavily influensed by the STK1160 driver.
+ * Copyright (C) 2012 Ezequiel Garcia
+ * <elezegarcia--a.t--gmail.com>
+ *
+ */
+
+#include "smi2021.h"
+
+/* The device will not return the chip_name on address 0x00.
+ * But the saa7115 i2c driver needs the chip id to match "f7113"
+ * if we want to use it,
+ * so we have to fake the return of this value
+ */
+
+static char chip_id[] = { 'x', 255, 55, 49, 49, 115, 0 };
+static int id_ptr;
+
+static unsigned int i2c_debug;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
+
+#define dprint_i2c(fmt, args...) \
+do { \
+ if (i2c_debug) \
+ pr_debug("smi2021[i2c]::%s: " fmt, __func__, ##args); \
+} while (0)
+
+
+static int i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct smi2021_dev *dev = i2c_adap->algo_data;
+
+ switch (num) {
+ case 2: { /* Read reg */
+ if (msgs[0].len != 1 || msgs[1].len != 1) {
+ dprint_i2c("both messages must be 1 byte\n");
+ goto err_out;
+
+ if ((msgs[1].flags & I2C_M_RD) != I2C_M_RD)
+ dprint_i2c("last message should have rd flag\n");
+ goto err_out;
+ }
+
+ if (msgs[0].buf[0] == 0) {
+ msgs[1].buf[0] = chip_id[id_ptr];
+ if (chip_id[id_ptr] != 0)
+ id_ptr += 1;
+ } else {
+ smi2021_read_reg(dev, msgs[0].addr, msgs[0].buf[0],
+ msgs[1].buf);
+ }
+ break;
+ }
+ case 1: { /* Write reg */
+ if (msgs[0].len == 0) {
+ break;
+ } else if (msgs[0].len != 2) {
+ dprint_i2c("unsupported len\n");
+ goto err_out;
+ }
+ if (msgs[0].buf[0] == 0) {
+ /* We don't handle writing to addr 0x00 */
+ break;
+ }
+
+ smi2021_write_reg(dev, msgs[0].addr, msgs[0].buf[0],
+ msgs[0].buf[1]);
+ break;
+ }
+ default: {
+ dprint_i2c("driver can only handle 1 or 2 messages\n");
+ goto err_out;
+ }
+ }
+ return num;
+
+err_out:
+ return -EOPNOTSUPP;
+}
+
+static u32 functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm algo = {
+ .master_xfer = i2c_xfer,
+ .functionality = functionality,
+};
+
+static struct i2c_adapter adap_template = {
+ .owner = THIS_MODULE,
+ .name = "smi2021_easycap_dc60",
+ .algo = &algo,
+};
+
+static struct i2c_client client_template = {
+ .name = "smi2021 internal",
+};
+
+int smi2021_i2c_register(struct smi2021_dev *dev)
+{
+ int rc;
+
+ id_ptr = 0;
+
+ dev->i2c_adap = adap_template;
+ dev->i2c_adap.dev.parent = dev->dev;
+ strcpy(dev->i2c_adap.name, "smi2021");
+ dev->i2c_adap.algo_data = dev;
+
+ i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
+
+ rc = i2c_add_adapter(&dev->i2c_adap);
+ if (rc < 0) {
+ smi2021_err("can't add i2c adapter, errno: %d\n", rc);
+ return rc;
+ }
+
+ dev->i2c_client = client_template;
+ dev->i2c_client.adapter = &dev->i2c_adap;
+
+ return 0;
+}
+
+int smi2021_i2c_unregister(struct smi2021_dev *dev)
+{
+ i2c_del_adapter(&dev->i2c_adap);
+ return 0;
+}
--
1.8.1.1

2013-03-14 14:12:48

by Jon Arne Jørgensen

[permalink] [raw]
Subject: [RFC V1 4/8] smi2021: Add smi2021_v4l2.c

This file is responsible for registering the device with the v4l2 subsystem,
and the communication with v4l2.
Most of the v4l2 ioctls are just passed on to vidbuf2.

Signed-off-by: Jon Arne Jørgensen <[email protected]>
---
drivers/media/usb/smi2021/smi2021_v4l2.c | 566 +++++++++++++++++++++++++++++++
1 file changed, 566 insertions(+)
create mode 100644 drivers/media/usb/smi2021/smi2021_v4l2.c

diff --git a/drivers/media/usb/smi2021/smi2021_v4l2.c b/drivers/media/usb/smi2021/smi2021_v4l2.c
new file mode 100644
index 0000000..d402093
--- /dev/null
+++ b/drivers/media/usb/smi2021/smi2021_v4l2.c
@@ -0,0 +1,566 @@
+/*******************************************************************************
+ * smi2021_v4l2.c *
+ * *
+ * USB Driver for smi2021 - EasyCap *
+ * USB ID 1c88:003c *
+ * *
+ * *****************************************************************************
+ *
+ * Copyright 2011-2013 Jon Arne Jørgensen
+ * <jonjon.arnearne--a.t--gmail.com>
+ *
+ * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
+ *
+ * This file is part of SMI2021
+ * http://code.google.com/p/easycap-somagic-linux/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This driver is heavily influensed by the STK1160 driver.
+ * Copyright (C) 2012 Ezequiel Garcia
+ * <elezegarcia--a.t--gmail.com>
+ *
+ */
+
+#include "smi2021.h"
+
+static struct smi2021_fmt format[] = {
+ {
+ .name = "16bpp YU2, 4:2:2, packed",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ }
+};
+
+static const int inputs = 2;
+static struct smi2021_input input[] = {
+ {
+ .name = "Composite",
+ .type = SAA7115_COMPOSITE0,
+ },
+ {
+ .name = "S-Video",
+ .type = SAA7115_SVIDEO1,
+ }
+};
+
+static void smi2021_set_input(struct smi2021_dev *dev)
+{
+ if (dev->udev == NULL)
+ return;
+
+ if (dev->ctl_input >= inputs) {
+ smi2021_err("BUG: ctl_input to big!\n");
+ return;
+ }
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
+ input[dev->ctl_input].type, 0, 0);
+}
+
+static int smi2021_start_streaming(struct smi2021_dev *dev)
+{
+ u8 data[2];
+ int i, rc = 0;
+ data[0] = 0x01;
+ data[1] = 0x05;
+
+ if (!dev->udev)
+ return -ENODEV;
+
+ dev->sync_state = HSYNC;
+ dev->buf_count = 0;
+
+ if (mutex_lock_interruptible(&dev->v4l2_lock))
+ return -ERESTARTSYS;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1);
+
+ /* The saa 7115 driver sets these wrong,
+ * this leads to sync issues.
+ * V_GATE1_START
+ * V_GATE1_STOP
+ * V_GATE1_MSB
+ * All these should be 0x00 for this device.
+ */
+ smi2021_write_reg(dev, 0x4a, 0x15, 0x00);
+ smi2021_write_reg(dev, 0x4a, 0x16, 0x00);
+ smi2021_write_reg(dev, 0x4a, 0x17, 0x00);
+
+ rc = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
+ 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x01, 0x00, data, sizeof(data), 1000);
+ if (rc < 0) {
+ smi2021_err("Could not start device!\n");
+ goto out_unlock;
+ }
+
+ /* It's mandatory to set alt interface before allocating isoc buffer */
+ usb_set_interface(dev->udev, 0, 2);
+
+ smi2021_write_reg(dev, 0, 0x1740, 0x1d);
+
+ if (!dev->isoc_ctl.num_bufs) {
+ rc = smi2021_alloc_isoc(dev);
+ if (rc < 0)
+ goto out_stop_hw;
+ }
+
+ /* submit urbs and enable IRQ */
+ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
+ rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_KERNEL);
+ if (rc) {
+ smi2021_err("cannot submit urb[%d] (%d)\n", i, rc);
+ goto out_uninit;
+ }
+ }
+
+ mutex_unlock(&dev->v4l2_lock);
+
+ smi2021_dbg("Streaming started!");
+ return 0;
+
+out_uninit:
+ smi2021_uninit_isoc(dev);
+out_stop_hw:
+ usb_set_interface(dev->udev, 0, 0);
+ smi2021_clear_queue(dev);
+
+out_unlock:
+ mutex_unlock(&dev->v4l2_lock);
+
+ return rc;
+}
+
+/* Must be called with v4l2_lock hold */
+static void smi2021_stop_hw(struct smi2021_dev *dev)
+{
+ int rc = 0;
+ u8 data[] = { 0x01, 0x03 };
+
+ if (!dev->udev)
+ return;
+
+ usb_set_interface(dev->udev, 0, 0);
+
+ rc = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
+ 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x01, 0x00, data, sizeof(data), 1000);
+ if (rc < 0)
+ smi2021_err("Could not stop device!\n");
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
+}
+
+static int smi2021_stop_streaming(struct smi2021_dev *dev)
+{
+ /* HACK: Stop the audio subsystem,
+ * without this, the pcm middle-layer will hang waiting for more data.
+ *
+ * Is there a better way to do this?
+ */
+ if (dev->pcm_substream && dev->pcm_substream->runtime) {
+ struct snd_pcm_runtime *runtime = dev->pcm_substream->runtime;
+ if (runtime->status) {
+ runtime->status->state = SNDRV_PCM_STATE_DRAINING;
+ wake_up(&runtime->sleep);
+ }
+ }
+
+ if (mutex_lock_interruptible(&dev->v4l2_lock))
+ return -ERESTARTSYS;
+
+ smi2021_cancel_isoc(dev);
+ smi2021_free_isoc(dev);
+ smi2021_stop_hw(dev);
+ smi2021_clear_queue(dev);
+
+ smi2021_dbg("Streaming stopped!\n");
+
+ mutex_unlock(&dev->v4l2_lock);
+
+ return 0;
+}
+
+static struct v4l2_file_operations smi2021_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+/******************************************************************************/
+/* */
+/* Vidioc IOCTLS */
+/* */
+/******************************************************************************/
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index != 0)
+ return -EINVAL;
+
+ strlcpy(f->description, format[f->index].name, sizeof(f->description));
+ f->pixelformat = format[f->index].fourcc;
+
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct smi2021_dev *dev = video_drvdata(file);
+
+ strcpy(cap->driver, "smi2021");
+ strcpy(cap->card, "smi2021");
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+ cap->device_caps =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct smi2021_dev *dev = video_drvdata(file);
+
+ f->fmt.pix.pixelformat = dev->fmt->fourcc;
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ f->fmt.pix.bytesperline = dev->width * 2;
+ f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ f->fmt.pix.priv = 0;
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct smi2021_dev *dev = video_drvdata(file);
+
+ f->fmt.pix.pixelformat = dev->fmt->fourcc;
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ f->fmt.pix.bytesperline = dev->width * 2;
+ f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ f->fmt.pix.priv = 0;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct smi2021_dev *dev = video_drvdata(file);
+ struct vb2_queue *q = &dev->vb_vidq;
+
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ vidioc_try_fmt_vid_cap(file, priv, f);
+ return 0;
+}
+
+static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct smi2021_dev *dev = video_drvdata(file);
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm);
+ return 0;
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct smi2021_dev *dev = video_drvdata(file);
+
+ *norm = dev->norm;
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct smi2021_dev *dev = video_drvdata(file);
+ struct vb2_queue *q = &dev->vb_vidq;
+
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ if (!dev->udev)
+ return -ENODEV;
+
+ dev->norm = *norm;
+ if (dev->norm & V4L2_STD_525_60) {
+ dev->width = SMI2021_BYTES_PER_LINE / 2;
+ dev->height = SMI2021_NTSC_LINES;
+ } else if (dev->norm & V4L2_STD_625_50) {
+ dev->width = SMI2021_BYTES_PER_LINE / 2;
+ dev->height = SMI2021_PAL_LINES;
+ } else {
+ smi2021_err("Invalid standard\n");
+ return -EINVAL;
+ }
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct smi2021_dev *dev = video_drvdata(file);
+
+ if (i->index >= inputs)
+ return -EINVAL;
+
+ strlcpy(i->name, input[i->index].name, sizeof(i->name));
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ i->std = dev->vdev.tvnorms;
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct smi2021_dev *dev = video_drvdata(file);
+ *i = dev->ctl_input;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct smi2021_dev *dev = video_drvdata(file);
+
+ if (i >= inputs)
+ return -EINVAL;
+
+ dev->ctl_input = i;
+ smi2021_set_input(dev);
+
+ return 0;
+}
+
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ switch (chip->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_ioctl_ops smi2021_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_querystd = vidioc_querystd,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+
+ /* vb2 handle these */
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
+
+};
+
+/******************************************************************************/
+/* */
+/* Videobuf2 operations */
+/* */
+/******************************************************************************/
+static int queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *v4l2_fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct smi2021_dev *dev = vb2_get_drv_priv(vq);
+ unsigned long size;
+
+ size = dev->width * dev->height * 2;
+
+ *nbuffers = clamp_t(unsigned int, *nbuffers, 2, 4);
+
+ /* Packed color format */
+ *nplanes = 1;
+ sizes[0] = size;
+
+ return 0;
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+ unsigned long flags;
+ struct smi2021_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct smi2021_buffer *buf = container_of(vb, struct smi2021_buffer,
+ vb);
+
+ spin_lock_irqsave(&dev->buf_lock, flags);
+ if (!dev->udev) {
+ /*
+ * If the device is disconnected return the buffer to userspace
+ * directly. The next QBUF call will fail with -ENODEV.
+ */
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ } else {
+ buf->mem = vb2_plane_vaddr(vb, 0);
+ buf->length = vb2_plane_size(vb, 0);
+
+ buf->pos = 0;
+ buf->trc_av = 0;
+ buf->in_blank = true;
+ buf->second_field = false;
+
+ if (buf->length < dev->width * dev->height * 2)
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ else
+ list_add_tail(&buf->list, &dev->avail_bufs);
+ }
+ spin_unlock_irqrestore(&dev->buf_lock, flags);
+}
+
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct smi2021_dev *dev = vb2_get_drv_priv(vq);
+ return smi2021_start_streaming(dev);
+}
+
+static int stop_streaming(struct vb2_queue *vq)
+{
+ struct smi2021_dev *dev = vb2_get_drv_priv(vq);
+ return smi2021_stop_streaming(dev);
+}
+
+static struct vb2_ops smi2021_video_qops = {
+ .queue_setup = queue_setup,
+ .buf_queue = buffer_queue,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static struct video_device v4l2_template = {
+ .name = "easycap_smi2021_dc60",
+ .tvnorms = V4L2_STD_625_50 | V4L2_STD_525_60,
+ .fops = &smi2021_fops,
+ .ioctl_ops = &smi2021_ioctl_ops,
+ .release = video_device_release_empty,
+};
+
+/* Must be called with both v4l2_lock and vb_queue_lock hold */
+void smi2021_clear_queue(struct smi2021_dev *dev)
+{
+ struct smi2021_buffer *buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->buf_lock, flags);
+ while (!list_empty(&dev->avail_bufs)) {
+ buf = list_first_entry(&dev->avail_bufs,
+ struct smi2021_buffer, list);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ dev->isoc_ctl.buf = NULL;
+ spin_unlock_irqrestore(&dev->buf_lock, flags);
+}
+
+int smi2021_vb2_setup(struct smi2021_dev *dev)
+{
+ int rc;
+ struct vb2_queue *q;
+
+ q = &dev->vb_vidq;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = dev;
+ q->buf_struct_size = sizeof(struct smi2021_buffer);
+ q->ops = &smi2021_video_qops;
+ q->mem_ops = &vb2_vmalloc_memops;
+
+ rc = vb2_queue_init(q);
+ if (rc < 0)
+ return rc;
+
+ /* Initialize video dma queue */
+ INIT_LIST_HEAD(&dev->avail_bufs);
+
+ return 0;
+}
+
+int smi2021_video_register(struct smi2021_dev *dev)
+{
+ int rc;
+
+ dev->vdev = v4l2_template;
+ dev->vdev.debug = 0;
+ dev->vdev.queue = &dev->vb_vidq;
+
+ dev->vdev.lock = &dev->v4l2_lock;
+ dev->vdev.queue->lock = &dev->vb_queue_lock;
+
+ dev->vdev.v4l2_dev = &dev->v4l2_dev;
+ set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags);
+
+ /* PAL is default */
+ dev->norm = V4L2_STD_PAL;
+ dev->width = SMI2021_BYTES_PER_LINE / 2;
+ dev->height = SMI2021_PAL_LINES;
+
+ dev->fmt = &format[0];
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
+ smi2021_set_input(dev);
+
+ video_set_drvdata(&dev->vdev, dev);
+ rc = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1);
+ if (rc < 0) {
+ smi2021_err("video_register_device failed %d\n", rc);
+ return rc;
+ }
+
+ v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
+ video_device_node_name(&dev->vdev));
+
+ return 0;
+}
--
1.8.1.1

2013-03-15 12:09:07

by Ezequiel Garcia

[permalink] [raw]
Subject: Re: [RFC V1 0/8] Add a driver for somagic smi2021

On Thu, Mar 14, 2013 at 03:06:56PM +0100, Jon Arne Jørgensen wrote:
> This patch-set will add a driver for the Somagic SMI2021 chip.
>
> This chip is found inside different usb video-capture devices.
> Most of them are branded as EasyCap, but there also seems to be
> some other brands selling devices with this chip.
>
> This driver is split into two modules, where one is called smi2021-bootloader,
> and the other is just called smi2021.
>
> The bootloader is responsible for the upload of a firmware that is needed by some
> versions of the devices.
>
> All Somagic devices that need firmware seems to identify themselves
> with the usb product id 0x0007. There is no way for the kernel to know
> what firmware to upload to the device without user interaction.
>
> If there is only one firmware present on the computer, the kernel
> will upload that firmware to any device that identifies as 0x0007.
> If there are multiple Somagic firmwares present, the user will have to pass
> a module parameter to the smi2021-bootloader module to tell what firmware to use.
>

Nice job!

I have some minor comments on each patch, but also I don't agree
with the patch splitting: what's the point in splitting and sending
one patch per file?

It doesn't make it any easier to review, so why don't you just
send one patch: "Introduce smi2021 driver"?

The rule is one patch per change, and I believe this whole patchset
is just one change: adding a new driver.

--
Ezequiel García, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com

2013-03-15 12:13:19

by Ezequiel Garcia

[permalink] [raw]
Subject: Re: [RFC V1 1/8] smi2021: Add the header file

On Thu, Mar 14, 2013 at 03:06:57PM +0100, Jon Arne Jørgensen wrote:
> This is the header file for the smi2021 module.
>
> Signed-off-by: Jon Arne Jørgensen <[email protected]>
> ---
> drivers/media/usb/smi2021/smi2021.h | 228 ++++++++++++++++++++++++++++++++++++
> 1 file changed, 228 insertions(+)
> create mode 100644 drivers/media/usb/smi2021/smi2021.h
>
> diff --git a/drivers/media/usb/smi2021/smi2021.h b/drivers/media/usb/smi2021/smi2021.h
> new file mode 100644
> index 0000000..eba0fef
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/smi2021.h
> @@ -0,0 +1,228 @@
> +/*******************************************************************************
> + * smi2021.h *
> + * *
> + * USB Driver for SMI2021 - EasyCap *
> + * USB ID 1c88:003c *
> + * *
> + * *****************************************************************************
> + *
> + * Copyright 2011-2013 Jon Arne Jørgensen
> + * <jonjon.arnearne--a.t--gmail.com>
> + *
> + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> + *
> + * This file is part of SMI2021
> + * http://code.google.com/p/easycap-somagic-linux/
> + *

I'm not sure how helpful it is to add external references.
Keep in mind that once mainlined, this driver will quickly evolve
and the external link can get outdated/removed/deprecated, etc.

> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *

Please note Linux kernel preferes GPL v2 *only*, and not any later
(although I'm not a lawyer).

> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + * This driver is heavily influensed by the STK1160 driver.
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>

Cool ;-)

> + *
> + */
> +
> +#ifndef SMI2021_H
> +#define SMI2021_H
> +
> +#include <linux/module.h>
> +#include <linux/usb.h>
> +#include <linux/types.h>
> +#include <linux/spinlock_types.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-chip-ident.h>
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-vmalloc.h>
> +#include <media/saa7115.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/initval.h>
> +
> +#define SMI2021_DRIVER_VERSION "0.1"
> +
> +/* For ISOC */
> +#define SMI2021_MAX_PKT_SIZE 3072
> +#define SMI2021_ISOC_PACKETS 10 /* 64 */
> +#define SMI2021_ISOC_BUFS 4 /* 16 */
> +#define SMI2021_ISOC_EP 0x82
> +
> +/* The structure of the array we use to send i2c settings to the device */
> +#define SMI2021_CTRL_HEAD 0x00
> +#define SMI2021_CTRL_ADDR 0x01
> +#define SMI2021_CTRL_BM_DATA_TYPE 0x02
> +#define SMI2021_CTRL_BM_DATA_OFFSET 0x03
> +#define SMI2021_CTRL_DATA_SIZE 0x04
> +
> +#define SMI2021_CTRL_REG_HI 0x05
> +#define SMI2021_CTRL_REG_LO 0x06
> +#define SMI2021_CTRL_REG_VAL 0x07
> +
> +#define SMI2021_CTRL_I2C_REG 0x05
> +#define SMI2021_CTRL_I2C_VAL 0x06
> +#define SMI2021_CTRL_I2C_RCV_VAL 0x05
> +
> +/* General video constants */
> +#define SMI2021_BYTES_PER_LINE 1440
> +#define SMI2021_PAL_LINES 576
> +#define SMI2021_NTSC_LINES 486
> +
> +/* Timing Referance Codes, see saa7113 datasheet */
> +#define SMI2021_TRC_EAV 0x10
> +#define SMI2021_TRC_VBI 0x20
> +#define SMI2021_TRC_FIELD_2 0x40
> +#define SMI2021_TRC 0x80
> +
> +#define DEBUG
> +#ifdef DEBUG
> +#define smi2021_dbg(fmt, args...) \
> + pr_debug("smi2021::%s: " fmt, __func__, \
> + ##args)
> +#else
> +#define smi2021_dbg(fmt, args...)
> +#endif
> +
> +#define smi2021_info(fmt, args...) \
> + pr_info("smi2021::%s: " fmt, \
> + __func__, ##args)
> +
> +#define smi2021_warn(fmt, args...) \
> + pr_warn("smi2021::%s: " fmt, \
> + __func__, ##args)
> +
> +#define smi2021_err(fmt, args...) \
> + pr_err("smi2021::%s: " fmt, \
> + __func__, ##args)
> +
> +enum smi2021_sync {
> + HSYNC,
> + SYNCZ1,
> + SYNCZ2,
> + TRC
> +};
> +
> +/* Buffer for one video frame */
> +struct smi2021_buffer {
> + /* Common vb2 stuff, must be first */
> + struct vb2_buffer vb;
> + struct list_head list;
> +
> + void *mem;
> + unsigned int length;
> +
> + bool second_field;
> + bool in_blank;
> + unsigned int pos;
> +
> + /* ActiveVideo - Line counter */
> + u16 trc_av;
> +};
> +
> +struct smi2021_isoc_ctl {
> + int max_pkt_size;
> + int num_bufs;
> + struct urb **urb;
> + char **transfer_buffer;
> + struct smi2021_buffer *buf;
> +};
> +
> +
> +struct smi2021_fmt {
> + char *name;
> + u32 fourcc;
> + int depth;
> +};
> +
> +struct smi2021_input {
> + char *name;
> + int type;
> +};
> +
> +struct smi2021_dev {
> + struct v4l2_device v4l2_dev;
> + struct video_device vdev;
> + struct v4l2_ctrl_handler ctrl_handler;
> +
> + struct v4l2_subdev *sd_saa7113;
> +
> + struct usb_device *udev;
> + struct device *dev;
> +
> + /* Capture buffer queue */
> + struct vb2_queue vb_vidq;
> +
> + /* ISOC control struct */
> + struct list_head avail_bufs;
> + struct smi2021_isoc_ctl isoc_ctl;
> +
> + int width; /* frame width */
> + int height; /* frame height */
> + unsigned int ctl_input; /* selected input */
> + v4l2_std_id norm; /* current norm */
> + struct smi2021_fmt *fmt; /* selected format */
> + unsigned int buf_count; /* for video buffers */
> +
> + /* i2c i/o */
> + struct i2c_adapter i2c_adap;
> + struct i2c_client i2c_client;
> +
> + struct mutex v4l2_lock;
> + struct mutex vb_queue_lock;
> + spinlock_t buf_lock;
> +
> + enum smi2021_sync sync_state;
> +
> + /* audio */
> + struct snd_card *snd_card;
> + struct snd_pcm_substream *pcm_substream;
> +
> + unsigned int pcm_write_ptr;
> + unsigned int pcm_complete_samples;
> +
> + u8 pcm_read_offset;
> + struct work_struct adev_capture_trigger;
> + atomic_t adev_capturing;
> +};
> +
> +/* Provided by smi2021_main.c */
> +int smi2021_write_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 val);
> +int smi2021_read_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 *val);
> +
> +/* Provided by smi2021_v4l2.c */
> +int smi2021_vb2_setup(struct smi2021_dev *dev);
> +int smi2021_video_register(struct smi2021_dev *dev);
> +void smi2021_clear_queue(struct smi2021_dev *dev);
> +
> +/* Provided by smi2021_video.c */
> +int smi2021_alloc_isoc(struct smi2021_dev *dev);
> +void smi2021_free_isoc(struct smi2021_dev *dev);
> +void smi2021_cancel_isoc(struct smi2021_dev *dev);
> +void smi2021_uninit_isoc(struct smi2021_dev *dev);
> +
> +/* Provided by smi2021_i2c.c */
> +int smi2021_i2c_register(struct smi2021_dev *dev);
> +int smi2021_i2c_unregister(struct smi2021_dev *dev);
> +
> +/* Provided by smi2021_audio.c */
> +int smi2021_snd_register(struct smi2021_dev *dev);
> +void smi2021_snd_unregister(struct smi2021_dev *dev);
> +void smi2021_audio(struct smi2021_dev *dev, u8 *data, int len);
> +#endif /* SMI2021_H */
> --
> 1.8.1.1
>

--
Ezequiel García, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com

2013-03-15 12:21:08

by Ezequiel Garcia

[permalink] [raw]
Subject: Re: [RFC V1 2/8] smi2021: Add smi2021_main.c

On Thu, Mar 14, 2013 at 03:06:58PM +0100, Jon Arne Jørgensen wrote:
> This is the core of the smi2021 module.
> It will register the module with the kernel, and register the
> usb probe function.
>
> Signed-off-by: Jon Arne Jørgensen <[email protected]>
> ---
> drivers/media/usb/smi2021/smi2021_main.c | 339 +++++++++++++++++++++++++++++++
> 1 file changed, 339 insertions(+)
> create mode 100644 drivers/media/usb/smi2021/smi2021_main.c
>
> diff --git a/drivers/media/usb/smi2021/smi2021_main.c b/drivers/media/usb/smi2021/smi2021_main.c
> new file mode 100644
> index 0000000..cc600e7
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/smi2021_main.c
> @@ -0,0 +1,339 @@
> +/*******************************************************************************
> + * smi2021_main.c *
> + * *
> + * USB Driver for SMI2021 - EasyCAP *
> + * USB ID 1c88:003c *
> + * *
> + * *****************************************************************************
> + *
> + * Copyright 2011-2013 Jon Arne Jørgensen
> + * <jonjon.arnearne--a.t--gmail.com>
> + *
> + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> + *
> + * This file is part of SMI2021
> + * http://code.google.com/p/easycap-somagic-linux/
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + * This driver is heavily influensed by the STK1160 driver.
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + */
> +
> +#include "smi2021.h"
> +
> +#define VENDOR_ID 0x1c88
> +
> +static unsigned int imput;
> +module_param(imput, int, 0644);

s/imput/input

> +MODULE_PARM_DESC(input, "Set default input");
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Jon Arne Jørgensen <jonjon.arnearne--a.t--gmail.com>");
> +MODULE_DESCRIPTION("SMI2021 - EasyCap");
> +MODULE_VERSION(SMI2021_DRIVER_VERSION);
> +
> +
> +struct usb_device_id smi2021_usb_device_id_table[] = {
> + { USB_DEVICE(VENDOR_ID, 0x003c) },
> + { USB_DEVICE(VENDOR_ID, 0x003d) },
> + { USB_DEVICE(VENDOR_ID, 0x003e) },
> + { USB_DEVICE(VENDOR_ID, 0x003f) },
> + { }
> +};
> +
> +MODULE_DEVICE_TABLE(usb, smi2021_usb_device_id_table);
> +
> +static unsigned short saa7113_addrs[] = {
> + 0x4a,
> + I2C_CLIENT_END
> +};
> +
> +/******************************************************************************/
> +/* */
> +/* Write to saa7113 */
> +/* */
> +/******************************************************************************/
> +
> +inline int transfer_usb_ctrl(struct smi2021_dev *dev, u8 *data, int len)
> +{
> + return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
> + 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> + 0x0b, 0x00,
> + data, len, 1000);
> +}
> +
> +int smi2021_write_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 val)
> +{
> + int rc;
> + u8 snd_data[8];
> +
> + memset(snd_data, 0x00, 8);
> +

I think you want ARRAY_SIZE macro here.

> + snd_data[SMI2021_CTRL_HEAD] = 0x0b;
> + snd_data[SMI2021_CTRL_ADDR] = addr;
> + snd_data[SMI2021_CTRL_DATA_SIZE] = 0x01;
> +
> + if (addr) {
> + /* This is I2C data for the saa7113 chip */
> + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0xc0;
> + snd_data[SMI2021_CTRL_BM_DATA_OFFSET] = 0x01;
> +
> + snd_data[SMI2021_CTRL_I2C_REG] = reg;
> + snd_data[SMI2021_CTRL_I2C_VAL] = val;
> + } else {
> + /* This is register settings for the smi2021 chip */
> + snd_data[SMI2021_CTRL_BM_DATA_OFFSET] = 0x82;
> +

Mmmm... you should really avoid magic numbers: 0x82, 0xc0, 0x01...
can't you use some more meaningful macros?

> + snd_data[SMI2021_CTRL_REG_HI] = __cpu_to_be16(reg) >> 8;
> + snd_data[SMI2021_CTRL_REG_LO] = __cpu_to_be16(reg);
> +
> + }
> +
> + rc = transfer_usb_ctrl(dev, snd_data, 8);

ARRAY_SIZE macro again...

> + if (rc < 0) {
> + smi2021_warn("write failed on register 0x%x, errno: %d\n",
> + reg, rc);
> + return rc;
> + }
> +
> + return 0;
> +}
> +
> +int smi2021_read_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 *val)
> +{
> + int rc;
> + u8 rcv_data[13];
> + u8 snd_data[8];
> + memset(rcv_data, 0x00, 13);
> + memset(snd_data, 0x00, 8);
> +

Ditto.

> + snd_data[SMI2021_CTRL_HEAD] = 0x0b;
> + snd_data[SMI2021_CTRL_ADDR] = addr;
> + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0x84;
> + snd_data[SMI2021_CTRL_DATA_SIZE] = 0x01;
> + snd_data[SMI2021_CTRL_I2C_REG] = reg;
> +
> + *val = 0;
> +
> + rc = transfer_usb_ctrl(dev, snd_data, 8);
> + if (rc < 0) {
> + smi2021_warn(
> + "1st pass failing to read reg 0x%x, usb-errno: %d\n",
> + reg, rc);
> + return rc;
> + }
> +
> + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0xa0;
> + rc = transfer_usb_ctrl(dev, snd_data, 8);
> + if (rc < 0) {
> + smi2021_warn(
> + "2nd pass failing to read reg 0x%x, usb-errno: %d\n",
> + reg, rc);
> + return rc;
> + }
> +
> + rc = usb_control_msg(dev->udev,
> + usb_rcvctrlpipe(dev->udev, 0x80), 0x01,
> + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> + 0x0b, 0x00, rcv_data, 13, 1000);
> + if (rc < 0) {
> + smi2021_warn("Failed to read reg 0x%x, usb-errno: %d\n",
> + reg, rc);
> + return rc;
> + }
> +
> + *val = rcv_data[SMI2021_CTRL_I2C_RCV_VAL];
> + return 0;
> +}
> +
> +static void smi2021_reset_device(struct smi2021_dev *dev)
> +{
> + smi2021_write_reg(dev, 0, 0x3a, 0x80);
> + smi2021_write_reg(dev, 0, 0x3b, 0x80);
> + smi2021_write_reg(dev, 0, 0x3b, 0x00);

More magic numbers :S

> +}
> +
> +static void release_v4l2_dev(struct v4l2_device *v4l2_dev)
> +{
> + struct smi2021_dev *dev = container_of(v4l2_dev, struct smi2021_dev,
> + v4l2_dev);
> + smi2021_dbg("Releasing all resources\n");
> +
> + smi2021_i2c_unregister(dev);
> +
> + v4l2_ctrl_handler_free(&dev->ctrl_handler);
> + v4l2_device_unregister(&dev->v4l2_dev);
> + kfree(dev);
> +}
> +
> +#define hb_mult(w_max_packet_size) (1 + (((w_max_packet_size) >> 11) & 0x03))
> +
> +static int smi2021_scan_usb(struct usb_interface *intf, struct usb_device *udev)
> +{
> + int i, e, ifnum, sizedescr, size;
> + const struct usb_endpoint_descriptor *desc;
> + ifnum = intf->altsetting[0].desc.bInterfaceNumber;
> +
> + for (i = 0; i < intf->num_altsetting; i++) {
> + for (e = 0; e < intf->altsetting[i].desc.bNumEndpoints; e++) {
> + desc = &intf->altsetting[i].endpoint[e].desc;
> + sizedescr = le16_to_cpu(desc->wMaxPacketSize);
> + size = sizedescr & 0x7ff;
> +
> + if (udev->speed == USB_SPEED_HIGH)
> + size = size * hb_mult(sizedescr);
> + }
> + }
> + return 0;
> +}
> +
> +/******************************************************************************/
> +/* */
> +/* DEVICE - PROBE & DISCONNECT */
> +/* */
> +/******************************************************************************/
> +static int smi2021_usb_probe(struct usb_interface *intf,
> + const struct usb_device_id *devid)
> +{
> + int rc = 0;

Is this rc initialization needed?

> + struct usb_device *udev = interface_to_usbdev(intf);
> + struct smi2021_dev *dev;
> +
> + if (udev == (struct usb_device *)NULL) {
> + smi2021_err("device is NULL\n");
> + return -EFAULT;
> + }
> +
> + smi2021_scan_usb(intf, udev);
> +
> + dev = kzalloc(sizeof(struct smi2021_dev), GFP_KERNEL);
> + if (dev == NULL)
> + return -ENOMEM;
> +
> + dev->udev = udev;
> + dev->dev = &intf->dev;
> + usb_set_intfdata(intf, dev);
> +
> + /* Initialize videobuf2 stuff */
> + rc = smi2021_vb2_setup(dev);
> + if (rc < 0)
> + goto free_err;
> +
> + spin_lock_init(&dev->buf_lock);
> + mutex_init(&dev->v4l2_lock);
> + mutex_init(&dev->vb_queue_lock);
> +
> + rc = v4l2_ctrl_handler_init(&dev->ctrl_handler, 0);
> + if (rc) {
> + smi2021_err("v4l2_ctrl_handler_init failed with: %d\n", rc);
> + goto free_err;
> + }
> +
> + dev->v4l2_dev.release = release_v4l2_dev;
> + dev->v4l2_dev.ctrl_handler = &dev->ctrl_handler;
> + rc = v4l2_device_register(dev->dev, &dev->v4l2_dev);
> + if (rc) {
> + smi2021_err("v4l2_device_register failed with %d\n", rc);
> + goto free_ctrl;
> + }
> +
> + smi2021_reset_device(dev);
> +
> + rc = smi2021_i2c_register(dev);
> + if (rc < 0)
> + goto unreg_v4l2;
> +
> + dev->sd_saa7113 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
> + "saa7113", 0, saa7113_addrs);
> +
> + smi2021_dbg("Driver version %s successfully loaded\n",
> + SMI2021_DRIVER_VERSION);
> +
> + v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0);
> + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
> +
> + rc = smi2021_snd_register(dev);
> + if (rc < 0)
> + goto unreg_i2c;
> +
> + rc = smi2021_video_register(dev);
> + if (rc < 0)
> + goto unreg_snd;
> +
> + return 0;
> +
> +unreg_snd:
> + smi2021_snd_unregister(dev);
> +unreg_i2c:
> + smi2021_i2c_unregister(dev);
> +unreg_v4l2:
> + v4l2_device_unregister(&dev->v4l2_dev);
> +free_ctrl:
> + v4l2_ctrl_handler_free(&dev->ctrl_handler);
> +free_err:
> + kfree(dev);
> +
> + return rc;
> +}
> +
> +static void smi2021_usb_disconnect(struct usb_interface *intf)
> +{
> + struct smi2021_dev *dev = usb_get_intfdata(intf);
> +
> + smi2021_dbg("Going for release!\n");
> +
> + usb_set_intfdata(intf, NULL);
> +
> + mutex_lock(&dev->vb_queue_lock);
> + mutex_lock(&dev->v4l2_lock);
> +
> + smi2021_uninit_isoc(dev);
> + smi2021_clear_queue(dev);
> +
> + video_unregister_device(&dev->vdev);
> + v4l2_device_disconnect(&dev->v4l2_dev);
> +
> + /* This way current users can detect device is gone */
> + dev->udev = NULL;
> +
> + mutex_unlock(&dev->v4l2_lock);
> + mutex_unlock(&dev->vb_queue_lock);
> +
> + smi2021_snd_unregister(dev);
> +
> + /*
> + * This calls release_v4l2_dev if it's the last reference.
> + * Otherwise, the release is postponed until there are no users left.
> + */
> + v4l2_device_put(&dev->v4l2_dev);
> +}
> +
> +/******************************************************************************/
> +/* */
> +/* MODULE - INIT & EXIT */
> +/* */
> +/******************************************************************************/
> +
> +struct usb_driver smi2021_usb_driver = {
> + .name = "smi2021",
> + .id_table = smi2021_usb_device_id_table,
> + .probe = smi2021_usb_probe,
> + .disconnect = smi2021_usb_disconnect
> +};
> +
> +module_usb_driver(smi2021_usb_driver);
> +
> --
> 1.8.1.1
>

--
Ezequiel García, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com

2013-03-15 12:28:04

by Ezequiel Garcia

[permalink] [raw]
Subject: Re: [RFC V1 3/8] smi2021: Add smi2021_i2c.c

On Thu, Mar 14, 2013 at 03:06:59PM +0100, Jon Arne Jørgensen wrote:
> This file is responsible for registering the device
> with the kernel i2c subsystem.
> v4l2 talks to the saa7113 chip of the device via i2c.
>
> Signed-off-by: Jon Arne Jørgensen <[email protected]>
> ---
> drivers/media/usb/smi2021/smi2021_i2c.c | 160 ++++++++++++++++++++++++++++++++
> 1 file changed, 160 insertions(+)
> create mode 100644 drivers/media/usb/smi2021/smi2021_i2c.c
>
> diff --git a/drivers/media/usb/smi2021/smi2021_i2c.c b/drivers/media/usb/smi2021/smi2021_i2c.c
> new file mode 100644
> index 0000000..5b6f3f5
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/smi2021_i2c.c
> @@ -0,0 +1,160 @@
> +/*******************************************************************************
> + * smi2021_i2c.c *
> + * *
> + * USB Driver for SMI2021 - EasyCAP *
> + * USB ID 1c88:003c *
> + * *
> + * *****************************************************************************
> + *
> + * Copyright 2011-2013 Jon Arne Jørgensen
> + * <jonjon.arnearne--a.t--gmail.com>
> + *
> + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> + *
> + * This file is part of SMI2021
> + * http://code.google.com/p/easycap-somagic-linux/
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + * This driver is heavily influensed by the STK1160 driver.
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + */
> +
> +#include "smi2021.h"
> +
> +/* The device will not return the chip_name on address 0x00.
> + * But the saa7115 i2c driver needs the chip id to match "f7113"
> + * if we want to use it,
> + * so we have to fake the return of this value
> + */

This comment style is wrong, check Documentation/CodingStyle.

BTW, Did you check the patches with checkpatch.pl?
(you can add checkpatch.pl as a git pre-commit hook, which I find very useful)

> +
> +static char chip_id[] = { 'x', 255, 55, 49, 49, 115, 0 };
> +static int id_ptr;
> +
> +static unsigned int i2c_debug;
> +module_param(i2c_debug, int, 0644);
> +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
> +
> +#define dprint_i2c(fmt, args...) \
> +do { \
> + if (i2c_debug) \
> + pr_debug("smi2021[i2c]::%s: " fmt, __func__, ##args); \
> +} while (0)
> +
> +
> +static int i2c_xfer(struct i2c_adapter *i2c_adap,
> + struct i2c_msg msgs[], int num)
> +{
> + struct smi2021_dev *dev = i2c_adap->algo_data;
> +
> + switch (num) {
> + case 2: { /* Read reg */

Do you need a local scope in here?

> + if (msgs[0].len != 1 || msgs[1].len != 1) {
> + dprint_i2c("both messages must be 1 byte\n");
> + goto err_out;
> +

I think you missed a closing } here. And an opening { below...

> + if ((msgs[1].flags & I2C_M_RD) != I2C_M_RD)
> + dprint_i2c("last message should have rd flag\n");
> + goto err_out;
> + }
> +
> + if (msgs[0].buf[0] == 0) {
> + msgs[1].buf[0] = chip_id[id_ptr];
> + if (chip_id[id_ptr] != 0)
> + id_ptr += 1;
> + } else {
> + smi2021_read_reg(dev, msgs[0].addr, msgs[0].buf[0],
> + msgs[1].buf);
> + }
> + break;
> + }
> + case 1: { /* Write reg */
> + if (msgs[0].len == 0) {
> + break;
> + } else if (msgs[0].len != 2) {
> + dprint_i2c("unsupported len\n");
> + goto err_out;
> + }
> + if (msgs[0].buf[0] == 0) {
> + /* We don't handle writing to addr 0x00 */
> + break;
> + }
> +
> + smi2021_write_reg(dev, msgs[0].addr, msgs[0].buf[0],
> + msgs[0].buf[1]);
> + break;
> + }
> + default: {
> + dprint_i2c("driver can only handle 1 or 2 messages\n");
> + goto err_out;
> + }
> + }
> + return num;
> +
> +err_out:
> + return -EOPNOTSUPP;
> +}
> +
> +static u32 functionality(struct i2c_adapter *adap)
> +{
> + return I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static struct i2c_algorithm algo = {
> + .master_xfer = i2c_xfer,
> + .functionality = functionality,
> +};
> +
> +static struct i2c_adapter adap_template = {
> + .owner = THIS_MODULE,
> + .name = "smi2021_easycap_dc60",
> + .algo = &algo,
> +};
> +
> +static struct i2c_client client_template = {
> + .name = "smi2021 internal",
> +};
> +
> +int smi2021_i2c_register(struct smi2021_dev *dev)
> +{
> + int rc;
> +
> + id_ptr = 0;

You don't need to initialize id_ptr here.

> +
> + dev->i2c_adap = adap_template;
> + dev->i2c_adap.dev.parent = dev->dev;
> + strcpy(dev->i2c_adap.name, "smi2021");
> + dev->i2c_adap.algo_data = dev;
> +
> + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
> +
> + rc = i2c_add_adapter(&dev->i2c_adap);
> + if (rc < 0) {
> + smi2021_err("can't add i2c adapter, errno: %d\n", rc);
> + return rc;
> + }
> +
> + dev->i2c_client = client_template;
> + dev->i2c_client.adapter = &dev->i2c_adap;
> +
> + return 0;
> +}
> +
> +int smi2021_i2c_unregister(struct smi2021_dev *dev)
> +{
> + i2c_del_adapter(&dev->i2c_adap);
> + return 0;
> +}
> --
> 1.8.1.1
>

--
Ezequiel García, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com

2013-03-15 12:33:30

by Ezequiel Garcia

[permalink] [raw]
Subject: Re: [RFC V1 4/8] smi2021: Add smi2021_v4l2.c

On Thu, Mar 14, 2013 at 03:07:00PM +0100, Jon Arne Jørgensen wrote:
> This file is responsible for registering the device with the v4l2 subsystem,
> and the communication with v4l2.
> Most of the v4l2 ioctls are just passed on to vidbuf2.
>
> Signed-off-by: Jon Arne Jørgensen <[email protected]>
> ---
> drivers/media/usb/smi2021/smi2021_v4l2.c | 566 +++++++++++++++++++++++++++++++
> 1 file changed, 566 insertions(+)
> create mode 100644 drivers/media/usb/smi2021/smi2021_v4l2.c
>
> diff --git a/drivers/media/usb/smi2021/smi2021_v4l2.c b/drivers/media/usb/smi2021/smi2021_v4l2.c
> new file mode 100644
> index 0000000..d402093
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/smi2021_v4l2.c
> @@ -0,0 +1,566 @@
> +/*******************************************************************************
> + * smi2021_v4l2.c *
> + * *
> + * USB Driver for smi2021 - EasyCap *
> + * USB ID 1c88:003c *
> + * *
> + * *****************************************************************************
> + *
> + * Copyright 2011-2013 Jon Arne Jørgensen
> + * <jonjon.arnearne--a.t--gmail.com>
> + *
> + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> + *
> + * This file is part of SMI2021
> + * http://code.google.com/p/easycap-somagic-linux/
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + * This driver is heavily influensed by the STK1160 driver.
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + */
> +
> +#include "smi2021.h"
> +
> +static struct smi2021_fmt format[] = {
> + {
> + .name = "16bpp YU2, 4:2:2, packed",
> + .fourcc = V4L2_PIX_FMT_UYVY,
> + .depth = 16,
> + }
> +};
> +
> +static const int inputs = 2;

You could use ARRAY_SIZE on inputs as well?
Hardcoded values in general are a good way to have future bugs.

> +static struct smi2021_input input[] = {
> + {
> + .name = "Composite",
> + .type = SAA7115_COMPOSITE0,
> + },
> + {
> + .name = "S-Video",
> + .type = SAA7115_SVIDEO1,
> + }
> +};
> +
> +static void smi2021_set_input(struct smi2021_dev *dev)
> +{
> + if (dev->udev == NULL)
> + return;
> +
> + if (dev->ctl_input >= inputs) {
> + smi2021_err("BUG: ctl_input to big!\n");
> + return;
> + }

Aren't you already checking for this? See vidioc_s_input.

> +
> + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
> + input[dev->ctl_input].type, 0, 0);
> +}
> +
> +static int smi2021_start_streaming(struct smi2021_dev *dev)
> +{
> + u8 data[2];
> + int i, rc = 0;
> + data[0] = 0x01;
> + data[1] = 0x05;
> +
> + if (!dev->udev)
> + return -ENODEV;
> +
> + dev->sync_state = HSYNC;
> + dev->buf_count = 0;
> +
> + if (mutex_lock_interruptible(&dev->v4l2_lock))
> + return -ERESTARTSYS;
> +
> + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1);
> +
> + /* The saa 7115 driver sets these wrong,
> + * this leads to sync issues.
> + * V_GATE1_START
> + * V_GATE1_STOP
> + * V_GATE1_MSB
> + * All these should be 0x00 for this device.
> + */
> + smi2021_write_reg(dev, 0x4a, 0x15, 0x00);
> + smi2021_write_reg(dev, 0x4a, 0x16, 0x00);
> + smi2021_write_reg(dev, 0x4a, 0x17, 0x00);

Oops, magic numbers!

> +
> + rc = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
> + 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> + 0x01, 0x00, data, sizeof(data), 1000);
> + if (rc < 0) {
> + smi2021_err("Could not start device!\n");
> + goto out_unlock;
> + }
> +
> + /* It's mandatory to set alt interface before allocating isoc buffer */
> + usb_set_interface(dev->udev, 0, 2);
> +
> + smi2021_write_reg(dev, 0, 0x1740, 0x1d);
> +
> + if (!dev->isoc_ctl.num_bufs) {
> + rc = smi2021_alloc_isoc(dev);
> + if (rc < 0)
> + goto out_stop_hw;
> + }
> +
> + /* submit urbs and enable IRQ */
> + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
> + rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_KERNEL);
> + if (rc) {
> + smi2021_err("cannot submit urb[%d] (%d)\n", i, rc);
> + goto out_uninit;
> + }
> + }
> +
> + mutex_unlock(&dev->v4l2_lock);
> +
> + smi2021_dbg("Streaming started!");
> + return 0;
> +
> +out_uninit:
> + smi2021_uninit_isoc(dev);
> +out_stop_hw:
> + usb_set_interface(dev->udev, 0, 0);
> + smi2021_clear_queue(dev);
> +
> +out_unlock:
> + mutex_unlock(&dev->v4l2_lock);
> +
> + return rc;
> +}
> +
> +/* Must be called with v4l2_lock hold */
> +static void smi2021_stop_hw(struct smi2021_dev *dev)
> +{
> + int rc = 0;
> + u8 data[] = { 0x01, 0x03 };
> +
> + if (!dev->udev)
> + return;
> +
> + usb_set_interface(dev->udev, 0, 0);
> +
> + rc = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
> + 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> + 0x01, 0x00, data, sizeof(data), 1000);
> + if (rc < 0)
> + smi2021_err("Could not stop device!\n");
> +
> + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
> +}
> +
> +static int smi2021_stop_streaming(struct smi2021_dev *dev)
> +{
> + /* HACK: Stop the audio subsystem,
> + * without this, the pcm middle-layer will hang waiting for more data.
> + *
> + * Is there a better way to do this?
> + */
> + if (dev->pcm_substream && dev->pcm_substream->runtime) {
> + struct snd_pcm_runtime *runtime = dev->pcm_substream->runtime;
> + if (runtime->status) {
> + runtime->status->state = SNDRV_PCM_STATE_DRAINING;
> + wake_up(&runtime->sleep);
> + }
> + }
> +
> + if (mutex_lock_interruptible(&dev->v4l2_lock))
> + return -ERESTARTSYS;
> +
> + smi2021_cancel_isoc(dev);
> + smi2021_free_isoc(dev);
> + smi2021_stop_hw(dev);
> + smi2021_clear_queue(dev);
> +
> + smi2021_dbg("Streaming stopped!\n");
> +
> + mutex_unlock(&dev->v4l2_lock);
> +
> + return 0;
> +}
> +
> +static struct v4l2_file_operations smi2021_fops = {
> + .owner = THIS_MODULE,
> + .open = v4l2_fh_open,
> + .release = vb2_fop_release,
> + .read = vb2_fop_read,
> + .poll = vb2_fop_poll,
> + .mmap = vb2_fop_mmap,
> + .unlocked_ioctl = video_ioctl2,
> +};
> +
> +/******************************************************************************/
> +/* */
> +/* Vidioc IOCTLS */
> +/* */
> +/******************************************************************************/
> +
> +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + if (f->index != 0)
> + return -EINVAL;
> +
> + strlcpy(f->description, format[f->index].name, sizeof(f->description));
> + f->pixelformat = format[f->index].fourcc;
> +
> + return 0;
> +}
> +
> +static int vidioc_querycap(struct file *file, void *priv,
> + struct v4l2_capability *cap)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> +
> + strcpy(cap->driver, "smi2021");
> + strcpy(cap->card, "smi2021");
> + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
> + cap->device_caps =
> + V4L2_CAP_VIDEO_CAPTURE |
> + V4L2_CAP_STREAMING |
> + V4L2_CAP_READWRITE;
> + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
> +
> + return 0;
> +}
> +
> +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> +
> + f->fmt.pix.pixelformat = dev->fmt->fourcc;
> + f->fmt.pix.width = dev->width;
> + f->fmt.pix.height = dev->height;
> + f->fmt.pix.field = V4L2_FIELD_INTERLACED;
> + f->fmt.pix.bytesperline = dev->width * 2;
> + f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline;
> + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
> + f->fmt.pix.priv = 0;
> +
> + return 0;
> +}
> +
> +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> +
> + f->fmt.pix.pixelformat = dev->fmt->fourcc;
> + f->fmt.pix.width = dev->width;
> + f->fmt.pix.height = dev->height;
> + f->fmt.pix.field = V4L2_FIELD_INTERLACED;
> + f->fmt.pix.bytesperline = dev->width * 2;
> + f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline;
> + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
> + f->fmt.pix.priv = 0;
> +
> + return 0;
> +}
> +
> +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> + struct vb2_queue *q = &dev->vb_vidq;
> +
> + if (vb2_is_busy(q))
> + return -EBUSY;
> +
> + vidioc_try_fmt_vid_cap(file, priv, f);
> + return 0;
> +}
> +
> +static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> +
> + v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm);
> + return 0;
> +}
> +
> +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> +
> + *norm = dev->norm;
> + return 0;
> +}
> +
> +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> + struct vb2_queue *q = &dev->vb_vidq;
> +
> + if (vb2_is_busy(q))
> + return -EBUSY;
> +
> + if (!dev->udev)
> + return -ENODEV;
> +
> + dev->norm = *norm;
> + if (dev->norm & V4L2_STD_525_60) {
> + dev->width = SMI2021_BYTES_PER_LINE / 2;
> + dev->height = SMI2021_NTSC_LINES;
> + } else if (dev->norm & V4L2_STD_625_50) {
> + dev->width = SMI2021_BYTES_PER_LINE / 2;
> + dev->height = SMI2021_PAL_LINES;
> + } else {
> + smi2021_err("Invalid standard\n");
> + return -EINVAL;
> + }
> +
> + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
> + return 0;
> +}
> +
> +static int vidioc_enum_input(struct file *file, void *priv,
> + struct v4l2_input *i)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> +
> + if (i->index >= inputs)
> + return -EINVAL;
> +
> + strlcpy(i->name, input[i->index].name, sizeof(i->name));
> + i->type = V4L2_INPUT_TYPE_CAMERA;
> + i->std = dev->vdev.tvnorms;
> + return 0;
> +}
> +
> +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> + *i = dev->ctl_input;
> + return 0;
> +}
> +
> +static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> +
> + if (i >= inputs)
> + return -EINVAL;
> +
> + dev->ctl_input = i;
> + smi2021_set_input(dev);
> +
> + return 0;
> +}
> +
> +static int vidioc_g_chip_ident(struct file *file, void *priv,
> + struct v4l2_dbg_chip_ident *chip)
> +{
> + switch (chip->match.type) {
> + case V4L2_CHIP_MATCH_HOST:
> + chip->ident = V4L2_IDENT_NONE;
> + chip->revision = 0;
> + return 0;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static const struct v4l2_ioctl_ops smi2021_ioctl_ops = {
> + .vidioc_querycap = vidioc_querycap,
> + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
> + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
> + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
> + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
> + .vidioc_querystd = vidioc_querystd,
> + .vidioc_g_std = vidioc_g_std,
> + .vidioc_s_std = vidioc_s_std,
> + .vidioc_enum_input = vidioc_enum_input,
> + .vidioc_g_input = vidioc_g_input,
> + .vidioc_s_input = vidioc_s_input,
> +
> + /* vb2 handle these */
> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> + .vidioc_querybuf = vb2_ioctl_querybuf,
> + .vidioc_qbuf = vb2_ioctl_qbuf,
> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> + .vidioc_streamon = vb2_ioctl_streamon,
> + .vidioc_streamoff = vb2_ioctl_streamoff,
> +
> + .vidioc_log_status = v4l2_ctrl_log_status,
> + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> + .vidioc_g_chip_ident = vidioc_g_chip_ident,
> +
> +};
> +
> +/******************************************************************************/
> +/* */
> +/* Videobuf2 operations */
> +/* */
> +/******************************************************************************/
> +static int queue_setup(struct vb2_queue *vq,
> + const struct v4l2_format *v4l2_fmt,
> + unsigned int *nbuffers, unsigned int *nplanes,
> + unsigned int sizes[], void *alloc_ctxs[])
> +{
> + struct smi2021_dev *dev = vb2_get_drv_priv(vq);
> + unsigned long size;
> +
> + size = dev->width * dev->height * 2;
> +
> + *nbuffers = clamp_t(unsigned int, *nbuffers, 2, 4);
> +
> + /* Packed color format */
> + *nplanes = 1;
> + sizes[0] = size;
> +
> + return 0;
> +}
> +
> +static void buffer_queue(struct vb2_buffer *vb)
> +{
> + unsigned long flags;
> + struct smi2021_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
> + struct smi2021_buffer *buf = container_of(vb, struct smi2021_buffer,
> + vb);
> +
> + spin_lock_irqsave(&dev->buf_lock, flags);
> + if (!dev->udev) {
> + /*
> + * If the device is disconnected return the buffer to userspace
> + * directly. The next QBUF call will fail with -ENODEV.
> + */
> + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> + } else {
> + buf->mem = vb2_plane_vaddr(vb, 0);
> + buf->length = vb2_plane_size(vb, 0);
> +
> + buf->pos = 0;
> + buf->trc_av = 0;
> + buf->in_blank = true;
> + buf->second_field = false;
> +
> + if (buf->length < dev->width * dev->height * 2)
> + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> + else
> + list_add_tail(&buf->list, &dev->avail_bufs);
> + }
> + spin_unlock_irqrestore(&dev->buf_lock, flags);
> +}
> +
> +static int start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> + struct smi2021_dev *dev = vb2_get_drv_priv(vq);
> + return smi2021_start_streaming(dev);
> +}
> +
> +static int stop_streaming(struct vb2_queue *vq)
> +{
> + struct smi2021_dev *dev = vb2_get_drv_priv(vq);
> + return smi2021_stop_streaming(dev);
> +}
> +
> +static struct vb2_ops smi2021_video_qops = {
> + .queue_setup = queue_setup,
> + .buf_queue = buffer_queue,
> + .start_streaming = start_streaming,
> + .stop_streaming = stop_streaming,
> + .wait_prepare = vb2_ops_wait_prepare,
> + .wait_finish = vb2_ops_wait_finish,
> +};
> +
> +static struct video_device v4l2_template = {
> + .name = "easycap_smi2021_dc60",
> + .tvnorms = V4L2_STD_625_50 | V4L2_STD_525_60,
> + .fops = &smi2021_fops,
> + .ioctl_ops = &smi2021_ioctl_ops,
> + .release = video_device_release_empty,
> +};
> +
> +/* Must be called with both v4l2_lock and vb_queue_lock hold */
> +void smi2021_clear_queue(struct smi2021_dev *dev)
> +{
> + struct smi2021_buffer *buf;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&dev->buf_lock, flags);
> + while (!list_empty(&dev->avail_bufs)) {
> + buf = list_first_entry(&dev->avail_bufs,
> + struct smi2021_buffer, list);
> + list_del(&buf->list);
> + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> + }
> + dev->isoc_ctl.buf = NULL;
> + spin_unlock_irqrestore(&dev->buf_lock, flags);
> +}
> +
> +int smi2021_vb2_setup(struct smi2021_dev *dev)
> +{
> + int rc;
> + struct vb2_queue *q;
> +
> + q = &dev->vb_vidq;
> + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
> + q->drv_priv = dev;
> + q->buf_struct_size = sizeof(struct smi2021_buffer);
> + q->ops = &smi2021_video_qops;
> + q->mem_ops = &vb2_vmalloc_memops;
> +
> + rc = vb2_queue_init(q);
> + if (rc < 0)
> + return rc;
> +
> + /* Initialize video dma queue */
> + INIT_LIST_HEAD(&dev->avail_bufs);
> +
> + return 0;
> +}
> +
> +int smi2021_video_register(struct smi2021_dev *dev)
> +{
> + int rc;
> +
> + dev->vdev = v4l2_template;
> + dev->vdev.debug = 0;
> + dev->vdev.queue = &dev->vb_vidq;
> +
> + dev->vdev.lock = &dev->v4l2_lock;
> + dev->vdev.queue->lock = &dev->vb_queue_lock;
> +
> + dev->vdev.v4l2_dev = &dev->v4l2_dev;
> + set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags);
> +
> + /* PAL is default */
> + dev->norm = V4L2_STD_PAL;
> + dev->width = SMI2021_BYTES_PER_LINE / 2;
> + dev->height = SMI2021_PAL_LINES;
> +
> + dev->fmt = &format[0];
> +
> + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
> + smi2021_set_input(dev);
> +
> + video_set_drvdata(&dev->vdev, dev);
> + rc = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1);
> + if (rc < 0) {
> + smi2021_err("video_register_device failed %d\n", rc);
> + return rc;
> + }
> +
> + v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
> + video_device_node_name(&dev->vdev));
> +
> + return 0;
> +}
> --
> 1.8.1.1
>

--
Ezequiel García, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com

2013-03-15 12:40:44

by Ezequiel Garcia

[permalink] [raw]
Subject: Re: [RFC V1 5/8] smi2021: Add smi2021_video.c

On Thu, Mar 14, 2013 at 03:07:01PM +0100, Jon Arne Jørgensen wrote:
> This file is responsible for all communication with the video hardware
> and also starting and stopping the capture.
>
> It also contains the setup and handling of the usb ISOCHRONOUS transfers.
>
> Signed-off-by: Jon Arne Jørgensen <[email protected]>
> ---
> drivers/media/usb/smi2021/smi2021_video.c | 543 ++++++++++++++++++++++++++++++
> 1 file changed, 543 insertions(+)
> create mode 100644 drivers/media/usb/smi2021/smi2021_video.c
>
> diff --git a/drivers/media/usb/smi2021/smi2021_video.c b/drivers/media/usb/smi2021/smi2021_video.c
> new file mode 100644
> index 0000000..48f3e80
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/smi2021_video.c
> @@ -0,0 +1,543 @@
> +/*******************************************************************************
> + * smi2021_video.c *
> + * *
> + * USB Driver for SMI2021 - EasyCAP *
> + * USB ID 1c88:003c *
> + * *
> + * *****************************************************************************
> + *
> + * Copyright 2011-2013 Jon Arne Jørgensen
> + * <jonjon.arnearne--a.t--gmail.com>
> + *
> + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> + *
> + * This file is part of SMI2021x
> + * http://code.google.com/p/easycap-somagic-linux/
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + * This driver is heavily influensed by the STK1160 driver.
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + */
> +
> +#include "smi2021.h"
> +
> +static void print_usb_err(struct smi2021_dev *dev, int packet, int status)
> +{
> + char *errmsg;
> +
> + switch (status) {
> + case -ENOENT:
> + errmsg = "unlinked synchronuously";
> + break;
> + case -ECONNRESET:
> + errmsg = "unlinked asynchronuously";
> + break;
> + case -ENOSR:
> + errmsg = "Buffer error (overrun)";
> + break;
> + case -EPIPE:
> + errmsg = "Stalled (device not responding)";
> + break;
> + case -EOVERFLOW:
> + errmsg = "Babble (bad cable?)";
> + break;
> + case -EPROTO:
> + errmsg = "Bit-stuff error (bad cable?)";
> + break;
> + case -EILSEQ:
> + errmsg = "CRC/Timeout (could be anything)";
> + break;
> + case -ETIME:
> + errmsg = "Device does not respond";
> + break;
> + default:
> + errmsg = "Unknown";
> + }
> +
> + if (packet < 0) {
> + printk_ratelimited(KERN_WARNING "Urb status %d [%s]\n",
> + status, errmsg);
> + } else {
> + printk_ratelimited(KERN_INFO "URB packet %d, status %d [%s]\n",
> + packet, status, errmsg);
> + }
> +}
> +
> +static struct smi2021_buffer *smi2021_next_buffer(struct smi2021_dev *dev)
> +{
> + struct smi2021_buffer *buf = NULL;
> + unsigned long flags = 0;
> +
> + BUG_ON(dev->isoc_ctl.buf);
> +

You should replace BUG_ON with WARN_ON. Linus' words:
http://permalink.gmane.org/gmane.linux.kernel/1347333

> + spin_lock_irqsave(&dev->buf_lock, flags);
> + if (!list_empty(&dev->avail_bufs)) {
> + buf = list_first_entry(&dev->avail_bufs, struct smi2021_buffer,
> + list);
> + list_del(&buf->list);
> + }
> + spin_unlock_irqrestore(&dev->buf_lock, flags);
> +
> + return buf;
> +}
> +
> +static void smi2021_buffer_done(struct smi2021_dev *dev)
> +{
> + struct smi2021_buffer *buf = dev->isoc_ctl.buf;
> +
> + dev->buf_count++;
> +
> + buf->vb.v4l2_buf.sequence = dev->buf_count >> 1;
> + buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
> + buf->vb.v4l2_buf.bytesused = buf->pos;
> + do_gettimeofday(&buf->vb.v4l2_buf.timestamp);
> +
> + vb2_set_plane_payload(&buf->vb, 0, buf->pos);
> + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
> +
> + dev->isoc_ctl.buf = NULL;
> +}
> +
> +static void copy_video(struct smi2021_dev *dev, u8 p)
> +{
> + struct smi2021_buffer *buf = dev->isoc_ctl.buf;
> +
> + int lines_per_field = dev->height / 2;
> + int line = 0;
> + int pos_in_line = 0;
> + unsigned int offset = 0;
> + u8 *dst;
> +
> + if (buf == NULL)
> + return;
> +
> + if (buf->in_blank)
> + return;
> +
> + if (buf->pos >= buf->length) {
> + if (buf->second_field == 0) {
> + /* We are probably trying to capture from
> + * a unconnected input
> + */
> + smi2021_buffer_done(dev);
> + } else {
> + printk_ratelimited(KERN_WARNING
> + "Buffer overflow!, max: %d bytes, av_lines_found: %d, second_field: %d\n",
> + buf->length, buf->trc_av,
> + buf->second_field);
> + }
> + return;
> + }
> +
> + pos_in_line = buf->pos % SMI2021_BYTES_PER_LINE;
> + line = buf->pos / SMI2021_BYTES_PER_LINE;
> + if (line >= lines_per_field)
> + line -= lines_per_field;
> +
> + if (line != buf->trc_av - 1) {
> + /* Keep video synchronized.
> + * The device will sometimes give us to many bytes
> + * for a line, before we get a new TRC.
> + * We just drop these bytes */
> + return;
> + }
> +
> + if (buf->second_field)
> + offset += SMI2021_BYTES_PER_LINE;
> +
> + offset += (SMI2021_BYTES_PER_LINE * line * 2) + pos_in_line;
> +
> + /* Will this ever happen? */
> + if (offset >= buf->length) {
> + printk_ratelimited(KERN_INFO
> + "Offset calculation error, field: %d, line: %d, pos_in_line: %d\n",
> + buf->second_field, line, pos_in_line);
> + return;
> + }
> +
> + dst = buf->mem + offset;
> + *dst = p;
> + buf->pos++;
> +}
> +
> +#define is_sav(trc) \
> + ((trc & SMI2021_TRC_EAV) == 0x00)
> +#define is_field2(trc) \
> + ((trc & SMI2021_TRC_FIELD_2) == SMI2021_TRC_FIELD_2)
> +#define is_active_video(trc) \
> + ((trc & SMI2021_TRC_VBI) == 0x00)
> +/*
> + * Parse the TRC.
> + * Grab a new buffer from the queue if don't have one
> + * and we are recieving the start of a video frame.
> + *
> + * Mark video buffers as done if we have one full frame.
> + */
> +static void parse_trc(struct smi2021_dev *dev, u8 trc)
> +{
> + struct smi2021_buffer *buf = dev->isoc_ctl.buf;
> + int lines_per_field = dev->height / 2;
> + int line = 0;
> +
> + if (buf == NULL) {
> + if (!is_sav(trc))
> + return;
> +
> + if (!is_active_video(trc))
> + return;
> +
> + if (is_field2(trc))
> + return;
> +
> + buf = smi2021_next_buffer(dev);
> + if (buf == NULL)
> + return;
> +
> + dev->isoc_ctl.buf = buf;
> + }
> +
> + if (is_sav(trc)) {
> + /* Start of VBI or ACTIVE VIDEO */
> + if (is_active_video(trc)) {
> + buf->in_blank = false;
> + buf->trc_av++;
> + } else {
> + /* VBI */
> + buf->in_blank = true;
> + }
> +
> + if (!buf->second_field && is_field2(trc)) {
> + line = buf->pos / SMI2021_BYTES_PER_LINE;
> + if (line < lines_per_field)
> + goto buf_done;
> +
> + buf->second_field = true;
> + buf->trc_av = 0;
> + }
> +
> + if (buf->second_field && !is_field2(trc))
> + goto buf_done;
> + } else {
> + /* End of VBI or ACTIVE VIDEO */
> + buf->in_blank = true;
> + }
> +
> + return;
> +
> +buf_done:
> + smi2021_buffer_done(dev);
> +}
> +
> +/*
> + * Scan the saa7113 Active video data.
> + * This data is:
> + * 4 bytes header (0xff 0x00 0x00 [TRC/SAV])
> + * 1440 bytes of UYUV Video data
> + * 4 bytes footer (0xff 0x00 0x00 [TRC/EAV])
> + *
> + * TRC = Time Reference Code.
> + * SAV = Start Active Video.
> + * EAV = End Active Video.
> + * This is described in the saa7113 datasheet.
> + */
> +static void parse_video(struct smi2021_dev *dev, u8 *p, int len)
> +{
> + int i;
> +
> + for (i = 0; i < len; i++) {
> + switch (dev->sync_state) {
> + case HSYNC:
> + if (p[i] == 0xff)
> + dev->sync_state = SYNCZ1;
> + else
> + copy_video(dev, p[i]);
> + break;
> + case SYNCZ1:
> + if (p[i] == 0x00) {
> + dev->sync_state = SYNCZ2;
> + } else {
> + dev->sync_state = HSYNC;
> + copy_video(dev, 0xff);
> + copy_video(dev, p[i]);
> + }
> + break;
> + case SYNCZ2:
> + if (p[i] == 0x00) {
> + dev->sync_state = TRC;
> + } else {
> + dev->sync_state = HSYNC;
> + copy_video(dev, 0xff);
> + copy_video(dev, 0x00);
> + copy_video(dev, p[i]);
> + }
> + break;
> + case TRC:
> + dev->sync_state = HSYNC;
> + parse_trc(dev, p[i]);
> + break;
> + }
> + }
> +
> +}
> +/*
> + *
> + * The device delivers data in chunks of 0x400 bytes.
> + * The four first bytes is a magic header to identify the chunks.
> + * 0xaa 0xaa 0x00 0x00 = saa7113 Active Video Data
> + * 0xaa 0xaa 0x00 0x01 = PCM - 24Bit 2 Channel audio data
> + */
> +static void process_packet(struct smi2021_dev *dev, u8 *p, int len)
> +{
> + int i;
> + u32 *header;
> +
> + if (len % 0x400 != 0) {
> + printk_ratelimited(KERN_INFO "smi2021::%s: len: %d\n",
> + __func__, len);
> + return;
> + }
> +
> + for (i = 0; i < len; i += 0x400) {
> + header = (u32 *)(p + i);
> + switch (__cpu_to_be32(*header)) {
> + case 0xaaaa0000: {
> + parse_video(dev, p+i+4, 0x400-4);
> + break;
> + }
> + case 0xaaaa0001: {
> + smi2021_audio(dev, p+i+4, 0x400-4);
> + break;
> + }
> + default: {
> + /* Nothing */
> + }
> + }
> + }
> +}
> +
> +/*
> + * Interrupt called by URB callback
> + */
> +static void smi2021_isoc_isr(struct urb *urb)
> +{
> + int i, rc, status, len;
> + struct smi2021_dev *dev = urb->context;
> + u8 *p;
> +
> + switch (urb->status) {
> + case 0:
> + break;
> + case -ECONNRESET: /* kill */
> + case -ENOENT:
> + case -ESHUTDOWN:
> + /* uvc driver frees the queue here */
> + return;
> + default:
> + smi2021_err("urb error! status %d\n", urb->status);
> + return;
> + }
> +
> + if (urb->status < 0)
> + print_usb_err(dev, -1, status);
> +
> + if (dev == NULL) {
> + smi2021_warn("called with null device\n");
> + return;
> + }
> +
> + for (i = 0; i < urb->number_of_packets; i++) {
> +
> + status = urb->iso_frame_desc[i].status;
> + if (status == -18) {
> + /* This seems to happen when the device
> + * trying to stream from an unconnected input
> + * */
> + continue;
> + }
> +
> + if (status < 0) {
> + print_usb_err(dev, i, status);
> + continue;
> + }
> +
> + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
> + len = urb->iso_frame_desc[i].actual_length;
> + process_packet(dev, p, len);
> + }
> +
> + for (i = 0; i < urb->number_of_packets; i++) {
> + urb->iso_frame_desc[i].status = 0;
> + urb->iso_frame_desc[i].actual_length = 0;
> + }
> +
> + rc = usb_submit_urb(urb, GFP_ATOMIC);
> + if (rc)
> + smi2021_err("urb re-submit failed (%d)\n", rc);
> +}
> +
> +/*
> + * Cancel urbs
> + * This function can not be called in atomic context
> + */
> +void smi2021_cancel_isoc(struct smi2021_dev *dev)
> +{
> + int i, num_bufs = dev->isoc_ctl.num_bufs;
> + if (!num_bufs)
> + return;
> +
> + smi2021_dbg("killing %d urbs...\n", num_bufs);
> +
> + for (i = 0; i < num_bufs; i++)
> + usb_kill_urb(dev->isoc_ctl.urb[i]);
> +
> + smi2021_dbg("all urbs killed\n");
> +
> +}
> +
> +/*
> + * Releases urb and transfer buffers
> + * Obviously, associated urb must be killed before releasing it
> + */
> +void smi2021_free_isoc(struct smi2021_dev *dev)
> +{
> + struct urb *urb;
> + int i, num_bufs = dev->isoc_ctl.num_bufs;
> +
> + smi2021_dbg("freeing %d urb buffers...\n", num_bufs);
> +
> + for (i = 0; i < num_bufs; i++) {
> + urb = dev->isoc_ctl.urb[i];
> + if (urb) {
> + if (dev->isoc_ctl.transfer_buffer[i]) {
> +#ifndef CONFIG_DMA_NONCOHERENT
> + usb_free_coherent(dev->udev,
> + urb->transfer_buffer_length,
> + dev->isoc_ctl.transfer_buffer[i],
> + urb->transfer_dma);
> +#else
> + kfree(dev->isoc_ctl.transfer_buffer[i]);
> +#endif
> + }
> + 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;
> +
> + smi2021_dbg("all urb buffers freed\n");
> +}
> +
> +/*
> + * Helper for canceling and freeing urbs
> + * This function can not be called in atomic context
> + */
> +void smi2021_uninit_isoc(struct smi2021_dev *dev)
> +{
> + smi2021_cancel_isoc(dev);
> + smi2021_free_isoc(dev);
> +}
> +
> +
> +int smi2021_alloc_isoc(struct smi2021_dev *dev)
> +{
> + struct urb *urb;
> + int i, j, k, sb_size, max_packets, num_bufs;
> +
> + if (dev->isoc_ctl.num_bufs)
> + smi2021_uninit_isoc(dev);
> +
> + num_bufs = SMI2021_ISOC_BUFS;
> + max_packets = SMI2021_ISOC_PACKETS;
> + sb_size = max_packets * SMI2021_MAX_PKT_SIZE;
> +
> + dev->isoc_ctl.buf = NULL;
> + dev->isoc_ctl.max_pkt_size = SMI2021_MAX_PKT_SIZE;
> + dev->isoc_ctl.urb = kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
> + if (!dev->isoc_ctl.urb) {
> + smi2021_err("out of memory for urb array\n");
> + return -ENOMEM;
> + }
> +
> + dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *) * num_bufs,
> + GFP_KERNEL);
> + if (!dev->isoc_ctl.transfer_buffer) {
> + smi2021_err("out of memory for usb transfer\n");
> + kfree(dev->isoc_ctl.urb);
> + return -ENOMEM;
> + }
> +
> + for (i = 0; i < num_bufs; i++) {
> + urb = usb_alloc_urb(max_packets, GFP_KERNEL);
> + if (!urb) {
> + smi2021_err("connot allocate urb[%d]\n", i);
> + goto free_i_bufs;
> + }
> + dev->isoc_ctl.urb[i] = urb;
> +#ifndef CONFIG_DMA_NONCOHERENT
> + dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(
> + dev->udev, sb_size, GFP_KERNEL,
> + &urb->transfer_dma);
> +#else
> + dev->isoc_ctl.transfer_buffer[i] = kmalloc(sb_size,
> + GFP_KERNEL);
> +#endif
> + if (!dev->isoc_ctl.transfer_buffer[i]) {
> + smi2021_err("cannot alloc %d bytes for tx[%d] buffer",
> + sb_size, i);
> + goto free_i_bufs;
> + }
> + /* Do not leak kernel data */
> + memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
> +
> + urb->dev = dev->udev;
> + urb->pipe = usb_rcvisocpipe(dev->udev, SMI2021_ISOC_EP);
> + urb->transfer_buffer = dev->isoc_ctl.transfer_buffer[i];
> + urb->transfer_buffer_length = sb_size;
> + urb->complete = smi2021_isoc_isr;
> + urb->context = dev;
> + urb->interval = 1;
> + urb->start_frame = 0;
> + urb->number_of_packets = max_packets;
> +#ifndef CONFIG_DMA_NONCOHERENT
> + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
> +#else
> + urb->transfer_flags = URB_ISO_ASAP;
> +#endif
> + 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;
> + }
> + }
> + smi2021_dbg("urbs allocated\n");
> + dev->isoc_ctl.num_bufs = num_bufs;
> + return 0;
> +
> +free_i_bufs:
> + dev->isoc_ctl.num_bufs = i+1;
> + smi2021_free_isoc(dev);
> + return -ENOMEM;
> +}
> --
> 1.8.1.1
>

--
Ezequiel García, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com

2013-03-17 19:55:55

by Jon Arne Jørgensen

[permalink] [raw]
Subject: Re: [RFC V1 3/8] smi2021: Add smi2021_i2c.c

On Fri, Mar 15, 2013 at 09:27:54AM -0300, Ezequiel Garcia wrote:
> On Thu, Mar 14, 2013 at 03:06:59PM +0100, Jon Arne Jørgensen wrote:
> > This file is responsible for registering the device
> > with the kernel i2c subsystem.
> > v4l2 talks to the saa7113 chip of the device via i2c.
> >
> > Signed-off-by: Jon Arne Jørgensen <[email protected]>
> > ---
> > drivers/media/usb/smi2021/smi2021_i2c.c | 160 ++++++++++++++++++++++++++++++++
> > 1 file changed, 160 insertions(+)
> > create mode 100644 drivers/media/usb/smi2021/smi2021_i2c.c
> >
> > diff --git a/drivers/media/usb/smi2021/smi2021_i2c.c b/drivers/media/usb/smi2021/smi2021_i2c.c
> > new file mode 100644
> > index 0000000..5b6f3f5
> > --- /dev/null
> > +++ b/drivers/media/usb/smi2021/smi2021_i2c.c
> > @@ -0,0 +1,160 @@
> > +/*******************************************************************************
> > + * smi2021_i2c.c *
> > + * *
> > + * USB Driver for SMI2021 - EasyCAP *
> > + * USB ID 1c88:003c *
> > + * *
> > + * *****************************************************************************
> > + *
> > + * Copyright 2011-2013 Jon Arne Jørgensen
> > + * <jonjon.arnearne--a.t--gmail.com>
> > + *
> > + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> > + *
> > + * This file is part of SMI2021
> > + * http://code.google.com/p/easycap-somagic-linux/
> > + *
> > + * This program is free software: you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation, either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> > + *
> > + * This driver is heavily influensed by the STK1160 driver.
> > + * Copyright (C) 2012 Ezequiel Garcia
> > + * <elezegarcia--a.t--gmail.com>
> > + *
> > + */
> > +
> > +#include "smi2021.h"
> > +
> > +/* The device will not return the chip_name on address 0x00.
> > + * But the saa7115 i2c driver needs the chip id to match "f7113"
> > + * if we want to use it,
> > + * so we have to fake the return of this value
> > + */
>
> This comment style is wrong, check Documentation/CodingStyle.
>
According to Documentation/CodingStyle line 446, this is the correct
coding style for multiline comments?

Or is there something I've misunderstood?

> BTW, Did you check the patches with checkpatch.pl?
> (you can add checkpatch.pl as a git pre-commit hook, which I find very useful)
>
I checked all patches with checkpatch.pl, and removed all sensible
warnings

> > +
> > +static char chip_id[] = { 'x', 255, 55, 49, 49, 115, 0 };
> > +static int id_ptr;
> > +
> > +static unsigned int i2c_debug;
> > +module_param(i2c_debug, int, 0644);
> > +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
> > +
> > +#define dprint_i2c(fmt, args...) \
> > +do { \
> > + if (i2c_debug) \
> > + pr_debug("smi2021[i2c]::%s: " fmt, __func__, ##args); \
> > +} while (0)
> > +
> > +
> > +static int i2c_xfer(struct i2c_adapter *i2c_adap,
> > + struct i2c_msg msgs[], int num)
> > +{
> > + struct smi2021_dev *dev = i2c_adap->algo_data;
> > +
> > + switch (num) {
> > + case 2: { /* Read reg */
>
> Do you need a local scope in here?
>

I Will look into this, but I probably don't :)

> > + if (msgs[0].len != 1 || msgs[1].len != 1) {
> > + dprint_i2c("both messages must be 1 byte\n");
> > + goto err_out;
> > +
>
> I think you missed a closing } here. And an opening { below...
>

Yeah, thx.
Will fix in next patch.

> > + if ((msgs[1].flags & I2C_M_RD) != I2C_M_RD)
> > + dprint_i2c("last message should have rd flag\n");
> > + goto err_out;
> > + }
> > +
> > + if (msgs[0].buf[0] == 0) {
> > + msgs[1].buf[0] = chip_id[id_ptr];
> > + if (chip_id[id_ptr] != 0)
> > + id_ptr += 1;
> > + } else {
> > + smi2021_read_reg(dev, msgs[0].addr, msgs[0].buf[0],
> > + msgs[1].buf);
> > + }
> > + break;
> > + }
> > + case 1: { /* Write reg */
> > + if (msgs[0].len == 0) {
> > + break;
> > + } else if (msgs[0].len != 2) {
> > + dprint_i2c("unsupported len\n");
> > + goto err_out;
> > + }
> > + if (msgs[0].buf[0] == 0) {
> > + /* We don't handle writing to addr 0x00 */
> > + break;
> > + }
> > +
> > + smi2021_write_reg(dev, msgs[0].addr, msgs[0].buf[0],
> > + msgs[0].buf[1]);
> > + break;
> > + }
> > + default: {
> > + dprint_i2c("driver can only handle 1 or 2 messages\n");
> > + goto err_out;
> > + }
> > + }
> > + return num;
> > +
> > +err_out:
> > + return -EOPNOTSUPP;
> > +}
> > +
> > +static u32 functionality(struct i2c_adapter *adap)
> > +{
> > + return I2C_FUNC_SMBUS_EMUL;
> > +}
> > +
> > +static struct i2c_algorithm algo = {
> > + .master_xfer = i2c_xfer,
> > + .functionality = functionality,
> > +};
> > +
> > +static struct i2c_adapter adap_template = {
> > + .owner = THIS_MODULE,
> > + .name = "smi2021_easycap_dc60",
> > + .algo = &algo,
> > +};
> > +
> > +static struct i2c_client client_template = {
> > + .name = "smi2021 internal",
> > +};
> > +
> > +int smi2021_i2c_register(struct smi2021_dev *dev)
> > +{
> > + int rc;
> > +
> > + id_ptr = 0;
>
> You don't need to initialize id_ptr here.
>

It's a loop counter used in the i2c_xfer function.
and must be set to 0 when a device is attached.

After looking at this code again, I realize that it's a mess, so I
will rewrite this part before I resubmit the patch.

> > +
> > + dev->i2c_adap = adap_template;
> > + dev->i2c_adap.dev.parent = dev->dev;
> > + strcpy(dev->i2c_adap.name, "smi2021");
> > + dev->i2c_adap.algo_data = dev;
> > +
> > + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
> > +
> > + rc = i2c_add_adapter(&dev->i2c_adap);
> > + if (rc < 0) {
> > + smi2021_err("can't add i2c adapter, errno: %d\n", rc);
> > + return rc;
> > + }
> > +
> > + dev->i2c_client = client_template;
> > + dev->i2c_client.adapter = &dev->i2c_adap;
> > +
> > + return 0;
> > +}
> > +
> > +int smi2021_i2c_unregister(struct smi2021_dev *dev)
> > +{
> > + i2c_del_adapter(&dev->i2c_adap);
> > + return 0;
> > +}
> > --
> > 1.8.1.1
> >
>
> --
> Ezequiel García, Free Electrons
> Embedded Linux, Kernel and Android Engineering
> http://free-electrons.com

2013-03-17 19:58:40

by Jon Arne Jørgensen

[permalink] [raw]
Subject: Re: [RFC V1 0/8] Add a driver for somagic smi2021

On Fri, Mar 15, 2013 at 09:08:58AM -0300, Ezequiel Garcia wrote:
> On Thu, Mar 14, 2013 at 03:06:56PM +0100, Jon Arne Jørgensen wrote:
> > This patch-set will add a driver for the Somagic SMI2021 chip.
> >
> > This chip is found inside different usb video-capture devices.
> > Most of them are branded as EasyCap, but there also seems to be
> > some other brands selling devices with this chip.
> >
> > This driver is split into two modules, where one is called smi2021-bootloader,
> > and the other is just called smi2021.
> >
> > The bootloader is responsible for the upload of a firmware that is needed by some
> > versions of the devices.
> >
> > All Somagic devices that need firmware seems to identify themselves
> > with the usb product id 0x0007. There is no way for the kernel to know
> > what firmware to upload to the device without user interaction.
> >
> > If there is only one firmware present on the computer, the kernel
> > will upload that firmware to any device that identifies as 0x0007.
> > If there are multiple Somagic firmwares present, the user will have to pass
> > a module parameter to the smi2021-bootloader module to tell what firmware to use.
> >
>
> Nice job!
>
Thanks :)

> I have some minor comments on each patch, but also I don't agree
> with the patch splitting: what's the point in splitting and sending
> one patch per file?
>
> It doesn't make it any easier to review, so why don't you just
> send one patch: "Introduce smi2021 driver"?
>
> The rule is one patch per change, and I believe this whole patchset
> is just one change: adding a new driver.
>

I think I read another patch to this mailinglist, where someone was told
to split his patch into one mail per file, but I can't find that thread
now :)

I will send the next version as a single mail, and see what happens...

> --
> Ezequiel García, Free Electrons
> Embedded Linux, Kernel and Android Engineering
> http://free-electrons.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-03-17 20:11:02

by Jon Arne Jørgensen

[permalink] [raw]
Subject: Re: [RFC V1 2/8] smi2021: Add smi2021_main.c

On Fri, Mar 15, 2013 at 09:20:58AM -0300, Ezequiel Garcia wrote:
> On Thu, Mar 14, 2013 at 03:06:58PM +0100, Jon Arne Jørgensen wrote:
> > This is the core of the smi2021 module.
> > It will register the module with the kernel, and register the
> > usb probe function.
> >
> > Signed-off-by: Jon Arne Jørgensen <[email protected]>
> > ---
> > drivers/media/usb/smi2021/smi2021_main.c | 339 +++++++++++++++++++++++++++++++
> > 1 file changed, 339 insertions(+)
> > create mode 100644 drivers/media/usb/smi2021/smi2021_main.c
> >
> > diff --git a/drivers/media/usb/smi2021/smi2021_main.c b/drivers/media/usb/smi2021/smi2021_main.c
> > new file mode 100644
> > index 0000000..cc600e7
> > --- /dev/null
> > +++ b/drivers/media/usb/smi2021/smi2021_main.c
> > @@ -0,0 +1,339 @@
> > +/*******************************************************************************
> > + * smi2021_main.c *
> > + * *
> > + * USB Driver for SMI2021 - EasyCAP *
> > + * USB ID 1c88:003c *
> > + * *
> > + * *****************************************************************************
> > + *
> > + * Copyright 2011-2013 Jon Arne Jørgensen
> > + * <jonjon.arnearne--a.t--gmail.com>
> > + *
> > + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> > + *
> > + * This file is part of SMI2021
> > + * http://code.google.com/p/easycap-somagic-linux/
> > + *
> > + * This program is free software: you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation, either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> > + *
> > + * This driver is heavily influensed by the STK1160 driver.
> > + * Copyright (C) 2012 Ezequiel Garcia
> > + * <elezegarcia--a.t--gmail.com>
> > + *
> > + */
> > +
> > +#include "smi2021.h"
> > +
> > +#define VENDOR_ID 0x1c88
> > +
> > +static unsigned int imput;
> > +module_param(imput, int, 0644);
>
> s/imput/input
>

Fixed!

> > +MODULE_PARM_DESC(input, "Set default input");
> > +
> > +MODULE_LICENSE("GPL");
> > +MODULE_AUTHOR("Jon Arne Jørgensen <jonjon.arnearne--a.t--gmail.com>");
> > +MODULE_DESCRIPTION("SMI2021 - EasyCap");
> > +MODULE_VERSION(SMI2021_DRIVER_VERSION);
> > +
> > +
> > +struct usb_device_id smi2021_usb_device_id_table[] = {
> > + { USB_DEVICE(VENDOR_ID, 0x003c) },
> > + { USB_DEVICE(VENDOR_ID, 0x003d) },
> > + { USB_DEVICE(VENDOR_ID, 0x003e) },
> > + { USB_DEVICE(VENDOR_ID, 0x003f) },
> > + { }
> > +};
> > +
> > +MODULE_DEVICE_TABLE(usb, smi2021_usb_device_id_table);
> > +
> > +static unsigned short saa7113_addrs[] = {
> > + 0x4a,
> > + I2C_CLIENT_END
> > +};
> > +
> > +/******************************************************************************/
> > +/* */
> > +/* Write to saa7113 */
> > +/* */
> > +/******************************************************************************/
> > +
> > +inline int transfer_usb_ctrl(struct smi2021_dev *dev, u8 *data, int len)
> > +{
> > + return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
> > + 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> > + 0x0b, 0x00,
> > + data, len, 1000);
> > +}
> > +
> > +int smi2021_write_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 val)
> > +{
> > + int rc;
> > + u8 snd_data[8];
> > +
> > + memset(snd_data, 0x00, 8);
> > +
>
> I think you want ARRAY_SIZE macro here.
>

Fixed

> > + snd_data[SMI2021_CTRL_HEAD] = 0x0b;
> > + snd_data[SMI2021_CTRL_ADDR] = addr;
> > + snd_data[SMI2021_CTRL_DATA_SIZE] = 0x01;
> > +
> > + if (addr) {
> > + /* This is I2C data for the saa7113 chip */
> > + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0xc0;
> > + snd_data[SMI2021_CTRL_BM_DATA_OFFSET] = 0x01;
> > +
> > + snd_data[SMI2021_CTRL_I2C_REG] = reg;
> > + snd_data[SMI2021_CTRL_I2C_VAL] = val;
> > + } else {
> > + /* This is register settings for the smi2021 chip */
> > + snd_data[SMI2021_CTRL_BM_DATA_OFFSET] = 0x82;
> > +
>
> Mmmm... you should really avoid magic numbers: 0x82, 0xc0, 0x01...
> can't you use some more meaningful macros?
>

The big problem here, is that I don't know what to call these numbers.

I know some of them are bit fields, and I have some knowledege of what
they do. But as I'm not sure about all of these bits, I figured it was
best to leave them as magic numbers.

I can try to make up some names for the different bits, and create some macros
for the next patch :)

> > + snd_data[SMI2021_CTRL_REG_HI] = __cpu_to_be16(reg) >> 8;
> > + snd_data[SMI2021_CTRL_REG_LO] = __cpu_to_be16(reg);
> > +
> > + }
> > +
> > + rc = transfer_usb_ctrl(dev, snd_data, 8);
>
> ARRAY_SIZE macro again...
>

Fixed

> > + if (rc < 0) {
> > + smi2021_warn("write failed on register 0x%x, errno: %d\n",
> > + reg, rc);
> > + return rc;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +int smi2021_read_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 *val)
> > +{
> > + int rc;
> > + u8 rcv_data[13];
> > + u8 snd_data[8];
> > + memset(rcv_data, 0x00, 13);
> > + memset(snd_data, 0x00, 8);
> > +
>
> Ditto.
>

Yes
> > + snd_data[SMI2021_CTRL_HEAD] = 0x0b;
> > + snd_data[SMI2021_CTRL_ADDR] = addr;
> > + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0x84;
> > + snd_data[SMI2021_CTRL_DATA_SIZE] = 0x01;
> > + snd_data[SMI2021_CTRL_I2C_REG] = reg;
> > +
> > + *val = 0;
> > +
> > + rc = transfer_usb_ctrl(dev, snd_data, 8);
> > + if (rc < 0) {
> > + smi2021_warn(
> > + "1st pass failing to read reg 0x%x, usb-errno: %d\n",
> > + reg, rc);
> > + return rc;
> > + }
> > +
> > + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0xa0;
> > + rc = transfer_usb_ctrl(dev, snd_data, 8);
> > + if (rc < 0) {
> > + smi2021_warn(
> > + "2nd pass failing to read reg 0x%x, usb-errno: %d\n",
> > + reg, rc);
> > + return rc;
> > + }
> > +
> > + rc = usb_control_msg(dev->udev,
> > + usb_rcvctrlpipe(dev->udev, 0x80), 0x01,
> > + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> > + 0x0b, 0x00, rcv_data, 13, 1000);
> > + if (rc < 0) {
> > + smi2021_warn("Failed to read reg 0x%x, usb-errno: %d\n",
> > + reg, rc);
> > + return rc;
> > + }
> > +
> > + *val = rcv_data[SMI2021_CTRL_I2C_RCV_VAL];
> > + return 0;
> > +}
> > +
> > +static void smi2021_reset_device(struct smi2021_dev *dev)
> > +{
> > + smi2021_write_reg(dev, 0, 0x3a, 0x80);
> > + smi2021_write_reg(dev, 0, 0x3b, 0x80);
> > + smi2021_write_reg(dev, 0, 0x3b, 0x00);
>
> More magic numbers :S
>

More stuff I don't know what is.
But as I said, I can try to make some nice macros for them :)

> > +}
> > +
> > +static void release_v4l2_dev(struct v4l2_device *v4l2_dev)
> > +{
> > + struct smi2021_dev *dev = container_of(v4l2_dev, struct smi2021_dev,
> > + v4l2_dev);
> > + smi2021_dbg("Releasing all resources\n");
> > +
> > + smi2021_i2c_unregister(dev);
> > +
> > + v4l2_ctrl_handler_free(&dev->ctrl_handler);
> > + v4l2_device_unregister(&dev->v4l2_dev);
> > + kfree(dev);
> > +}
> > +
> > +#define hb_mult(w_max_packet_size) (1 + (((w_max_packet_size) >> 11) & 0x03))
> > +
> > +static int smi2021_scan_usb(struct usb_interface *intf, struct usb_device *udev)
> > +{
> > + int i, e, ifnum, sizedescr, size;
> > + const struct usb_endpoint_descriptor *desc;
> > + ifnum = intf->altsetting[0].desc.bInterfaceNumber;
> > +
> > + for (i = 0; i < intf->num_altsetting; i++) {
> > + for (e = 0; e < intf->altsetting[i].desc.bNumEndpoints; e++) {
> > + desc = &intf->altsetting[i].endpoint[e].desc;
> > + sizedescr = le16_to_cpu(desc->wMaxPacketSize);
> > + size = sizedescr & 0x7ff;
> > +
> > + if (udev->speed == USB_SPEED_HIGH)
> > + size = size * hb_mult(sizedescr);
> > + }
> > + }
> > + return 0;
> > +}
> > +
> > +/******************************************************************************/
> > +/* */
> > +/* DEVICE - PROBE & DISCONNECT */
> > +/* */
> > +/******************************************************************************/
> > +static int smi2021_usb_probe(struct usb_interface *intf,
> > + const struct usb_device_id *devid)
> > +{
> > + int rc = 0;
>
> Is this rc initialization needed?
>
Nope, removed!

> > + struct usb_device *udev = interface_to_usbdev(intf);
> > + struct smi2021_dev *dev;
> > +
> > + if (udev == (struct usb_device *)NULL) {
> > + smi2021_err("device is NULL\n");
> > + return -EFAULT;
> > + }
> > +
> > + smi2021_scan_usb(intf, udev);
> > +
> > + dev = kzalloc(sizeof(struct smi2021_dev), GFP_KERNEL);
> > + if (dev == NULL)
> > + return -ENOMEM;
> > +
> > + dev->udev = udev;
> > + dev->dev = &intf->dev;
> > + usb_set_intfdata(intf, dev);
> > +
> > + /* Initialize videobuf2 stuff */
> > + rc = smi2021_vb2_setup(dev);
> > + if (rc < 0)
> > + goto free_err;
> > +
> > + spin_lock_init(&dev->buf_lock);
> > + mutex_init(&dev->v4l2_lock);
> > + mutex_init(&dev->vb_queue_lock);
> > +
> > + rc = v4l2_ctrl_handler_init(&dev->ctrl_handler, 0);
> > + if (rc) {
> > + smi2021_err("v4l2_ctrl_handler_init failed with: %d\n", rc);
> > + goto free_err;
> > + }
> > +
> > + dev->v4l2_dev.release = release_v4l2_dev;
> > + dev->v4l2_dev.ctrl_handler = &dev->ctrl_handler;
> > + rc = v4l2_device_register(dev->dev, &dev->v4l2_dev);
> > + if (rc) {
> > + smi2021_err("v4l2_device_register failed with %d\n", rc);
> > + goto free_ctrl;
> > + }
> > +
> > + smi2021_reset_device(dev);
> > +
> > + rc = smi2021_i2c_register(dev);
> > + if (rc < 0)
> > + goto unreg_v4l2;
> > +
> > + dev->sd_saa7113 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
> > + "saa7113", 0, saa7113_addrs);
> > +
> > + smi2021_dbg("Driver version %s successfully loaded\n",
> > + SMI2021_DRIVER_VERSION);
> > +
> > + v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0);
> > + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
> > +
> > + rc = smi2021_snd_register(dev);
> > + if (rc < 0)
> > + goto unreg_i2c;
> > +
> > + rc = smi2021_video_register(dev);
> > + if (rc < 0)
> > + goto unreg_snd;
> > +
> > + return 0;
> > +
> > +unreg_snd:
> > + smi2021_snd_unregister(dev);
> > +unreg_i2c:
> > + smi2021_i2c_unregister(dev);
> > +unreg_v4l2:
> > + v4l2_device_unregister(&dev->v4l2_dev);
> > +free_ctrl:
> > + v4l2_ctrl_handler_free(&dev->ctrl_handler);
> > +free_err:
> > + kfree(dev);
> > +
> > + return rc;
> > +}
> > +
> > +static void smi2021_usb_disconnect(struct usb_interface *intf)
> > +{
> > + struct smi2021_dev *dev = usb_get_intfdata(intf);
> > +
> > + smi2021_dbg("Going for release!\n");
> > +
> > + usb_set_intfdata(intf, NULL);
> > +
> > + mutex_lock(&dev->vb_queue_lock);
> > + mutex_lock(&dev->v4l2_lock);
> > +
> > + smi2021_uninit_isoc(dev);
> > + smi2021_clear_queue(dev);
> > +
> > + video_unregister_device(&dev->vdev);
> > + v4l2_device_disconnect(&dev->v4l2_dev);
> > +
> > + /* This way current users can detect device is gone */
> > + dev->udev = NULL;
> > +
> > + mutex_unlock(&dev->v4l2_lock);
> > + mutex_unlock(&dev->vb_queue_lock);
> > +
> > + smi2021_snd_unregister(dev);
> > +
> > + /*
> > + * This calls release_v4l2_dev if it's the last reference.
> > + * Otherwise, the release is postponed until there are no users left.
> > + */
> > + v4l2_device_put(&dev->v4l2_dev);
> > +}
> > +
> > +/******************************************************************************/
> > +/* */
> > +/* MODULE - INIT & EXIT */
> > +/* */
> > +/******************************************************************************/
> > +
> > +struct usb_driver smi2021_usb_driver = {
> > + .name = "smi2021",
> > + .id_table = smi2021_usb_device_id_table,
> > + .probe = smi2021_usb_probe,
> > + .disconnect = smi2021_usb_disconnect
> > +};
> > +
> > +module_usb_driver(smi2021_usb_driver);
> > +
> > --
> > 1.8.1.1
> >
>
> --
> Ezequiel García, Free Electrons
> Embedded Linux, Kernel and Android Engineering
> http://free-electrons.com

2013-03-17 20:13:33

by Jon Arne Jørgensen

[permalink] [raw]
Subject: Re: [RFC V1 1/8] smi2021: Add the header file

On Fri, Mar 15, 2013 at 09:13:10AM -0300, Ezequiel Garcia wrote:
> On Thu, Mar 14, 2013 at 03:06:57PM +0100, Jon Arne Jørgensen wrote:
> > This is the header file for the smi2021 module.
> >
> > Signed-off-by: Jon Arne Jørgensen <[email protected]>
> > ---
> > drivers/media/usb/smi2021/smi2021.h | 228 ++++++++++++++++++++++++++++++++++++
> > 1 file changed, 228 insertions(+)
> > create mode 100644 drivers/media/usb/smi2021/smi2021.h
> >
> > diff --git a/drivers/media/usb/smi2021/smi2021.h b/drivers/media/usb/smi2021/smi2021.h
> > new file mode 100644
> > index 0000000..eba0fef
> > --- /dev/null
> > +++ b/drivers/media/usb/smi2021/smi2021.h
> > @@ -0,0 +1,228 @@
> > +/*******************************************************************************
> > + * smi2021.h *
> > + * *
> > + * USB Driver for SMI2021 - EasyCap *
> > + * USB ID 1c88:003c *
> > + * *
> > + * *****************************************************************************
> > + *
> > + * Copyright 2011-2013 Jon Arne Jørgensen
> > + * <jonjon.arnearne--a.t--gmail.com>
> > + *
> > + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> > + *
> > + * This file is part of SMI2021
> > + * http://code.google.com/p/easycap-somagic-linux/
> > + *
>
> I'm not sure how helpful it is to add external references.
> Keep in mind that once mainlined, this driver will quickly evolve
> and the external link can get outdated/removed/deprecated, etc.
>

Yes, I forgot to remove this part before sending the patch.

> > + * This program is free software: you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation, either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
>
> Please note Linux kernel preferes GPL v2 *only*, and not any later
> (although I'm not a lawyer).
>

Ok, I will look into this, shouldn't be a problem to change it :)

> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> > + *
> > + * This driver is heavily influensed by the STK1160 driver.
> > + * Copyright (C) 2012 Ezequiel Garcia
> > + * <elezegarcia--a.t--gmail.com>
>
> Cool ;-)
>

You are welcome, thanks for the review btw :)

> > + *
> > + */
> > +
> > +#ifndef SMI2021_H
> > +#define SMI2021_H
> > +
> > +#include <linux/module.h>
> > +#include <linux/usb.h>
> > +#include <linux/types.h>
> > +#include <linux/spinlock_types.h>
> > +#include <linux/slab.h>
> > +#include <linux/i2c.h>
> > +
> > +#include <media/v4l2-event.h>
> > +#include <media/v4l2-device.h>
> > +#include <media/v4l2-ioctl.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-chip-ident.h>
> > +#include <media/videobuf2-core.h>
> > +#include <media/videobuf2-vmalloc.h>
> > +#include <media/saa7115.h>
> > +
> > +#include <sound/core.h>
> > +#include <sound/pcm.h>
> > +#include <sound/pcm_params.h>
> > +#include <sound/initval.h>
> > +
> > +#define SMI2021_DRIVER_VERSION "0.1"
> > +
> > +/* For ISOC */
> > +#define SMI2021_MAX_PKT_SIZE 3072
> > +#define SMI2021_ISOC_PACKETS 10 /* 64 */
> > +#define SMI2021_ISOC_BUFS 4 /* 16 */
> > +#define SMI2021_ISOC_EP 0x82
> > +
> > +/* The structure of the array we use to send i2c settings to the device */
> > +#define SMI2021_CTRL_HEAD 0x00
> > +#define SMI2021_CTRL_ADDR 0x01
> > +#define SMI2021_CTRL_BM_DATA_TYPE 0x02
> > +#define SMI2021_CTRL_BM_DATA_OFFSET 0x03
> > +#define SMI2021_CTRL_DATA_SIZE 0x04
> > +
> > +#define SMI2021_CTRL_REG_HI 0x05
> > +#define SMI2021_CTRL_REG_LO 0x06
> > +#define SMI2021_CTRL_REG_VAL 0x07
> > +
> > +#define SMI2021_CTRL_I2C_REG 0x05
> > +#define SMI2021_CTRL_I2C_VAL 0x06
> > +#define SMI2021_CTRL_I2C_RCV_VAL 0x05
> > +
> > +/* General video constants */
> > +#define SMI2021_BYTES_PER_LINE 1440
> > +#define SMI2021_PAL_LINES 576
> > +#define SMI2021_NTSC_LINES 486
> > +
> > +/* Timing Referance Codes, see saa7113 datasheet */
> > +#define SMI2021_TRC_EAV 0x10
> > +#define SMI2021_TRC_VBI 0x20
> > +#define SMI2021_TRC_FIELD_2 0x40
> > +#define SMI2021_TRC 0x80
> > +
> > +#define DEBUG
> > +#ifdef DEBUG
> > +#define smi2021_dbg(fmt, args...) \
> > + pr_debug("smi2021::%s: " fmt, __func__, \
> > + ##args)
> > +#else
> > +#define smi2021_dbg(fmt, args...)
> > +#endif
> > +
> > +#define smi2021_info(fmt, args...) \
> > + pr_info("smi2021::%s: " fmt, \
> > + __func__, ##args)
> > +
> > +#define smi2021_warn(fmt, args...) \
> > + pr_warn("smi2021::%s: " fmt, \
> > + __func__, ##args)
> > +
> > +#define smi2021_err(fmt, args...) \
> > + pr_err("smi2021::%s: " fmt, \
> > + __func__, ##args)
> > +
> > +enum smi2021_sync {
> > + HSYNC,
> > + SYNCZ1,
> > + SYNCZ2,
> > + TRC
> > +};
> > +
> > +/* Buffer for one video frame */
> > +struct smi2021_buffer {
> > + /* Common vb2 stuff, must be first */
> > + struct vb2_buffer vb;
> > + struct list_head list;
> > +
> > + void *mem;
> > + unsigned int length;
> > +
> > + bool second_field;
> > + bool in_blank;
> > + unsigned int pos;
> > +
> > + /* ActiveVideo - Line counter */
> > + u16 trc_av;
> > +};
> > +
> > +struct smi2021_isoc_ctl {
> > + int max_pkt_size;
> > + int num_bufs;
> > + struct urb **urb;
> > + char **transfer_buffer;
> > + struct smi2021_buffer *buf;
> > +};
> > +
> > +
> > +struct smi2021_fmt {
> > + char *name;
> > + u32 fourcc;
> > + int depth;
> > +};
> > +
> > +struct smi2021_input {
> > + char *name;
> > + int type;
> > +};
> > +
> > +struct smi2021_dev {
> > + struct v4l2_device v4l2_dev;
> > + struct video_device vdev;
> > + struct v4l2_ctrl_handler ctrl_handler;
> > +
> > + struct v4l2_subdev *sd_saa7113;
> > +
> > + struct usb_device *udev;
> > + struct device *dev;
> > +
> > + /* Capture buffer queue */
> > + struct vb2_queue vb_vidq;
> > +
> > + /* ISOC control struct */
> > + struct list_head avail_bufs;
> > + struct smi2021_isoc_ctl isoc_ctl;
> > +
> > + int width; /* frame width */
> > + int height; /* frame height */
> > + unsigned int ctl_input; /* selected input */
> > + v4l2_std_id norm; /* current norm */
> > + struct smi2021_fmt *fmt; /* selected format */
> > + unsigned int buf_count; /* for video buffers */
> > +
> > + /* i2c i/o */
> > + struct i2c_adapter i2c_adap;
> > + struct i2c_client i2c_client;
> > +
> > + struct mutex v4l2_lock;
> > + struct mutex vb_queue_lock;
> > + spinlock_t buf_lock;
> > +
> > + enum smi2021_sync sync_state;
> > +
> > + /* audio */
> > + struct snd_card *snd_card;
> > + struct snd_pcm_substream *pcm_substream;
> > +
> > + unsigned int pcm_write_ptr;
> > + unsigned int pcm_complete_samples;
> > +
> > + u8 pcm_read_offset;
> > + struct work_struct adev_capture_trigger;
> > + atomic_t adev_capturing;
> > +};
> > +
> > +/* Provided by smi2021_main.c */
> > +int smi2021_write_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 val);
> > +int smi2021_read_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 *val);
> > +
> > +/* Provided by smi2021_v4l2.c */
> > +int smi2021_vb2_setup(struct smi2021_dev *dev);
> > +int smi2021_video_register(struct smi2021_dev *dev);
> > +void smi2021_clear_queue(struct smi2021_dev *dev);
> > +
> > +/* Provided by smi2021_video.c */
> > +int smi2021_alloc_isoc(struct smi2021_dev *dev);
> > +void smi2021_free_isoc(struct smi2021_dev *dev);
> > +void smi2021_cancel_isoc(struct smi2021_dev *dev);
> > +void smi2021_uninit_isoc(struct smi2021_dev *dev);
> > +
> > +/* Provided by smi2021_i2c.c */
> > +int smi2021_i2c_register(struct smi2021_dev *dev);
> > +int smi2021_i2c_unregister(struct smi2021_dev *dev);
> > +
> > +/* Provided by smi2021_audio.c */
> > +int smi2021_snd_register(struct smi2021_dev *dev);
> > +void smi2021_snd_unregister(struct smi2021_dev *dev);
> > +void smi2021_audio(struct smi2021_dev *dev, u8 *data, int len);
> > +#endif /* SMI2021_H */
> > --
> > 1.8.1.1
> >
>
> --
> Ezequiel García, Free Electrons
> Embedded Linux, Kernel and Android Engineering
> http://free-electrons.com

2013-03-17 20:16:37

by Jon Arne Jørgensen

[permalink] [raw]
Subject: Re: [RFC V1 5/8] smi2021: Add smi2021_video.c

On Fri, Mar 15, 2013 at 09:40:35AM -0300, Ezequiel Garcia wrote:
> On Thu, Mar 14, 2013 at 03:07:01PM +0100, Jon Arne Jørgensen wrote:
> > This file is responsible for all communication with the video hardware
> > and also starting and stopping the capture.
> >
> > It also contains the setup and handling of the usb ISOCHRONOUS transfers.
> >
> > Signed-off-by: Jon Arne Jørgensen <[email protected]>
> > ---
> > drivers/media/usb/smi2021/smi2021_video.c | 543 ++++++++++++++++++++++++++++++
> > 1 file changed, 543 insertions(+)
> > create mode 100644 drivers/media/usb/smi2021/smi2021_video.c
> >
> > diff --git a/drivers/media/usb/smi2021/smi2021_video.c b/drivers/media/usb/smi2021/smi2021_video.c
> > new file mode 100644
> > index 0000000..48f3e80
> > --- /dev/null
> > +++ b/drivers/media/usb/smi2021/smi2021_video.c
> > @@ -0,0 +1,543 @@
> > +/*******************************************************************************
> > + * smi2021_video.c *
> > + * *
> > + * USB Driver for SMI2021 - EasyCAP *
> > + * USB ID 1c88:003c *
> > + * *
> > + * *****************************************************************************
> > + *
> > + * Copyright 2011-2013 Jon Arne Jørgensen
> > + * <jonjon.arnearne--a.t--gmail.com>
> > + *
> > + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> > + *
> > + * This file is part of SMI2021x
> > + * http://code.google.com/p/easycap-somagic-linux/
> > + *
> > + * This program is free software: you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation, either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> > + *
> > + * This driver is heavily influensed by the STK1160 driver.
> > + * Copyright (C) 2012 Ezequiel Garcia
> > + * <elezegarcia--a.t--gmail.com>
> > + *
> > + */
> > +
> > +#include "smi2021.h"
> > +
> > +static void print_usb_err(struct smi2021_dev *dev, int packet, int status)
> > +{
> > + char *errmsg;
> > +
> > + switch (status) {
> > + case -ENOENT:
> > + errmsg = "unlinked synchronuously";
> > + break;
> > + case -ECONNRESET:
> > + errmsg = "unlinked asynchronuously";
> > + break;
> > + case -ENOSR:
> > + errmsg = "Buffer error (overrun)";
> > + break;
> > + case -EPIPE:
> > + errmsg = "Stalled (device not responding)";
> > + break;
> > + case -EOVERFLOW:
> > + errmsg = "Babble (bad cable?)";
> > + break;
> > + case -EPROTO:
> > + errmsg = "Bit-stuff error (bad cable?)";
> > + break;
> > + case -EILSEQ:
> > + errmsg = "CRC/Timeout (could be anything)";
> > + break;
> > + case -ETIME:
> > + errmsg = "Device does not respond";
> > + break;
> > + default:
> > + errmsg = "Unknown";
> > + }
> > +
> > + if (packet < 0) {
> > + printk_ratelimited(KERN_WARNING "Urb status %d [%s]\n",
> > + status, errmsg);
> > + } else {
> > + printk_ratelimited(KERN_INFO "URB packet %d, status %d [%s]\n",
> > + packet, status, errmsg);
> > + }
> > +}
> > +
> > +static struct smi2021_buffer *smi2021_next_buffer(struct smi2021_dev *dev)
> > +{
> > + struct smi2021_buffer *buf = NULL;
> > + unsigned long flags = 0;
> > +
> > + BUG_ON(dev->isoc_ctl.buf);
> > +
>
> You should replace BUG_ON with WARN_ON. Linus' words:
> http://permalink.gmane.org/gmane.linux.kernel/1347333
>

Done!

> > + spin_lock_irqsave(&dev->buf_lock, flags);
> > + if (!list_empty(&dev->avail_bufs)) {
> > + buf = list_first_entry(&dev->avail_bufs, struct smi2021_buffer,
> > + list);
> > + list_del(&buf->list);
> > + }
> > + spin_unlock_irqrestore(&dev->buf_lock, flags);
> > +
> > + return buf;
> > +}
> > +
> > +static void smi2021_buffer_done(struct smi2021_dev *dev)
> > +{
> > + struct smi2021_buffer *buf = dev->isoc_ctl.buf;
> > +
> > + dev->buf_count++;
> > +
> > + buf->vb.v4l2_buf.sequence = dev->buf_count >> 1;
> > + buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
> > + buf->vb.v4l2_buf.bytesused = buf->pos;
> > + do_gettimeofday(&buf->vb.v4l2_buf.timestamp);
> > +
> > + vb2_set_plane_payload(&buf->vb, 0, buf->pos);
> > + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
> > +
> > + dev->isoc_ctl.buf = NULL;
> > +}
> > +
> > +static void copy_video(struct smi2021_dev *dev, u8 p)
> > +{
> > + struct smi2021_buffer *buf = dev->isoc_ctl.buf;
> > +
> > + int lines_per_field = dev->height / 2;
> > + int line = 0;
> > + int pos_in_line = 0;
> > + unsigned int offset = 0;
> > + u8 *dst;
> > +
> > + if (buf == NULL)
> > + return;
> > +
> > + if (buf->in_blank)
> > + return;
> > +
> > + if (buf->pos >= buf->length) {
> > + if (buf->second_field == 0) {
> > + /* We are probably trying to capture from
> > + * a unconnected input
> > + */
> > + smi2021_buffer_done(dev);
> > + } else {
> > + printk_ratelimited(KERN_WARNING
> > + "Buffer overflow!, max: %d bytes, av_lines_found: %d, second_field: %d\n",
> > + buf->length, buf->trc_av,
> > + buf->second_field);
> > + }
> > + return;
> > + }
> > +
> > + pos_in_line = buf->pos % SMI2021_BYTES_PER_LINE;
> > + line = buf->pos / SMI2021_BYTES_PER_LINE;
> > + if (line >= lines_per_field)
> > + line -= lines_per_field;
> > +
> > + if (line != buf->trc_av - 1) {
> > + /* Keep video synchronized.
> > + * The device will sometimes give us to many bytes
> > + * for a line, before we get a new TRC.
> > + * We just drop these bytes */
> > + return;
> > + }
> > +
> > + if (buf->second_field)
> > + offset += SMI2021_BYTES_PER_LINE;
> > +
> > + offset += (SMI2021_BYTES_PER_LINE * line * 2) + pos_in_line;
> > +
> > + /* Will this ever happen? */
> > + if (offset >= buf->length) {
> > + printk_ratelimited(KERN_INFO
> > + "Offset calculation error, field: %d, line: %d, pos_in_line: %d\n",
> > + buf->second_field, line, pos_in_line);
> > + return;
> > + }
> > +
> > + dst = buf->mem + offset;
> > + *dst = p;
> > + buf->pos++;
> > +}
> > +
> > +#define is_sav(trc) \
> > + ((trc & SMI2021_TRC_EAV) == 0x00)
> > +#define is_field2(trc) \
> > + ((trc & SMI2021_TRC_FIELD_2) == SMI2021_TRC_FIELD_2)
> > +#define is_active_video(trc) \
> > + ((trc & SMI2021_TRC_VBI) == 0x00)
> > +/*
> > + * Parse the TRC.
> > + * Grab a new buffer from the queue if don't have one
> > + * and we are recieving the start of a video frame.
> > + *
> > + * Mark video buffers as done if we have one full frame.
> > + */
> > +static void parse_trc(struct smi2021_dev *dev, u8 trc)
> > +{
> > + struct smi2021_buffer *buf = dev->isoc_ctl.buf;
> > + int lines_per_field = dev->height / 2;
> > + int line = 0;
> > +
> > + if (buf == NULL) {
> > + if (!is_sav(trc))
> > + return;
> > +
> > + if (!is_active_video(trc))
> > + return;
> > +
> > + if (is_field2(trc))
> > + return;
> > +
> > + buf = smi2021_next_buffer(dev);
> > + if (buf == NULL)
> > + return;
> > +
> > + dev->isoc_ctl.buf = buf;
> > + }
> > +
> > + if (is_sav(trc)) {
> > + /* Start of VBI or ACTIVE VIDEO */
> > + if (is_active_video(trc)) {
> > + buf->in_blank = false;
> > + buf->trc_av++;
> > + } else {
> > + /* VBI */
> > + buf->in_blank = true;
> > + }
> > +
> > + if (!buf->second_field && is_field2(trc)) {
> > + line = buf->pos / SMI2021_BYTES_PER_LINE;
> > + if (line < lines_per_field)
> > + goto buf_done;
> > +
> > + buf->second_field = true;
> > + buf->trc_av = 0;
> > + }
> > +
> > + if (buf->second_field && !is_field2(trc))
> > + goto buf_done;
> > + } else {
> > + /* End of VBI or ACTIVE VIDEO */
> > + buf->in_blank = true;
> > + }
> > +
> > + return;
> > +
> > +buf_done:
> > + smi2021_buffer_done(dev);
> > +}
> > +
> > +/*
> > + * Scan the saa7113 Active video data.
> > + * This data is:
> > + * 4 bytes header (0xff 0x00 0x00 [TRC/SAV])
> > + * 1440 bytes of UYUV Video data
> > + * 4 bytes footer (0xff 0x00 0x00 [TRC/EAV])
> > + *
> > + * TRC = Time Reference Code.
> > + * SAV = Start Active Video.
> > + * EAV = End Active Video.
> > + * This is described in the saa7113 datasheet.
> > + */
> > +static void parse_video(struct smi2021_dev *dev, u8 *p, int len)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < len; i++) {
> > + switch (dev->sync_state) {
> > + case HSYNC:
> > + if (p[i] == 0xff)
> > + dev->sync_state = SYNCZ1;
> > + else
> > + copy_video(dev, p[i]);
> > + break;
> > + case SYNCZ1:
> > + if (p[i] == 0x00) {
> > + dev->sync_state = SYNCZ2;
> > + } else {
> > + dev->sync_state = HSYNC;
> > + copy_video(dev, 0xff);
> > + copy_video(dev, p[i]);
> > + }
> > + break;
> > + case SYNCZ2:
> > + if (p[i] == 0x00) {
> > + dev->sync_state = TRC;
> > + } else {
> > + dev->sync_state = HSYNC;
> > + copy_video(dev, 0xff);
> > + copy_video(dev, 0x00);
> > + copy_video(dev, p[i]);
> > + }
> > + break;
> > + case TRC:
> > + dev->sync_state = HSYNC;
> > + parse_trc(dev, p[i]);
> > + break;
> > + }
> > + }
> > +
> > +}
> > +/*
> > + *
> > + * The device delivers data in chunks of 0x400 bytes.
> > + * The four first bytes is a magic header to identify the chunks.
> > + * 0xaa 0xaa 0x00 0x00 = saa7113 Active Video Data
> > + * 0xaa 0xaa 0x00 0x01 = PCM - 24Bit 2 Channel audio data
> > + */
> > +static void process_packet(struct smi2021_dev *dev, u8 *p, int len)
> > +{
> > + int i;
> > + u32 *header;
> > +
> > + if (len % 0x400 != 0) {
> > + printk_ratelimited(KERN_INFO "smi2021::%s: len: %d\n",
> > + __func__, len);
> > + return;
> > + }
> > +
> > + for (i = 0; i < len; i += 0x400) {
> > + header = (u32 *)(p + i);
> > + switch (__cpu_to_be32(*header)) {
> > + case 0xaaaa0000: {
> > + parse_video(dev, p+i+4, 0x400-4);
> > + break;
> > + }
> > + case 0xaaaa0001: {
> > + smi2021_audio(dev, p+i+4, 0x400-4);
> > + break;
> > + }
> > + default: {
> > + /* Nothing */
> > + }
> > + }
> > + }
> > +}
> > +
> > +/*
> > + * Interrupt called by URB callback
> > + */
> > +static void smi2021_isoc_isr(struct urb *urb)
> > +{
> > + int i, rc, status, len;
> > + struct smi2021_dev *dev = urb->context;
> > + u8 *p;
> > +
> > + switch (urb->status) {
> > + case 0:
> > + break;
> > + case -ECONNRESET: /* kill */
> > + case -ENOENT:
> > + case -ESHUTDOWN:
> > + /* uvc driver frees the queue here */
> > + return;
> > + default:
> > + smi2021_err("urb error! status %d\n", urb->status);
> > + return;
> > + }
> > +
> > + if (urb->status < 0)
> > + print_usb_err(dev, -1, status);
> > +
> > + if (dev == NULL) {
> > + smi2021_warn("called with null device\n");
> > + return;
> > + }
> > +
> > + for (i = 0; i < urb->number_of_packets; i++) {
> > +
> > + status = urb->iso_frame_desc[i].status;
> > + if (status == -18) {
> > + /* This seems to happen when the device
> > + * trying to stream from an unconnected input
> > + * */
> > + continue;
> > + }
> > +
> > + if (status < 0) {
> > + print_usb_err(dev, i, status);
> > + continue;
> > + }
> > +
> > + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
> > + len = urb->iso_frame_desc[i].actual_length;
> > + process_packet(dev, p, len);
> > + }
> > +
> > + for (i = 0; i < urb->number_of_packets; i++) {
> > + urb->iso_frame_desc[i].status = 0;
> > + urb->iso_frame_desc[i].actual_length = 0;
> > + }
> > +
> > + rc = usb_submit_urb(urb, GFP_ATOMIC);
> > + if (rc)
> > + smi2021_err("urb re-submit failed (%d)\n", rc);
> > +}
> > +
> > +/*
> > + * Cancel urbs
> > + * This function can not be called in atomic context
> > + */
> > +void smi2021_cancel_isoc(struct smi2021_dev *dev)
> > +{
> > + int i, num_bufs = dev->isoc_ctl.num_bufs;
> > + if (!num_bufs)
> > + return;
> > +
> > + smi2021_dbg("killing %d urbs...\n", num_bufs);
> > +
> > + for (i = 0; i < num_bufs; i++)
> > + usb_kill_urb(dev->isoc_ctl.urb[i]);
> > +
> > + smi2021_dbg("all urbs killed\n");
> > +
> > +}
> > +
> > +/*
> > + * Releases urb and transfer buffers
> > + * Obviously, associated urb must be killed before releasing it
> > + */
> > +void smi2021_free_isoc(struct smi2021_dev *dev)
> > +{
> > + struct urb *urb;
> > + int i, num_bufs = dev->isoc_ctl.num_bufs;
> > +
> > + smi2021_dbg("freeing %d urb buffers...\n", num_bufs);
> > +
> > + for (i = 0; i < num_bufs; i++) {
> > + urb = dev->isoc_ctl.urb[i];
> > + if (urb) {
> > + if (dev->isoc_ctl.transfer_buffer[i]) {
> > +#ifndef CONFIG_DMA_NONCOHERENT
> > + usb_free_coherent(dev->udev,
> > + urb->transfer_buffer_length,
> > + dev->isoc_ctl.transfer_buffer[i],
> > + urb->transfer_dma);
> > +#else
> > + kfree(dev->isoc_ctl.transfer_buffer[i]);
> > +#endif
> > + }
> > + 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;
> > +
> > + smi2021_dbg("all urb buffers freed\n");
> > +}
> > +
> > +/*
> > + * Helper for canceling and freeing urbs
> > + * This function can not be called in atomic context
> > + */
> > +void smi2021_uninit_isoc(struct smi2021_dev *dev)
> > +{
> > + smi2021_cancel_isoc(dev);
> > + smi2021_free_isoc(dev);
> > +}
> > +
> > +
> > +int smi2021_alloc_isoc(struct smi2021_dev *dev)
> > +{
> > + struct urb *urb;
> > + int i, j, k, sb_size, max_packets, num_bufs;
> > +
> > + if (dev->isoc_ctl.num_bufs)
> > + smi2021_uninit_isoc(dev);
> > +
> > + num_bufs = SMI2021_ISOC_BUFS;
> > + max_packets = SMI2021_ISOC_PACKETS;
> > + sb_size = max_packets * SMI2021_MAX_PKT_SIZE;
> > +
> > + dev->isoc_ctl.buf = NULL;
> > + dev->isoc_ctl.max_pkt_size = SMI2021_MAX_PKT_SIZE;
> > + dev->isoc_ctl.urb = kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
> > + if (!dev->isoc_ctl.urb) {
> > + smi2021_err("out of memory for urb array\n");
> > + return -ENOMEM;
> > + }
> > +
> > + dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *) * num_bufs,
> > + GFP_KERNEL);
> > + if (!dev->isoc_ctl.transfer_buffer) {
> > + smi2021_err("out of memory for usb transfer\n");
> > + kfree(dev->isoc_ctl.urb);
> > + return -ENOMEM;
> > + }
> > +
> > + for (i = 0; i < num_bufs; i++) {
> > + urb = usb_alloc_urb(max_packets, GFP_KERNEL);
> > + if (!urb) {
> > + smi2021_err("connot allocate urb[%d]\n", i);
> > + goto free_i_bufs;
> > + }
> > + dev->isoc_ctl.urb[i] = urb;
> > +#ifndef CONFIG_DMA_NONCOHERENT
> > + dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(
> > + dev->udev, sb_size, GFP_KERNEL,
> > + &urb->transfer_dma);
> > +#else
> > + dev->isoc_ctl.transfer_buffer[i] = kmalloc(sb_size,
> > + GFP_KERNEL);
> > +#endif
> > + if (!dev->isoc_ctl.transfer_buffer[i]) {
> > + smi2021_err("cannot alloc %d bytes for tx[%d] buffer",
> > + sb_size, i);
> > + goto free_i_bufs;
> > + }
> > + /* Do not leak kernel data */
> > + memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
> > +
> > + urb->dev = dev->udev;
> > + urb->pipe = usb_rcvisocpipe(dev->udev, SMI2021_ISOC_EP);
> > + urb->transfer_buffer = dev->isoc_ctl.transfer_buffer[i];
> > + urb->transfer_buffer_length = sb_size;
> > + urb->complete = smi2021_isoc_isr;
> > + urb->context = dev;
> > + urb->interval = 1;
> > + urb->start_frame = 0;
> > + urb->number_of_packets = max_packets;
> > +#ifndef CONFIG_DMA_NONCOHERENT
> > + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
> > +#else
> > + urb->transfer_flags = URB_ISO_ASAP;
> > +#endif
> > + 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;
> > + }
> > + }
> > + smi2021_dbg("urbs allocated\n");
> > + dev->isoc_ctl.num_bufs = num_bufs;
> > + return 0;
> > +
> > +free_i_bufs:
> > + dev->isoc_ctl.num_bufs = i+1;
> > + smi2021_free_isoc(dev);
> > + return -ENOMEM;
> > +}
> > --
> > 1.8.1.1
> >
>
> --
> Ezequiel García, Free Electrons
> Embedded Linux, Kernel and Android Engineering
> http://free-electrons.com

2013-03-17 20:24:39

by Jon Arne Jørgensen

[permalink] [raw]
Subject: Re: [RFC V1 4/8] smi2021: Add smi2021_v4l2.c

On Fri, Mar 15, 2013 at 09:33:20AM -0300, Ezequiel Garcia wrote:
> On Thu, Mar 14, 2013 at 03:07:00PM +0100, Jon Arne Jørgensen wrote:
> > This file is responsible for registering the device with the v4l2 subsystem,
> > and the communication with v4l2.
> > Most of the v4l2 ioctls are just passed on to vidbuf2.
> >
> > Signed-off-by: Jon Arne Jørgensen <[email protected]>
> > ---
> > drivers/media/usb/smi2021/smi2021_v4l2.c | 566 +++++++++++++++++++++++++++++++
> > 1 file changed, 566 insertions(+)
> > create mode 100644 drivers/media/usb/smi2021/smi2021_v4l2.c
> >
> > diff --git a/drivers/media/usb/smi2021/smi2021_v4l2.c b/drivers/media/usb/smi2021/smi2021_v4l2.c
> > new file mode 100644
> > index 0000000..d402093
> > --- /dev/null
> > +++ b/drivers/media/usb/smi2021/smi2021_v4l2.c
> > @@ -0,0 +1,566 @@
> > +/*******************************************************************************
> > + * smi2021_v4l2.c *
> > + * *
> > + * USB Driver for smi2021 - EasyCap *
> > + * USB ID 1c88:003c *
> > + * *
> > + * *****************************************************************************
> > + *
> > + * Copyright 2011-2013 Jon Arne Jørgensen
> > + * <jonjon.arnearne--a.t--gmail.com>
> > + *
> > + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> > + *
> > + * This file is part of SMI2021
> > + * http://code.google.com/p/easycap-somagic-linux/
> > + *
> > + * This program is free software: you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation, either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> > + *
> > + * This driver is heavily influensed by the STK1160 driver.
> > + * Copyright (C) 2012 Ezequiel Garcia
> > + * <elezegarcia--a.t--gmail.com>
> > + *
> > + */
> > +
> > +#include "smi2021.h"
> > +
> > +static struct smi2021_fmt format[] = {
> > + {
> > + .name = "16bpp YU2, 4:2:2, packed",
> > + .fourcc = V4L2_PIX_FMT_UYVY,
> > + .depth = 16,
> > + }
> > +};
> > +
> > +static const int inputs = 2;
>
> You could use ARRAY_SIZE on inputs as well?
> Hardcoded values in general are a good way to have future bugs.
>

Done!

> > +static struct smi2021_input input[] = {
> > + {
> > + .name = "Composite",
> > + .type = SAA7115_COMPOSITE0,
> > + },
> > + {
> > + .name = "S-Video",
> > + .type = SAA7115_SVIDEO1,
> > + }
> > +};
> > +
> > +static void smi2021_set_input(struct smi2021_dev *dev)
> > +{
> > + if (dev->udev == NULL)
> > + return;
> > +
> > + if (dev->ctl_input >= inputs) {
> > + smi2021_err("BUG: ctl_input to big!\n");
> > + return;
> > + }
>
> Aren't you already checking for this? See vidioc_s_input.
>

True, I will remove that check.

> > +
> > + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
> > + input[dev->ctl_input].type, 0, 0);
> > +}
> > +
> > +static int smi2021_start_streaming(struct smi2021_dev *dev)
> > +{
> > + u8 data[2];
> > + int i, rc = 0;
> > + data[0] = 0x01;
> > + data[1] = 0x05;
> > +
> > + if (!dev->udev)
> > + return -ENODEV;
> > +
> > + dev->sync_state = HSYNC;
> > + dev->buf_count = 0;
> > +
> > + if (mutex_lock_interruptible(&dev->v4l2_lock))
> > + return -ERESTARTSYS;
> > +
> > + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1);
> > +
> > + /* The saa 7115 driver sets these wrong,
> > + * this leads to sync issues.
> > + * V_GATE1_START
> > + * V_GATE1_STOP
> > + * V_GATE1_MSB
> > + * All these should be 0x00 for this device.
> > + */
> > + smi2021_write_reg(dev, 0x4a, 0x15, 0x00);
> > + smi2021_write_reg(dev, 0x4a, 0x16, 0x00);
> > + smi2021_write_reg(dev, 0x4a, 0x17, 0x00);
>
> Oops, magic numbers!
>

yep, will fix.
> > +
> > + rc = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
> > + 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> > + 0x01, 0x00, data, sizeof(data), 1000);
> > + if (rc < 0) {
> > + smi2021_err("Could not start device!\n");
> > + goto out_unlock;
> > + }
> > +
> > + /* It's mandatory to set alt interface before allocating isoc buffer */
> > + usb_set_interface(dev->udev, 0, 2);
> > +
> > + smi2021_write_reg(dev, 0, 0x1740, 0x1d);
> > +
> > + if (!dev->isoc_ctl.num_bufs) {
> > + rc = smi2021_alloc_isoc(dev);
> > + if (rc < 0)
> > + goto out_stop_hw;
> > + }
> > +
> > + /* submit urbs and enable IRQ */
> > + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
> > + rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_KERNEL);
> > + if (rc) {
> > + smi2021_err("cannot submit urb[%d] (%d)\n", i, rc);
> > + goto out_uninit;
> > + }
> > + }
> > +
> > + mutex_unlock(&dev->v4l2_lock);
> > +
> > + smi2021_dbg("Streaming started!");
> > + return 0;
> > +
> > +out_uninit:
> > + smi2021_uninit_isoc(dev);
> > +out_stop_hw:
> > + usb_set_interface(dev->udev, 0, 0);
> > + smi2021_clear_queue(dev);
> > +
> > +out_unlock:
> > + mutex_unlock(&dev->v4l2_lock);
> > +
> > + return rc;
> > +}
> > +
> > +/* Must be called with v4l2_lock hold */
> > +static void smi2021_stop_hw(struct smi2021_dev *dev)
> > +{
> > + int rc = 0;
> > + u8 data[] = { 0x01, 0x03 };
> > +
> > + if (!dev->udev)
> > + return;
> > +
> > + usb_set_interface(dev->udev, 0, 0);
> > +
> > + rc = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
> > + 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> > + 0x01, 0x00, data, sizeof(data), 1000);
> > + if (rc < 0)
> > + smi2021_err("Could not stop device!\n");
> > +
> > + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
> > +}
> > +
> > +static int smi2021_stop_streaming(struct smi2021_dev *dev)
> > +{
> > + /* HACK: Stop the audio subsystem,
> > + * without this, the pcm middle-layer will hang waiting for more data.
> > + *
> > + * Is there a better way to do this?
> > + */
> > + if (dev->pcm_substream && dev->pcm_substream->runtime) {
> > + struct snd_pcm_runtime *runtime = dev->pcm_substream->runtime;
> > + if (runtime->status) {
> > + runtime->status->state = SNDRV_PCM_STATE_DRAINING;
> > + wake_up(&runtime->sleep);
> > + }
> > + }
> > +
> > + if (mutex_lock_interruptible(&dev->v4l2_lock))
> > + return -ERESTARTSYS;
> > +
> > + smi2021_cancel_isoc(dev);
> > + smi2021_free_isoc(dev);
> > + smi2021_stop_hw(dev);
> > + smi2021_clear_queue(dev);
> > +
> > + smi2021_dbg("Streaming stopped!\n");
> > +
> > + mutex_unlock(&dev->v4l2_lock);
> > +
> > + return 0;
> > +}
> > +
> > +static struct v4l2_file_operations smi2021_fops = {
> > + .owner = THIS_MODULE,
> > + .open = v4l2_fh_open,
> > + .release = vb2_fop_release,
> > + .read = vb2_fop_read,
> > + .poll = vb2_fop_poll,
> > + .mmap = vb2_fop_mmap,
> > + .unlocked_ioctl = video_ioctl2,
> > +};
> > +
> > +/******************************************************************************/
> > +/* */
> > +/* Vidioc IOCTLS */
> > +/* */
> > +/******************************************************************************/
> > +
> > +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
> > + struct v4l2_fmtdesc *f)
> > +{
> > + if (f->index != 0)
> > + return -EINVAL;
> > +
> > + strlcpy(f->description, format[f->index].name, sizeof(f->description));
> > + f->pixelformat = format[f->index].fourcc;
> > +
> > + return 0;
> > +}
> > +
> > +static int vidioc_querycap(struct file *file, void *priv,
> > + struct v4l2_capability *cap)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > +
> > + strcpy(cap->driver, "smi2021");
> > + strcpy(cap->card, "smi2021");
> > + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
> > + cap->device_caps =
> > + V4L2_CAP_VIDEO_CAPTURE |
> > + V4L2_CAP_STREAMING |
> > + V4L2_CAP_READWRITE;
> > + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
> > +
> > + return 0;
> > +}
> > +
> > +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > +
> > + f->fmt.pix.pixelformat = dev->fmt->fourcc;
> > + f->fmt.pix.width = dev->width;
> > + f->fmt.pix.height = dev->height;
> > + f->fmt.pix.field = V4L2_FIELD_INTERLACED;
> > + f->fmt.pix.bytesperline = dev->width * 2;
> > + f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline;
> > + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
> > + f->fmt.pix.priv = 0;
> > +
> > + return 0;
> > +}
> > +
> > +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > +
> > + f->fmt.pix.pixelformat = dev->fmt->fourcc;
> > + f->fmt.pix.width = dev->width;
> > + f->fmt.pix.height = dev->height;
> > + f->fmt.pix.field = V4L2_FIELD_INTERLACED;
> > + f->fmt.pix.bytesperline = dev->width * 2;
> > + f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline;
> > + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
> > + f->fmt.pix.priv = 0;
> > +
> > + return 0;
> > +}
> > +
> > +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > + struct vb2_queue *q = &dev->vb_vidq;
> > +
> > + if (vb2_is_busy(q))
> > + return -EBUSY;
> > +
> > + vidioc_try_fmt_vid_cap(file, priv, f);
> > + return 0;
> > +}
> > +
> > +static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > +
> > + v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm);
> > + return 0;
> > +}
> > +
> > +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > +
> > + *norm = dev->norm;
> > + return 0;
> > +}
> > +
> > +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > + struct vb2_queue *q = &dev->vb_vidq;
> > +
> > + if (vb2_is_busy(q))
> > + return -EBUSY;
> > +
> > + if (!dev->udev)
> > + return -ENODEV;
> > +
> > + dev->norm = *norm;
> > + if (dev->norm & V4L2_STD_525_60) {
> > + dev->width = SMI2021_BYTES_PER_LINE / 2;
> > + dev->height = SMI2021_NTSC_LINES;
> > + } else if (dev->norm & V4L2_STD_625_50) {
> > + dev->width = SMI2021_BYTES_PER_LINE / 2;
> > + dev->height = SMI2021_PAL_LINES;
> > + } else {
> > + smi2021_err("Invalid standard\n");
> > + return -EINVAL;
> > + }
> > +
> > + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
> > + return 0;
> > +}
> > +
> > +static int vidioc_enum_input(struct file *file, void *priv,
> > + struct v4l2_input *i)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > +
> > + if (i->index >= inputs)
> > + return -EINVAL;
> > +
> > + strlcpy(i->name, input[i->index].name, sizeof(i->name));
> > + i->type = V4L2_INPUT_TYPE_CAMERA;
> > + i->std = dev->vdev.tvnorms;
> > + return 0;
> > +}
> > +
> > +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > + *i = dev->ctl_input;
> > + return 0;
> > +}
> > +
> > +static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > +
> > + if (i >= inputs)
> > + return -EINVAL;
> > +
> > + dev->ctl_input = i;
> > + smi2021_set_input(dev);
> > +
> > + return 0;
> > +}
> > +
> > +static int vidioc_g_chip_ident(struct file *file, void *priv,
> > + struct v4l2_dbg_chip_ident *chip)
> > +{
> > + switch (chip->match.type) {
> > + case V4L2_CHIP_MATCH_HOST:
> > + chip->ident = V4L2_IDENT_NONE;
> > + chip->revision = 0;
> > + return 0;
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static const struct v4l2_ioctl_ops smi2021_ioctl_ops = {
> > + .vidioc_querycap = vidioc_querycap,
> > + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
> > + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
> > + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
> > + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
> > + .vidioc_querystd = vidioc_querystd,
> > + .vidioc_g_std = vidioc_g_std,
> > + .vidioc_s_std = vidioc_s_std,
> > + .vidioc_enum_input = vidioc_enum_input,
> > + .vidioc_g_input = vidioc_g_input,
> > + .vidioc_s_input = vidioc_s_input,
> > +
> > + /* vb2 handle these */
> > + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> > + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> > + .vidioc_querybuf = vb2_ioctl_querybuf,
> > + .vidioc_qbuf = vb2_ioctl_qbuf,
> > + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> > + .vidioc_streamon = vb2_ioctl_streamon,
> > + .vidioc_streamoff = vb2_ioctl_streamoff,
> > +
> > + .vidioc_log_status = v4l2_ctrl_log_status,
> > + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> > + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> > + .vidioc_g_chip_ident = vidioc_g_chip_ident,
> > +
> > +};
> > +
> > +/******************************************************************************/
> > +/* */
> > +/* Videobuf2 operations */
> > +/* */
> > +/******************************************************************************/
> > +static int queue_setup(struct vb2_queue *vq,
> > + const struct v4l2_format *v4l2_fmt,
> > + unsigned int *nbuffers, unsigned int *nplanes,
> > + unsigned int sizes[], void *alloc_ctxs[])
> > +{
> > + struct smi2021_dev *dev = vb2_get_drv_priv(vq);
> > + unsigned long size;
> > +
> > + size = dev->width * dev->height * 2;
> > +
> > + *nbuffers = clamp_t(unsigned int, *nbuffers, 2, 4);
> > +
> > + /* Packed color format */
> > + *nplanes = 1;
> > + sizes[0] = size;
> > +
> > + return 0;
> > +}
> > +
> > +static void buffer_queue(struct vb2_buffer *vb)
> > +{
> > + unsigned long flags;
> > + struct smi2021_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
> > + struct smi2021_buffer *buf = container_of(vb, struct smi2021_buffer,
> > + vb);
> > +
> > + spin_lock_irqsave(&dev->buf_lock, flags);
> > + if (!dev->udev) {
> > + /*
> > + * If the device is disconnected return the buffer to userspace
> > + * directly. The next QBUF call will fail with -ENODEV.
> > + */
> > + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> > + } else {
> > + buf->mem = vb2_plane_vaddr(vb, 0);
> > + buf->length = vb2_plane_size(vb, 0);
> > +
> > + buf->pos = 0;
> > + buf->trc_av = 0;
> > + buf->in_blank = true;
> > + buf->second_field = false;
> > +
> > + if (buf->length < dev->width * dev->height * 2)
> > + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> > + else
> > + list_add_tail(&buf->list, &dev->avail_bufs);
> > + }
> > + spin_unlock_irqrestore(&dev->buf_lock, flags);
> > +}
> > +
> > +static int start_streaming(struct vb2_queue *vq, unsigned int count)
> > +{
> > + struct smi2021_dev *dev = vb2_get_drv_priv(vq);
> > + return smi2021_start_streaming(dev);
> > +}
> > +
> > +static int stop_streaming(struct vb2_queue *vq)
> > +{
> > + struct smi2021_dev *dev = vb2_get_drv_priv(vq);
> > + return smi2021_stop_streaming(dev);
> > +}
> > +
> > +static struct vb2_ops smi2021_video_qops = {
> > + .queue_setup = queue_setup,
> > + .buf_queue = buffer_queue,
> > + .start_streaming = start_streaming,
> > + .stop_streaming = stop_streaming,
> > + .wait_prepare = vb2_ops_wait_prepare,
> > + .wait_finish = vb2_ops_wait_finish,
> > +};
> > +
> > +static struct video_device v4l2_template = {
> > + .name = "easycap_smi2021_dc60",
> > + .tvnorms = V4L2_STD_625_50 | V4L2_STD_525_60,
> > + .fops = &smi2021_fops,
> > + .ioctl_ops = &smi2021_ioctl_ops,
> > + .release = video_device_release_empty,
> > +};
> > +
> > +/* Must be called with both v4l2_lock and vb_queue_lock hold */
> > +void smi2021_clear_queue(struct smi2021_dev *dev)
> > +{
> > + struct smi2021_buffer *buf;
> > + unsigned long flags;
> > +
> > + spin_lock_irqsave(&dev->buf_lock, flags);
> > + while (!list_empty(&dev->avail_bufs)) {
> > + buf = list_first_entry(&dev->avail_bufs,
> > + struct smi2021_buffer, list);
> > + list_del(&buf->list);
> > + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> > + }
> > + dev->isoc_ctl.buf = NULL;
> > + spin_unlock_irqrestore(&dev->buf_lock, flags);
> > +}
> > +
> > +int smi2021_vb2_setup(struct smi2021_dev *dev)
> > +{
> > + int rc;
> > + struct vb2_queue *q;
> > +
> > + q = &dev->vb_vidq;
> > + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> > + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
> > + q->drv_priv = dev;
> > + q->buf_struct_size = sizeof(struct smi2021_buffer);
> > + q->ops = &smi2021_video_qops;
> > + q->mem_ops = &vb2_vmalloc_memops;
> > +
> > + rc = vb2_queue_init(q);
> > + if (rc < 0)
> > + return rc;
> > +
> > + /* Initialize video dma queue */
> > + INIT_LIST_HEAD(&dev->avail_bufs);
> > +
> > + return 0;
> > +}
> > +
> > +int smi2021_video_register(struct smi2021_dev *dev)
> > +{
> > + int rc;
> > +
> > + dev->vdev = v4l2_template;
> > + dev->vdev.debug = 0;
> > + dev->vdev.queue = &dev->vb_vidq;
> > +
> > + dev->vdev.lock = &dev->v4l2_lock;
> > + dev->vdev.queue->lock = &dev->vb_queue_lock;
> > +
> > + dev->vdev.v4l2_dev = &dev->v4l2_dev;
> > + set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags);
> > +
> > + /* PAL is default */
> > + dev->norm = V4L2_STD_PAL;
> > + dev->width = SMI2021_BYTES_PER_LINE / 2;
> > + dev->height = SMI2021_PAL_LINES;
> > +
> > + dev->fmt = &format[0];
> > +
> > + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
> > + smi2021_set_input(dev);
> > +
> > + video_set_drvdata(&dev->vdev, dev);
> > + rc = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1);
> > + if (rc < 0) {
> > + smi2021_err("video_register_device failed %d\n", rc);
> > + return rc;
> > + }
> > +
> > + v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
> > + video_device_node_name(&dev->vdev));
> > +
> > + return 0;
> > +}
> > --
> > 1.8.1.1
> >
>
> --
> Ezequiel García, Free Electrons
> Embedded Linux, Kernel and Android Engineering
> http://free-electrons.com

2013-03-18 00:05:14

by Ezequiel Garcia

[permalink] [raw]
Subject: Re: [RFC V1 0/8] Add a driver for somagic smi2021

Hi Jon,

On Sun, Mar 17, 2013 at 09:01:58PM +0100, Jon Arne Jørgensen wrote:
> On Fri, Mar 15, 2013 at 09:08:58AM -0300, Ezequiel Garcia wrote:
> > On Thu, Mar 14, 2013 at 03:06:56PM +0100, Jon Arne Jørgensen wrote:
> > > This patch-set will add a driver for the Somagic SMI2021 chip.
> > >
> > > This chip is found inside different usb video-capture devices.
> > > Most of them are branded as EasyCap, but there also seems to be
> > > some other brands selling devices with this chip.
> > >
> > > This driver is split into two modules, where one is called smi2021-bootloader,
> > > and the other is just called smi2021.
> > >
> > > The bootloader is responsible for the upload of a firmware that is needed by some
> > > versions of the devices.
> > >
> > > All Somagic devices that need firmware seems to identify themselves
> > > with the usb product id 0x0007. There is no way for the kernel to know
> > > what firmware to upload to the device without user interaction.
> > >
> > > If there is only one firmware present on the computer, the kernel
> > > will upload that firmware to any device that identifies as 0x0007.
> > > If there are multiple Somagic firmwares present, the user will have to pass
> > > a module parameter to the smi2021-bootloader module to tell what firmware to use.
> > >
> >
> > Nice job!
> >
> Thanks :)
>
> > I have some minor comments on each patch, but also I don't agree
> > with the patch splitting: what's the point in splitting and sending
> > one patch per file?
> >
> > It doesn't make it any easier to review, so why don't you just
> > send one patch: "Introduce smi2021 driver"?
> >
> > The rule is one patch per change, and I believe this whole patchset
> > is just one change: adding a new driver.
> >
>
> I think I read another patch to this mailinglist, where someone was told
> to split his patch into one mail per file, but I can't find that thread
> now :)
>
> I will send the next version as a single mail, and see what happens...
>

As you will soon realize, the patch preparation is equally important as the
patch content itself. Often, it takes the same time to implement or
fix something, as it takes to prepare the patchset carefully.

When deciding how to prepare your patches you have to keep two main things in mind:

* The kernel build can never be broken, in any configuration and in any
point of the kernel history (in other words, by any patch of a patchset).
This is called 'keep the bisectability' because it's essential
to make 'git bisect' work properly.

* Do as much as possible to facilitate reviews from other people.
This is also important because patches tend to be accepted quicker
if they recieve attention (reviews, testing, etc.).

In this particular case, I think that the easier way to review is to be
able to see the complete driver in a single patch. Of course, I can be
wrong, so feel free to correct me.

Please note that the reviews I made where almost nitpicks, and the
driver looks good in general. I cannot provide any testing for lack of hardware.

Also, for your next patch, add the output of v4l2-compliance tool,
showing it passes all the tests. This shows your driver is in good shape.
Get v4l2-compliancefrom the git repo, as distribution often provide
an outdated version.

(And another thing, please fix your mail client: the reply-to is pointing
to '[email protected]'.)

Thanks for your work and best regards,
--
Ezequiel García, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com

2013-03-18 08:05:10

by Hans Verkuil

[permalink] [raw]
Subject: Re: [RFC V1 3/8] smi2021: Add smi2021_i2c.c

On Thu March 14 2013 15:06:59 Jon Arne Jørgensen wrote:
> This file is responsible for registering the device
> with the kernel i2c subsystem.
> v4l2 talks to the saa7113 chip of the device via i2c.
>
> Signed-off-by: Jon Arne Jørgensen <[email protected]>
> ---
> drivers/media/usb/smi2021/smi2021_i2c.c | 160 ++++++++++++++++++++++++++++++++
> 1 file changed, 160 insertions(+)
> create mode 100644 drivers/media/usb/smi2021/smi2021_i2c.c
>
> diff --git a/drivers/media/usb/smi2021/smi2021_i2c.c b/drivers/media/usb/smi2021/smi2021_i2c.c
> new file mode 100644
> index 0000000..5b6f3f5
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/smi2021_i2c.c
> @@ -0,0 +1,160 @@
> +/*******************************************************************************
> + * smi2021_i2c.c *
> + * *
> + * USB Driver for SMI2021 - EasyCAP *
> + * USB ID 1c88:003c *
> + * *
> + * *****************************************************************************
> + *
> + * Copyright 2011-2013 Jon Arne Jørgensen
> + * <jonjon.arnearne--a.t--gmail.com>
> + *
> + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> + *
> + * This file is part of SMI2021
> + * http://code.google.com/p/easycap-somagic-linux/
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + * This driver is heavily influensed by the STK1160 driver.
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + */
> +
> +#include "smi2021.h"
> +
> +/* The device will not return the chip_name on address 0x00.
> + * But the saa7115 i2c driver needs the chip id to match "f7113"
> + * if we want to use it,
> + * so we have to fake the return of this value
> + */
> +
> +static char chip_id[] = { 'x', 255, 55, 49, 49, 115, 0 };
> +static int id_ptr;

Don't do this! Instead modify saa7115.c and add a new chip_id (the name of
this saa7113 clone) and specify that in v4l2_i2c_new_subdev instead of "saa7113".
The saa7115.c driver should skip the chip ID test if that name is specified.

Then you don't need this hack anymore and everything will look much cleaner.

> +
> +static unsigned int i2c_debug;
> +module_param(i2c_debug, int, 0644);
> +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
> +
> +#define dprint_i2c(fmt, args...) \
> +do { \
> + if (i2c_debug) \
> + pr_debug("smi2021[i2c]::%s: " fmt, __func__, ##args); \
> +} while (0)
> +
> +
> +static int i2c_xfer(struct i2c_adapter *i2c_adap,
> + struct i2c_msg msgs[], int num)
> +{
> + struct smi2021_dev *dev = i2c_adap->algo_data;
> +
> + switch (num) {
> + case 2: { /* Read reg */
> + if (msgs[0].len != 1 || msgs[1].len != 1) {
> + dprint_i2c("both messages must be 1 byte\n");
> + goto err_out;
> +
> + if ((msgs[1].flags & I2C_M_RD) != I2C_M_RD)
> + dprint_i2c("last message should have rd flag\n");
> + goto err_out;
> + }
> +
> + if (msgs[0].buf[0] == 0) {
> + msgs[1].buf[0] = chip_id[id_ptr];
> + if (chip_id[id_ptr] != 0)
> + id_ptr += 1;
> + } else {
> + smi2021_read_reg(dev, msgs[0].addr, msgs[0].buf[0],
> + msgs[1].buf);
> + }
> + break;
> + }
> + case 1: { /* Write reg */
> + if (msgs[0].len == 0) {
> + break;
> + } else if (msgs[0].len != 2) {
> + dprint_i2c("unsupported len\n");
> + goto err_out;
> + }
> + if (msgs[0].buf[0] == 0) {
> + /* We don't handle writing to addr 0x00 */
> + break;
> + }
> +
> + smi2021_write_reg(dev, msgs[0].addr, msgs[0].buf[0],
> + msgs[0].buf[1]);
> + break;
> + }
> + default: {
> + dprint_i2c("driver can only handle 1 or 2 messages\n");
> + goto err_out;
> + }
> + }
> + return num;
> +
> +err_out:
> + return -EOPNOTSUPP;
> +}
> +
> +static u32 functionality(struct i2c_adapter *adap)
> +{
> + return I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static struct i2c_algorithm algo = {
> + .master_xfer = i2c_xfer,
> + .functionality = functionality,
> +};
> +
> +static struct i2c_adapter adap_template = {
> + .owner = THIS_MODULE,
> + .name = "smi2021_easycap_dc60",
> + .algo = &algo,
> +};
> +
> +static struct i2c_client client_template = {
> + .name = "smi2021 internal",
> +};
> +
> +int smi2021_i2c_register(struct smi2021_dev *dev)
> +{
> + int rc;
> +
> + id_ptr = 0;
> +
> + dev->i2c_adap = adap_template;
> + dev->i2c_adap.dev.parent = dev->dev;
> + strcpy(dev->i2c_adap.name, "smi2021");
> + dev->i2c_adap.algo_data = dev;
> +
> + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
> +
> + rc = i2c_add_adapter(&dev->i2c_adap);
> + if (rc < 0) {
> + smi2021_err("can't add i2c adapter, errno: %d\n", rc);
> + return rc;
> + }
> +
> + dev->i2c_client = client_template;
> + dev->i2c_client.adapter = &dev->i2c_adap;
> +
> + return 0;
> +}
> +
> +int smi2021_i2c_unregister(struct smi2021_dev *dev)
> +{
> + i2c_del_adapter(&dev->i2c_adap);
> + return 0;
> +}
>

Regards,

Hans

2013-03-18 08:10:36

by Hans Verkuil

[permalink] [raw]
Subject: Re: [RFC V1 2/8] smi2021: Add smi2021_main.c

Hi Jon Arne,

Here is my review of this driver.

Regards,

Hans

On Thu March 14 2013 15:06:58 Jon Arne Jørgensen wrote:
> This is the core of the smi2021 module.
> It will register the module with the kernel, and register the
> usb probe function.
>
> Signed-off-by: Jon Arne Jørgensen <[email protected]>
> ---
> drivers/media/usb/smi2021/smi2021_main.c | 339 +++++++++++++++++++++++++++++++
> 1 file changed, 339 insertions(+)
> create mode 100644 drivers/media/usb/smi2021/smi2021_main.c
>
> diff --git a/drivers/media/usb/smi2021/smi2021_main.c b/drivers/media/usb/smi2021/smi2021_main.c
> new file mode 100644
> index 0000000..cc600e7
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/smi2021_main.c
> @@ -0,0 +1,339 @@
> +/*******************************************************************************
> + * smi2021_main.c *
> + * *
> + * USB Driver for SMI2021 - EasyCAP *
> + * USB ID 1c88:003c *
> + * *
> + * *****************************************************************************
> + *
> + * Copyright 2011-2013 Jon Arne Jørgensen
> + * <jonjon.arnearne--a.t--gmail.com>
> + *
> + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> + *
> + * This file is part of SMI2021
> + * http://code.google.com/p/easycap-somagic-linux/
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + * This driver is heavily influensed by the STK1160 driver.
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + */
> +
> +#include "smi2021.h"
> +
> +#define VENDOR_ID 0x1c88
> +
> +static unsigned int imput;
> +module_param(imput, int, 0644);
> +MODULE_PARM_DESC(input, "Set default input");
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Jon Arne Jørgensen <jonjon.arnearne--a.t--gmail.com>");
> +MODULE_DESCRIPTION("SMI2021 - EasyCap");
> +MODULE_VERSION(SMI2021_DRIVER_VERSION);
> +
> +
> +struct usb_device_id smi2021_usb_device_id_table[] = {

Should be static const

> + { USB_DEVICE(VENDOR_ID, 0x003c) },
> + { USB_DEVICE(VENDOR_ID, 0x003d) },
> + { USB_DEVICE(VENDOR_ID, 0x003e) },
> + { USB_DEVICE(VENDOR_ID, 0x003f) },
> + { }
> +};
> +
> +MODULE_DEVICE_TABLE(usb, smi2021_usb_device_id_table);
> +
> +static unsigned short saa7113_addrs[] = {
> + 0x4a,
> + I2C_CLIENT_END
> +};

Don't use this. See below for more info.

> +
> +/******************************************************************************/
> +/* */
> +/* Write to saa7113 */
> +/* */
> +/******************************************************************************/
> +
> +inline int transfer_usb_ctrl(struct smi2021_dev *dev, u8 *data, int len)
> +{
> + return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
> + 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> + 0x0b, 0x00,
> + data, len, 1000);
> +}
> +
> +int smi2021_write_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 val)
> +{
> + int rc;
> + u8 snd_data[8];
> +
> + memset(snd_data, 0x00, 8);
> +
> + snd_data[SMI2021_CTRL_HEAD] = 0x0b;
> + snd_data[SMI2021_CTRL_ADDR] = addr;
> + snd_data[SMI2021_CTRL_DATA_SIZE] = 0x01;
> +
> + if (addr) {
> + /* This is I2C data for the saa7113 chip */
> + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0xc0;
> + snd_data[SMI2021_CTRL_BM_DATA_OFFSET] = 0x01;
> +
> + snd_data[SMI2021_CTRL_I2C_REG] = reg;
> + snd_data[SMI2021_CTRL_I2C_VAL] = val;
> + } else {
> + /* This is register settings for the smi2021 chip */
> + snd_data[SMI2021_CTRL_BM_DATA_OFFSET] = 0x82;
> +
> + snd_data[SMI2021_CTRL_REG_HI] = __cpu_to_be16(reg) >> 8;
> + snd_data[SMI2021_CTRL_REG_LO] = __cpu_to_be16(reg);

This looks really weird. On a little endian system REG_HI is now initialized
with the LSB, not MSB and the other way around on a big endian system.
The _cpu_to_be16 certainly doesn't belong here.

> +
> + }
> +
> + rc = transfer_usb_ctrl(dev, snd_data, 8);
> + if (rc < 0) {
> + smi2021_warn("write failed on register 0x%x, errno: %d\n",
> + reg, rc);
> + return rc;
> + }
> +
> + return 0;
> +}
> +
> +int smi2021_read_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 *val)
> +{
> + int rc;
> + u8 rcv_data[13];
> + u8 snd_data[8];
> + memset(rcv_data, 0x00, 13);
> + memset(snd_data, 0x00, 8);
> +
> + snd_data[SMI2021_CTRL_HEAD] = 0x0b;
> + snd_data[SMI2021_CTRL_ADDR] = addr;
> + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0x84;
> + snd_data[SMI2021_CTRL_DATA_SIZE] = 0x01;
> + snd_data[SMI2021_CTRL_I2C_REG] = reg;
> +
> + *val = 0;
> +
> + rc = transfer_usb_ctrl(dev, snd_data, 8);
> + if (rc < 0) {
> + smi2021_warn(
> + "1st pass failing to read reg 0x%x, usb-errno: %d\n",
> + reg, rc);
> + return rc;
> + }
> +
> + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0xa0;
> + rc = transfer_usb_ctrl(dev, snd_data, 8);
> + if (rc < 0) {
> + smi2021_warn(
> + "2nd pass failing to read reg 0x%x, usb-errno: %d\n",
> + reg, rc);
> + return rc;
> + }
> +
> + rc = usb_control_msg(dev->udev,
> + usb_rcvctrlpipe(dev->udev, 0x80), 0x01,
> + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> + 0x0b, 0x00, rcv_data, 13, 1000);
> + if (rc < 0) {
> + smi2021_warn("Failed to read reg 0x%x, usb-errno: %d\n",
> + reg, rc);
> + return rc;
> + }
> +
> + *val = rcv_data[SMI2021_CTRL_I2C_RCV_VAL];
> + return 0;
> +}
> +
> +static void smi2021_reset_device(struct smi2021_dev *dev)
> +{
> + smi2021_write_reg(dev, 0, 0x3a, 0x80);
> + smi2021_write_reg(dev, 0, 0x3b, 0x80);
> + smi2021_write_reg(dev, 0, 0x3b, 0x00);
> +}
> +
> +static void release_v4l2_dev(struct v4l2_device *v4l2_dev)
> +{
> + struct smi2021_dev *dev = container_of(v4l2_dev, struct smi2021_dev,
> + v4l2_dev);
> + smi2021_dbg("Releasing all resources\n");
> +
> + smi2021_i2c_unregister(dev);
> +
> + v4l2_ctrl_handler_free(&dev->ctrl_handler);
> + v4l2_device_unregister(&dev->v4l2_dev);
> + kfree(dev);
> +}
> +
> +#define hb_mult(w_max_packet_size) (1 + (((w_max_packet_size) >> 11) & 0x03))
> +
> +static int smi2021_scan_usb(struct usb_interface *intf, struct usb_device *udev)
> +{
> + int i, e, ifnum, sizedescr, size;
> + const struct usb_endpoint_descriptor *desc;
> + ifnum = intf->altsetting[0].desc.bInterfaceNumber;
> +
> + for (i = 0; i < intf->num_altsetting; i++) {
> + for (e = 0; e < intf->altsetting[i].desc.bNumEndpoints; e++) {
> + desc = &intf->altsetting[i].endpoint[e].desc;
> + sizedescr = le16_to_cpu(desc->wMaxPacketSize);
> + size = sizedescr & 0x7ff;
> +
> + if (udev->speed == USB_SPEED_HIGH)
> + size = size * hb_mult(sizedescr);
> + }
> + }
> + return 0;
> +}
> +
> +/******************************************************************************/
> +/* */
> +/* DEVICE - PROBE & DISCONNECT */
> +/* */
> +/******************************************************************************/
> +static int smi2021_usb_probe(struct usb_interface *intf,
> + const struct usb_device_id *devid)
> +{
> + int rc = 0;
> + struct usb_device *udev = interface_to_usbdev(intf);
> + struct smi2021_dev *dev;
> +
> + if (udev == (struct usb_device *)NULL) {
> + smi2021_err("device is NULL\n");
> + return -EFAULT;
> + }
> +
> + smi2021_scan_usb(intf, udev);
> +
> + dev = kzalloc(sizeof(struct smi2021_dev), GFP_KERNEL);
> + if (dev == NULL)
> + return -ENOMEM;
> +
> + dev->udev = udev;
> + dev->dev = &intf->dev;
> + usb_set_intfdata(intf, dev);
> +
> + /* Initialize videobuf2 stuff */
> + rc = smi2021_vb2_setup(dev);
> + if (rc < 0)
> + goto free_err;
> +
> + spin_lock_init(&dev->buf_lock);
> + mutex_init(&dev->v4l2_lock);
> + mutex_init(&dev->vb_queue_lock);
> +
> + rc = v4l2_ctrl_handler_init(&dev->ctrl_handler, 0);
> + if (rc) {
> + smi2021_err("v4l2_ctrl_handler_init failed with: %d\n", rc);
> + goto free_err;
> + }
> +
> + dev->v4l2_dev.release = release_v4l2_dev;
> + dev->v4l2_dev.ctrl_handler = &dev->ctrl_handler;
> + rc = v4l2_device_register(dev->dev, &dev->v4l2_dev);
> + if (rc) {
> + smi2021_err("v4l2_device_register failed with %d\n", rc);
> + goto free_ctrl;
> + }
> +
> + smi2021_reset_device(dev);
> +
> + rc = smi2021_i2c_register(dev);
> + if (rc < 0)
> + goto unreg_v4l2;
> +
> + dev->sd_saa7113 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
> + "saa7113", 0, saa7113_addrs);

Fill in the addr argument directly and use NULL for probe_addrs. Probing is
not something new drivers should do.

See also my comments in the i2c.c review.

> +
> + smi2021_dbg("Driver version %s successfully loaded\n",
> + SMI2021_DRIVER_VERSION);
> +
> + v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0);
> + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
> +
> + rc = smi2021_snd_register(dev);
> + if (rc < 0)
> + goto unreg_i2c;
> +
> + rc = smi2021_video_register(dev);
> + if (rc < 0)
> + goto unreg_snd;
> +
> + return 0;
> +
> +unreg_snd:
> + smi2021_snd_unregister(dev);
> +unreg_i2c:
> + smi2021_i2c_unregister(dev);
> +unreg_v4l2:
> + v4l2_device_unregister(&dev->v4l2_dev);
> +free_ctrl:
> + v4l2_ctrl_handler_free(&dev->ctrl_handler);
> +free_err:
> + kfree(dev);
> +
> + return rc;
> +}
> +
> +static void smi2021_usb_disconnect(struct usb_interface *intf)
> +{
> + struct smi2021_dev *dev = usb_get_intfdata(intf);
> +
> + smi2021_dbg("Going for release!\n");
> +
> + usb_set_intfdata(intf, NULL);
> +
> + mutex_lock(&dev->vb_queue_lock);
> + mutex_lock(&dev->v4l2_lock);
> +
> + smi2021_uninit_isoc(dev);
> + smi2021_clear_queue(dev);
> +
> + video_unregister_device(&dev->vdev);
> + v4l2_device_disconnect(&dev->v4l2_dev);
> +
> + /* This way current users can detect device is gone */
> + dev->udev = NULL;
> +
> + mutex_unlock(&dev->v4l2_lock);
> + mutex_unlock(&dev->vb_queue_lock);
> +
> + smi2021_snd_unregister(dev);
> +
> + /*
> + * This calls release_v4l2_dev if it's the last reference.
> + * Otherwise, the release is postponed until there are no users left.
> + */
> + v4l2_device_put(&dev->v4l2_dev);
> +}
> +
> +/******************************************************************************/
> +/* */
> +/* MODULE - INIT & EXIT */
> +/* */
> +/******************************************************************************/
> +
> +struct usb_driver smi2021_usb_driver = {
> + .name = "smi2021",
> + .id_table = smi2021_usb_device_id_table,
> + .probe = smi2021_usb_probe,
> + .disconnect = smi2021_usb_disconnect
> +};
> +
> +module_usb_driver(smi2021_usb_driver);
> +
>

Regards,

Hans

2013-03-18 08:12:44

by Hans Verkuil

[permalink] [raw]
Subject: Re: [RFC V1 4/8] smi2021: Add smi2021_v4l2.c

On Thu March 14 2013 15:07:00 Jon Arne Jørgensen wrote:
> This file is responsible for registering the device with the v4l2 subsystem,
> and the communication with v4l2.
> Most of the v4l2 ioctls are just passed on to vidbuf2.
>
> Signed-off-by: Jon Arne Jørgensen <[email protected]>
> ---
> drivers/media/usb/smi2021/smi2021_v4l2.c | 566 +++++++++++++++++++++++++++++++
> 1 file changed, 566 insertions(+)
> create mode 100644 drivers/media/usb/smi2021/smi2021_v4l2.c
>
> diff --git a/drivers/media/usb/smi2021/smi2021_v4l2.c b/drivers/media/usb/smi2021/smi2021_v4l2.c
> new file mode 100644
> index 0000000..d402093
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/smi2021_v4l2.c
> @@ -0,0 +1,566 @@
> +/*******************************************************************************
> + * smi2021_v4l2.c *
> + * *
> + * USB Driver for smi2021 - EasyCap *
> + * USB ID 1c88:003c *
> + * *
> + * *****************************************************************************
> + *
> + * Copyright 2011-2013 Jon Arne Jørgensen
> + * <jonjon.arnearne--a.t--gmail.com>
> + *
> + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> + *
> + * This file is part of SMI2021
> + * http://code.google.com/p/easycap-somagic-linux/
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + * This driver is heavily influensed by the STK1160 driver.
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + */
> +
> +#include "smi2021.h"
> +
> +static struct smi2021_fmt format[] = {

const.

> + {
> + .name = "16bpp YU2, 4:2:2, packed",
> + .fourcc = V4L2_PIX_FMT_UYVY,
> + .depth = 16,
> + }
> +};
> +
> +static const int inputs = 2;
> +static struct smi2021_input input[] = {
> + {
> + .name = "Composite",
> + .type = SAA7115_COMPOSITE0,
> + },
> + {
> + .name = "S-Video",
> + .type = SAA7115_SVIDEO1,
> + }
> +};

const

> +
> +static void smi2021_set_input(struct smi2021_dev *dev)
> +{
> + if (dev->udev == NULL)
> + return;

These udev tests are unnecessary. Once the video_unregister is called no
new ioctls or file operations (except release()) can ever be issued.

> +
> + if (dev->ctl_input >= inputs) {
> + smi2021_err("BUG: ctl_input to big!\n");
> + return;
> + }
> +
> + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
> + input[dev->ctl_input].type, 0, 0);
> +}
> +
> +static int smi2021_start_streaming(struct smi2021_dev *dev)
> +{
> + u8 data[2];
> + int i, rc = 0;

Extra newline.

> + data[0] = 0x01;
> + data[1] = 0x05;
> +
> + if (!dev->udev)
> + return -ENODEV;
> +
> + dev->sync_state = HSYNC;
> + dev->buf_count = 0;
> +
> + if (mutex_lock_interruptible(&dev->v4l2_lock))
> + return -ERESTARTSYS;
> +
> + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1);
> +
> + /* The saa 7115 driver sets these wrong,
> + * this leads to sync issues.
> + * V_GATE1_START
> + * V_GATE1_STOP
> + * V_GATE1_MSB
> + * All these should be 0x00 for this device.
> + */
> + smi2021_write_reg(dev, 0x4a, 0x15, 0x00);
> + smi2021_write_reg(dev, 0x4a, 0x16, 0x00);
> + smi2021_write_reg(dev, 0x4a, 0x17, 0x00);
> +
> + rc = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
> + 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> + 0x01, 0x00, data, sizeof(data), 1000);
> + if (rc < 0) {
> + smi2021_err("Could not start device!\n");
> + goto out_unlock;
> + }
> +
> + /* It's mandatory to set alt interface before allocating isoc buffer */
> + usb_set_interface(dev->udev, 0, 2);
> +
> + smi2021_write_reg(dev, 0, 0x1740, 0x1d);
> +
> + if (!dev->isoc_ctl.num_bufs) {
> + rc = smi2021_alloc_isoc(dev);
> + if (rc < 0)
> + goto out_stop_hw;
> + }
> +
> + /* submit urbs and enable IRQ */
> + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
> + rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_KERNEL);
> + if (rc) {
> + smi2021_err("cannot submit urb[%d] (%d)\n", i, rc);
> + goto out_uninit;
> + }
> + }
> +
> + mutex_unlock(&dev->v4l2_lock);
> +
> + smi2021_dbg("Streaming started!");
> + return 0;
> +
> +out_uninit:
> + smi2021_uninit_isoc(dev);
> +out_stop_hw:
> + usb_set_interface(dev->udev, 0, 0);
> + smi2021_clear_queue(dev);
> +
> +out_unlock:
> + mutex_unlock(&dev->v4l2_lock);
> +
> + return rc;
> +}
> +
> +/* Must be called with v4l2_lock hold */
> +static void smi2021_stop_hw(struct smi2021_dev *dev)
> +{
> + int rc = 0;
> + u8 data[] = { 0x01, 0x03 };
> +
> + if (!dev->udev)
> + return;
> +
> + usb_set_interface(dev->udev, 0, 0);
> +
> + rc = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
> + 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> + 0x01, 0x00, data, sizeof(data), 1000);
> + if (rc < 0)
> + smi2021_err("Could not stop device!\n");
> +
> + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
> +}
> +
> +static int smi2021_stop_streaming(struct smi2021_dev *dev)
> +{
> + /* HACK: Stop the audio subsystem,
> + * without this, the pcm middle-layer will hang waiting for more data.
> + *
> + * Is there a better way to do this?
> + */
> + if (dev->pcm_substream && dev->pcm_substream->runtime) {
> + struct snd_pcm_runtime *runtime = dev->pcm_substream->runtime;
> + if (runtime->status) {
> + runtime->status->state = SNDRV_PCM_STATE_DRAINING;
> + wake_up(&runtime->sleep);
> + }
> + }
> +
> + if (mutex_lock_interruptible(&dev->v4l2_lock))
> + return -ERESTARTSYS;
> +
> + smi2021_cancel_isoc(dev);
> + smi2021_free_isoc(dev);
> + smi2021_stop_hw(dev);
> + smi2021_clear_queue(dev);
> +
> + smi2021_dbg("Streaming stopped!\n");
> +
> + mutex_unlock(&dev->v4l2_lock);
> +
> + return 0;
> +}
> +
> +static struct v4l2_file_operations smi2021_fops = {
> + .owner = THIS_MODULE,
> + .open = v4l2_fh_open,
> + .release = vb2_fop_release,
> + .read = vb2_fop_read,
> + .poll = vb2_fop_poll,
> + .mmap = vb2_fop_mmap,
> + .unlocked_ioctl = video_ioctl2,
> +};
> +
> +/******************************************************************************/
> +/* */
> +/* Vidioc IOCTLS */
> +/* */
> +/******************************************************************************/
> +
> +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + if (f->index != 0)
> + return -EINVAL;
> +
> + strlcpy(f->description, format[f->index].name, sizeof(f->description));
> + f->pixelformat = format[f->index].fourcc;
> +
> + return 0;
> +}
> +
> +static int vidioc_querycap(struct file *file, void *priv,
> + struct v4l2_capability *cap)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> +
> + strcpy(cap->driver, "smi2021");
> + strcpy(cap->card, "smi2021");
> + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
> + cap->device_caps =
> + V4L2_CAP_VIDEO_CAPTURE |
> + V4L2_CAP_STREAMING |
> + V4L2_CAP_READWRITE;
> + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
> +
> + return 0;
> +}
> +
> +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> +
> + f->fmt.pix.pixelformat = dev->fmt->fourcc;
> + f->fmt.pix.width = dev->width;
> + f->fmt.pix.height = dev->height;
> + f->fmt.pix.field = V4L2_FIELD_INTERLACED;
> + f->fmt.pix.bytesperline = dev->width * 2;
> + f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline;
> + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
> + f->fmt.pix.priv = 0;
> +
> + return 0;
> +}
> +
> +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> +
> + f->fmt.pix.pixelformat = dev->fmt->fourcc;
> + f->fmt.pix.width = dev->width;
> + f->fmt.pix.height = dev->height;
> + f->fmt.pix.field = V4L2_FIELD_INTERLACED;
> + f->fmt.pix.bytesperline = dev->width * 2;
> + f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline;
> + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
> + f->fmt.pix.priv = 0;
> +
> + return 0;
> +}
> +
> +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> + struct vb2_queue *q = &dev->vb_vidq;
> +
> + if (vb2_is_busy(q))
> + return -EBUSY;
> +
> + vidioc_try_fmt_vid_cap(file, priv, f);
> + return 0;
> +}
> +
> +static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> +
> + v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm);
> + return 0;
> +}
> +
> +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> +
> + *norm = dev->norm;
> + return 0;
> +}
> +
> +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> + struct vb2_queue *q = &dev->vb_vidq;
> +
> + if (vb2_is_busy(q))
> + return -EBUSY;
> +
> + if (!dev->udev)
> + return -ENODEV;
> +
> + dev->norm = *norm;
> + if (dev->norm & V4L2_STD_525_60) {
> + dev->width = SMI2021_BYTES_PER_LINE / 2;
> + dev->height = SMI2021_NTSC_LINES;
> + } else if (dev->norm & V4L2_STD_625_50) {
> + dev->width = SMI2021_BYTES_PER_LINE / 2;
> + dev->height = SMI2021_PAL_LINES;
> + } else {
> + smi2021_err("Invalid standard\n");
> + return -EINVAL;
> + }
> +
> + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
> + return 0;
> +}
> +
> +static int vidioc_enum_input(struct file *file, void *priv,
> + struct v4l2_input *i)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> +
> + if (i->index >= inputs)
> + return -EINVAL;
> +
> + strlcpy(i->name, input[i->index].name, sizeof(i->name));
> + i->type = V4L2_INPUT_TYPE_CAMERA;
> + i->std = dev->vdev.tvnorms;
> + return 0;
> +}
> +
> +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);

newline

> + *i = dev->ctl_input;
> + return 0;
> +}
> +
> +static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
> +{
> + struct smi2021_dev *dev = video_drvdata(file);
> +
> + if (i >= inputs)
> + return -EINVAL;
> +
> + dev->ctl_input = i;
> + smi2021_set_input(dev);
> +
> + return 0;
> +}
> +
> +static int vidioc_g_chip_ident(struct file *file, void *priv,
> + struct v4l2_dbg_chip_ident *chip)
> +{
> + switch (chip->match.type) {
> + case V4L2_CHIP_MATCH_HOST:
> + chip->ident = V4L2_IDENT_NONE;
> + chip->revision = 0;
> + return 0;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static const struct v4l2_ioctl_ops smi2021_ioctl_ops = {
> + .vidioc_querycap = vidioc_querycap,
> + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
> + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
> + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
> + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
> + .vidioc_querystd = vidioc_querystd,
> + .vidioc_g_std = vidioc_g_std,
> + .vidioc_s_std = vidioc_s_std,
> + .vidioc_enum_input = vidioc_enum_input,
> + .vidioc_g_input = vidioc_g_input,
> + .vidioc_s_input = vidioc_s_input,
> +
> + /* vb2 handle these */
> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> + .vidioc_querybuf = vb2_ioctl_querybuf,
> + .vidioc_qbuf = vb2_ioctl_qbuf,
> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> + .vidioc_streamon = vb2_ioctl_streamon,
> + .vidioc_streamoff = vb2_ioctl_streamoff,
> +
> + .vidioc_log_status = v4l2_ctrl_log_status,
> + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> + .vidioc_g_chip_ident = vidioc_g_chip_ident,
> +
> +};
> +
> +/******************************************************************************/
> +/* */
> +/* Videobuf2 operations */
> +/* */
> +/******************************************************************************/
> +static int queue_setup(struct vb2_queue *vq,
> + const struct v4l2_format *v4l2_fmt,
> + unsigned int *nbuffers, unsigned int *nplanes,
> + unsigned int sizes[], void *alloc_ctxs[])
> +{
> + struct smi2021_dev *dev = vb2_get_drv_priv(vq);
> + unsigned long size;
> +
> + size = dev->width * dev->height * 2;
> +
> + *nbuffers = clamp_t(unsigned int, *nbuffers, 2, 4);
> +
> + /* Packed color format */
> + *nplanes = 1;
> + sizes[0] = size;
> +
> + return 0;
> +}
> +
> +static void buffer_queue(struct vb2_buffer *vb)
> +{
> + unsigned long flags;
> + struct smi2021_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
> + struct smi2021_buffer *buf = container_of(vb, struct smi2021_buffer,
> + vb);

Just put this in one line, the dangling vb looks odd.

> +
> + spin_lock_irqsave(&dev->buf_lock, flags);
> + if (!dev->udev) {
> + /*
> + * If the device is disconnected return the buffer to userspace
> + * directly. The next QBUF call will fail with -ENODEV.
> + */
> + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> + } else {
> + buf->mem = vb2_plane_vaddr(vb, 0);
> + buf->length = vb2_plane_size(vb, 0);
> +
> + buf->pos = 0;
> + buf->trc_av = 0;
> + buf->in_blank = true;
> + buf->second_field = false;
> +
> + if (buf->length < dev->width * dev->height * 2)
> + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> + else
> + list_add_tail(&buf->list, &dev->avail_bufs);
> + }
> + spin_unlock_irqrestore(&dev->buf_lock, flags);
> +}
> +
> +static int start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> + struct smi2021_dev *dev = vb2_get_drv_priv(vq);

newline

> + return smi2021_start_streaming(dev);

Why not move smi2021_start_streaming in here?

> +}
> +
> +static int stop_streaming(struct vb2_queue *vq)
> +{
> + struct smi2021_dev *dev = vb2_get_drv_priv(vq);

newline

> + return smi2021_stop_streaming(dev);

same question.

> +}
> +
> +static struct vb2_ops smi2021_video_qops = {
> + .queue_setup = queue_setup,
> + .buf_queue = buffer_queue,
> + .start_streaming = start_streaming,
> + .stop_streaming = stop_streaming,
> + .wait_prepare = vb2_ops_wait_prepare,
> + .wait_finish = vb2_ops_wait_finish,
> +};
> +
> +static struct video_device v4l2_template = {
> + .name = "easycap_smi2021_dc60",
> + .tvnorms = V4L2_STD_625_50 | V4L2_STD_525_60,

Use V4L2_STD_ALL.

> + .fops = &smi2021_fops,
> + .ioctl_ops = &smi2021_ioctl_ops,
> + .release = video_device_release_empty,
> +};
> +
> +/* Must be called with both v4l2_lock and vb_queue_lock hold */
> +void smi2021_clear_queue(struct smi2021_dev *dev)
> +{
> + struct smi2021_buffer *buf;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&dev->buf_lock, flags);
> + while (!list_empty(&dev->avail_bufs)) {
> + buf = list_first_entry(&dev->avail_bufs,
> + struct smi2021_buffer, list);
> + list_del(&buf->list);
> + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> + }
> + dev->isoc_ctl.buf = NULL;
> + spin_unlock_irqrestore(&dev->buf_lock, flags);
> +}
> +
> +int smi2021_vb2_setup(struct smi2021_dev *dev)
> +{
> + int rc;
> + struct vb2_queue *q;
> +
> + q = &dev->vb_vidq;
> + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
> + q->drv_priv = dev;
> + q->buf_struct_size = sizeof(struct smi2021_buffer);
> + q->ops = &smi2021_video_qops;
> + q->mem_ops = &vb2_vmalloc_memops;
> +
> + rc = vb2_queue_init(q);
> + if (rc < 0)
> + return rc;
> +
> + /* Initialize video dma queue */
> + INIT_LIST_HEAD(&dev->avail_bufs);
> +
> + return 0;
> +}
> +
> +int smi2021_video_register(struct smi2021_dev *dev)
> +{
> + int rc;
> +
> + dev->vdev = v4l2_template;
> + dev->vdev.debug = 0;

No need for this, it's already 0.

> + dev->vdev.queue = &dev->vb_vidq;
> +
> + dev->vdev.lock = &dev->v4l2_lock;
> + dev->vdev.queue->lock = &dev->vb_queue_lock;
> +
> + dev->vdev.v4l2_dev = &dev->v4l2_dev;
> + set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags);
> +
> + /* PAL is default */
> + dev->norm = V4L2_STD_PAL;
> + dev->width = SMI2021_BYTES_PER_LINE / 2;
> + dev->height = SMI2021_PAL_LINES;
> +
> + dev->fmt = &format[0];
> +
> + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
> + smi2021_set_input(dev);
> +
> + video_set_drvdata(&dev->vdev, dev);
> + rc = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1);
> + if (rc < 0) {
> + smi2021_err("video_register_device failed %d\n", rc);
> + return rc;
> + }
> +
> + v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
> + video_device_node_name(&dev->vdev));
> +
> + return 0;
> +}
>

Regards,

Hans

2013-03-18 08:17:24

by Hans Verkuil

[permalink] [raw]
Subject: Re: [RFC V1 5/8] smi2021: Add smi2021_video.c

On Thu March 14 2013 15:07:01 Jon Arne Jørgensen wrote:
> This file is responsible for all communication with the video hardware
> and also starting and stopping the capture.
>
> It also contains the setup and handling of the usb ISOCHRONOUS transfers.
>
> Signed-off-by: Jon Arne Jørgensen <[email protected]>
> ---
> drivers/media/usb/smi2021/smi2021_video.c | 543 ++++++++++++++++++++++++++++++
> 1 file changed, 543 insertions(+)
> create mode 100644 drivers/media/usb/smi2021/smi2021_video.c
>
> diff --git a/drivers/media/usb/smi2021/smi2021_video.c b/drivers/media/usb/smi2021/smi2021_video.c
> new file mode 100644
> index 0000000..48f3e80
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/smi2021_video.c
> @@ -0,0 +1,543 @@
> +/*******************************************************************************
> + * smi2021_video.c *
> + * *
> + * USB Driver for SMI2021 - EasyCAP *
> + * USB ID 1c88:003c *
> + * *
> + * *****************************************************************************
> + *
> + * Copyright 2011-2013 Jon Arne Jørgensen
> + * <jonjon.arnearne--a.t--gmail.com>
> + *
> + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> + *
> + * This file is part of SMI2021x
> + * http://code.google.com/p/easycap-somagic-linux/
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + * This driver is heavily influensed by the STK1160 driver.
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + */
> +
> +#include "smi2021.h"
> +
> +static void print_usb_err(struct smi2021_dev *dev, int packet, int status)
> +{
> + char *errmsg;
> +
> + switch (status) {
> + case -ENOENT:
> + errmsg = "unlinked synchronuously";
> + break;
> + case -ECONNRESET:
> + errmsg = "unlinked asynchronuously";
> + break;
> + case -ENOSR:
> + errmsg = "Buffer error (overrun)";
> + break;
> + case -EPIPE:
> + errmsg = "Stalled (device not responding)";
> + break;
> + case -EOVERFLOW:
> + errmsg = "Babble (bad cable?)";
> + break;
> + case -EPROTO:
> + errmsg = "Bit-stuff error (bad cable?)";
> + break;
> + case -EILSEQ:
> + errmsg = "CRC/Timeout (could be anything)";
> + break;
> + case -ETIME:
> + errmsg = "Device does not respond";
> + break;
> + default:
> + errmsg = "Unknown";
> + }
> +
> + if (packet < 0) {
> + printk_ratelimited(KERN_WARNING "Urb status %d [%s]\n",
> + status, errmsg);
> + } else {
> + printk_ratelimited(KERN_INFO "URB packet %d, status %d [%s]\n",
> + packet, status, errmsg);
> + }
> +}
> +
> +static struct smi2021_buffer *smi2021_next_buffer(struct smi2021_dev *dev)
> +{
> + struct smi2021_buffer *buf = NULL;
> + unsigned long flags = 0;
> +
> + BUG_ON(dev->isoc_ctl.buf);
> +
> + spin_lock_irqsave(&dev->buf_lock, flags);
> + if (!list_empty(&dev->avail_bufs)) {
> + buf = list_first_entry(&dev->avail_bufs, struct smi2021_buffer,
> + list);
> + list_del(&buf->list);
> + }
> + spin_unlock_irqrestore(&dev->buf_lock, flags);
> +
> + return buf;
> +}
> +
> +static void smi2021_buffer_done(struct smi2021_dev *dev)
> +{
> + struct smi2021_buffer *buf = dev->isoc_ctl.buf;
> +
> + dev->buf_count++;
> +
> + buf->vb.v4l2_buf.sequence = dev->buf_count >> 1;
> + buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
> + buf->vb.v4l2_buf.bytesused = buf->pos;
> + do_gettimeofday(&buf->vb.v4l2_buf.timestamp);
> +
> + vb2_set_plane_payload(&buf->vb, 0, buf->pos);
> + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
> +
> + dev->isoc_ctl.buf = NULL;
> +}
> +
> +static void copy_video(struct smi2021_dev *dev, u8 p)
> +{
> + struct smi2021_buffer *buf = dev->isoc_ctl.buf;
> +
> + int lines_per_field = dev->height / 2;
> + int line = 0;
> + int pos_in_line = 0;
> + unsigned int offset = 0;
> + u8 *dst;
> +
> + if (buf == NULL)
> + return;
> +
> + if (buf->in_blank)
> + return;
> +
> + if (buf->pos >= buf->length) {
> + if (buf->second_field == 0) {
> + /* We are probably trying to capture from
> + * a unconnected input
> + */
> + smi2021_buffer_done(dev);
> + } else {
> + printk_ratelimited(KERN_WARNING
> + "Buffer overflow!, max: %d bytes, av_lines_found: %d, second_field: %d\n",
> + buf->length, buf->trc_av,
> + buf->second_field);
> + }
> + return;
> + }
> +
> + pos_in_line = buf->pos % SMI2021_BYTES_PER_LINE;
> + line = buf->pos / SMI2021_BYTES_PER_LINE;
> + if (line >= lines_per_field)
> + line -= lines_per_field;
> +
> + if (line != buf->trc_av - 1) {
> + /* Keep video synchronized.
> + * The device will sometimes give us to many bytes
> + * for a line, before we get a new TRC.
> + * We just drop these bytes */
> + return;
> + }
> +
> + if (buf->second_field)
> + offset += SMI2021_BYTES_PER_LINE;
> +
> + offset += (SMI2021_BYTES_PER_LINE * line * 2) + pos_in_line;
> +
> + /* Will this ever happen? */
> + if (offset >= buf->length) {
> + printk_ratelimited(KERN_INFO
> + "Offset calculation error, field: %d, line: %d, pos_in_line: %d\n",
> + buf->second_field, line, pos_in_line);
> + return;
> + }
> +
> + dst = buf->mem + offset;
> + *dst = p;
> + buf->pos++;
> +}
> +
> +#define is_sav(trc) \
> + ((trc & SMI2021_TRC_EAV) == 0x00)
> +#define is_field2(trc) \
> + ((trc & SMI2021_TRC_FIELD_2) == SMI2021_TRC_FIELD_2)
> +#define is_active_video(trc) \
> + ((trc & SMI2021_TRC_VBI) == 0x00)
> +/*
> + * Parse the TRC.
> + * Grab a new buffer from the queue if don't have one
> + * and we are recieving the start of a video frame.
> + *
> + * Mark video buffers as done if we have one full frame.
> + */
> +static void parse_trc(struct smi2021_dev *dev, u8 trc)
> +{
> + struct smi2021_buffer *buf = dev->isoc_ctl.buf;
> + int lines_per_field = dev->height / 2;
> + int line = 0;
> +
> + if (buf == NULL) {
> + if (!is_sav(trc))
> + return;
> +
> + if (!is_active_video(trc))
> + return;
> +
> + if (is_field2(trc))
> + return;
> +
> + buf = smi2021_next_buffer(dev);
> + if (buf == NULL)
> + return;
> +
> + dev->isoc_ctl.buf = buf;
> + }
> +
> + if (is_sav(trc)) {
> + /* Start of VBI or ACTIVE VIDEO */
> + if (is_active_video(trc)) {
> + buf->in_blank = false;
> + buf->trc_av++;
> + } else {
> + /* VBI */
> + buf->in_blank = true;
> + }
> +
> + if (!buf->second_field && is_field2(trc)) {
> + line = buf->pos / SMI2021_BYTES_PER_LINE;
> + if (line < lines_per_field)
> + goto buf_done;
> +
> + buf->second_field = true;
> + buf->trc_av = 0;
> + }
> +
> + if (buf->second_field && !is_field2(trc))
> + goto buf_done;
> + } else {
> + /* End of VBI or ACTIVE VIDEO */
> + buf->in_blank = true;
> + }
> +
> + return;
> +
> +buf_done:
> + smi2021_buffer_done(dev);
> +}
> +
> +/*
> + * Scan the saa7113 Active video data.
> + * This data is:
> + * 4 bytes header (0xff 0x00 0x00 [TRC/SAV])
> + * 1440 bytes of UYUV Video data
> + * 4 bytes footer (0xff 0x00 0x00 [TRC/EAV])
> + *
> + * TRC = Time Reference Code.
> + * SAV = Start Active Video.
> + * EAV = End Active Video.
> + * This is described in the saa7113 datasheet.
> + */
> +static void parse_video(struct smi2021_dev *dev, u8 *p, int len)
> +{
> + int i;
> +
> + for (i = 0; i < len; i++) {
> + switch (dev->sync_state) {
> + case HSYNC:
> + if (p[i] == 0xff)
> + dev->sync_state = SYNCZ1;
> + else
> + copy_video(dev, p[i]);
> + break;
> + case SYNCZ1:
> + if (p[i] == 0x00) {
> + dev->sync_state = SYNCZ2;
> + } else {
> + dev->sync_state = HSYNC;
> + copy_video(dev, 0xff);
> + copy_video(dev, p[i]);
> + }
> + break;
> + case SYNCZ2:
> + if (p[i] == 0x00) {
> + dev->sync_state = TRC;
> + } else {
> + dev->sync_state = HSYNC;
> + copy_video(dev, 0xff);
> + copy_video(dev, 0x00);
> + copy_video(dev, p[i]);
> + }
> + break;
> + case TRC:
> + dev->sync_state = HSYNC;
> + parse_trc(dev, p[i]);
> + break;
> + }
> + }
> +
> +}
> +/*
> + *
> + * The device delivers data in chunks of 0x400 bytes.
> + * The four first bytes is a magic header to identify the chunks.
> + * 0xaa 0xaa 0x00 0x00 = saa7113 Active Video Data
> + * 0xaa 0xaa 0x00 0x01 = PCM - 24Bit 2 Channel audio data
> + */
> +static void process_packet(struct smi2021_dev *dev, u8 *p, int len)
> +{
> + int i;
> + u32 *header;
> +
> + if (len % 0x400 != 0) {
> + printk_ratelimited(KERN_INFO "smi2021::%s: len: %d\n",
> + __func__, len);
> + return;
> + }
> +
> + for (i = 0; i < len; i += 0x400) {
> + header = (u32 *)(p + i);
> + switch (__cpu_to_be32(*header)) {

That's not right. You probably mean __be32_to_cpu, that makes more sense.

> + case 0xaaaa0000: {
> + parse_video(dev, p+i+4, 0x400-4);
> + break;
> + }
> + case 0xaaaa0001: {
> + smi2021_audio(dev, p+i+4, 0x400-4);
> + break;
> + }
> + default: {
> + /* Nothing */
> + }
> + }
> + }
> +}
> +
> +/*
> + * Interrupt called by URB callback
> + */
> +static void smi2021_isoc_isr(struct urb *urb)
> +{
> + int i, rc, status, len;
> + struct smi2021_dev *dev = urb->context;
> + u8 *p;
> +
> + switch (urb->status) {
> + case 0:
> + break;
> + case -ECONNRESET: /* kill */
> + case -ENOENT:
> + case -ESHUTDOWN:
> + /* uvc driver frees the queue here */
> + return;
> + default:
> + smi2021_err("urb error! status %d\n", urb->status);
> + return;
> + }
> +
> + if (urb->status < 0)
> + print_usb_err(dev, -1, status);
> +
> + if (dev == NULL) {
> + smi2021_warn("called with null device\n");
> + return;
> + }
> +
> + for (i = 0; i < urb->number_of_packets; i++) {
> +
> + status = urb->iso_frame_desc[i].status;
> + if (status == -18) {
> + /* This seems to happen when the device
> + * trying to stream from an unconnected input
> + * */
> + continue;
> + }
> +
> + if (status < 0) {
> + print_usb_err(dev, i, status);
> + continue;
> + }
> +
> + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
> + len = urb->iso_frame_desc[i].actual_length;
> + process_packet(dev, p, len);
> + }
> +
> + for (i = 0; i < urb->number_of_packets; i++) {
> + urb->iso_frame_desc[i].status = 0;
> + urb->iso_frame_desc[i].actual_length = 0;
> + }
> +
> + rc = usb_submit_urb(urb, GFP_ATOMIC);
> + if (rc)
> + smi2021_err("urb re-submit failed (%d)\n", rc);
> +}
> +
> +/*
> + * Cancel urbs
> + * This function can not be called in atomic context
> + */
> +void smi2021_cancel_isoc(struct smi2021_dev *dev)
> +{
> + int i, num_bufs = dev->isoc_ctl.num_bufs;
> + if (!num_bufs)
> + return;
> +
> + smi2021_dbg("killing %d urbs...\n", num_bufs);
> +
> + for (i = 0; i < num_bufs; i++)
> + usb_kill_urb(dev->isoc_ctl.urb[i]);
> +
> + smi2021_dbg("all urbs killed\n");
> +
> +}
> +
> +/*
> + * Releases urb and transfer buffers
> + * Obviously, associated urb must be killed before releasing it
> + */
> +void smi2021_free_isoc(struct smi2021_dev *dev)
> +{
> + struct urb *urb;
> + int i, num_bufs = dev->isoc_ctl.num_bufs;
> +
> + smi2021_dbg("freeing %d urb buffers...\n", num_bufs);
> +
> + for (i = 0; i < num_bufs; i++) {
> + urb = dev->isoc_ctl.urb[i];
> + if (urb) {
> + if (dev->isoc_ctl.transfer_buffer[i]) {
> +#ifndef CONFIG_DMA_NONCOHERENT
> + usb_free_coherent(dev->udev,
> + urb->transfer_buffer_length,
> + dev->isoc_ctl.transfer_buffer[i],
> + urb->transfer_dma);
> +#else
> + kfree(dev->isoc_ctl.transfer_buffer[i]);
> +#endif
> + }
> + 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;
> +
> + smi2021_dbg("all urb buffers freed\n");
> +}
> +
> +/*
> + * Helper for canceling and freeing urbs
> + * This function can not be called in atomic context
> + */
> +void smi2021_uninit_isoc(struct smi2021_dev *dev)
> +{
> + smi2021_cancel_isoc(dev);
> + smi2021_free_isoc(dev);
> +}
> +
> +
> +int smi2021_alloc_isoc(struct smi2021_dev *dev)
> +{
> + struct urb *urb;
> + int i, j, k, sb_size, max_packets, num_bufs;
> +
> + if (dev->isoc_ctl.num_bufs)
> + smi2021_uninit_isoc(dev);
> +
> + num_bufs = SMI2021_ISOC_BUFS;
> + max_packets = SMI2021_ISOC_PACKETS;
> + sb_size = max_packets * SMI2021_MAX_PKT_SIZE;
> +
> + dev->isoc_ctl.buf = NULL;
> + dev->isoc_ctl.max_pkt_size = SMI2021_MAX_PKT_SIZE;
> + dev->isoc_ctl.urb = kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
> + if (!dev->isoc_ctl.urb) {
> + smi2021_err("out of memory for urb array\n");
> + return -ENOMEM;
> + }
> +
> + dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *) * num_bufs,
> + GFP_KERNEL);
> + if (!dev->isoc_ctl.transfer_buffer) {
> + smi2021_err("out of memory for usb transfer\n");
> + kfree(dev->isoc_ctl.urb);
> + return -ENOMEM;
> + }
> +
> + for (i = 0; i < num_bufs; i++) {
> + urb = usb_alloc_urb(max_packets, GFP_KERNEL);
> + if (!urb) {
> + smi2021_err("connot allocate urb[%d]\n", i);
> + goto free_i_bufs;
> + }
> + dev->isoc_ctl.urb[i] = urb;
> +#ifndef CONFIG_DMA_NONCOHERENT
> + dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(
> + dev->udev, sb_size, GFP_KERNEL,
> + &urb->transfer_dma);
> +#else
> + dev->isoc_ctl.transfer_buffer[i] = kmalloc(sb_size,
> + GFP_KERNEL);
> +#endif
> + if (!dev->isoc_ctl.transfer_buffer[i]) {
> + smi2021_err("cannot alloc %d bytes for tx[%d] buffer",
> + sb_size, i);
> + goto free_i_bufs;
> + }
> + /* Do not leak kernel data */
> + memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
> +
> + urb->dev = dev->udev;
> + urb->pipe = usb_rcvisocpipe(dev->udev, SMI2021_ISOC_EP);
> + urb->transfer_buffer = dev->isoc_ctl.transfer_buffer[i];
> + urb->transfer_buffer_length = sb_size;
> + urb->complete = smi2021_isoc_isr;
> + urb->context = dev;
> + urb->interval = 1;
> + urb->start_frame = 0;
> + urb->number_of_packets = max_packets;
> +#ifndef CONFIG_DMA_NONCOHERENT
> + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
> +#else
> + urb->transfer_flags = URB_ISO_ASAP;
> +#endif
> + 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;
> + }
> + }
> + smi2021_dbg("urbs allocated\n");
> + dev->isoc_ctl.num_bufs = num_bufs;
> + return 0;
> +
> +free_i_bufs:
> + dev->isoc_ctl.num_bufs = i+1;
> + smi2021_free_isoc(dev);
> + return -ENOMEM;
> +}
>

Regards,

Hans

2013-03-18 08:29:19

by Hans Verkuil

[permalink] [raw]
Subject: Re: [RFC V1 4/8] smi2021: Add smi2021_v4l2.c

On Thu March 14 2013 15:07:00 Jon Arne Jørgensen wrote:
> This file is responsible for registering the device with the v4l2 subsystem,
> and the communication with v4l2.
> Most of the v4l2 ioctls are just passed on to vidbuf2.
>
> Signed-off-by: Jon Arne Jørgensen <[email protected]>
> ---
> drivers/media/usb/smi2021/smi2021_v4l2.c | 566 +++++++++++++++++++++++++++++++
> 1 file changed, 566 insertions(+)
> create mode 100644 drivers/media/usb/smi2021/smi2021_v4l2.c
>
> diff --git a/drivers/media/usb/smi2021/smi2021_v4l2.c b/drivers/media/usb/smi2021/smi2021_v4l2.c
> new file mode 100644
> index 0000000..d402093
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/smi2021_v4l2.c
> @@ -0,0 +1,566 @@

...

> +int smi2021_vb2_setup(struct smi2021_dev *dev)
> +{
> + int rc;
> + struct vb2_queue *q;
> +
> + q = &dev->vb_vidq;
> + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
> + q->drv_priv = dev;
> + q->buf_struct_size = sizeof(struct smi2021_buffer);
> + q->ops = &smi2021_video_qops;
> + q->mem_ops = &vb2_vmalloc_memops;

q->timestamp_type isn't filled in.

For that matter, neither the sequence number nor the timestamp are filled in
in v4l2_buffer during capturing.

You need to add a buf_finish op to fill those in (use v4l2_timestamp() for the
timestamp).

Regards,

Hans

2013-03-18 08:30:51

by Hans Verkuil

[permalink] [raw]
Subject: Re: [RFC V1 2/8] smi2021: Add smi2021_main.c

On Thu March 14 2013 15:06:58 Jon Arne Jørgensen wrote:
> This is the core of the smi2021 module.
> It will register the module with the kernel, and register the
> usb probe function.
>
> Signed-off-by: Jon Arne Jørgensen <[email protected]>
> ---
> drivers/media/usb/smi2021/smi2021_main.c | 339 +++++++++++++++++++++++++++++++
> 1 file changed, 339 insertions(+)
> create mode 100644 drivers/media/usb/smi2021/smi2021_main.c
>
> diff --git a/drivers/media/usb/smi2021/smi2021_main.c b/drivers/media/usb/smi2021/smi2021_main.c
> new file mode 100644
> index 0000000..cc600e7
> --- /dev/null
> +++ b/drivers/media/usb/smi2021/smi2021_main.c
> @@ -0,0 +1,339 @@
> +/*******************************************************************************
> + * smi2021_main.c *
> + * *
> + * USB Driver for SMI2021 - EasyCAP *
> + * USB ID 1c88:003c *
> + * *
> + * *****************************************************************************
> + *
> + * Copyright 2011-2013 Jon Arne Jørgensen
> + * <jonjon.arnearne--a.t--gmail.com>
> + *
> + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> + *
> + * This file is part of SMI2021
> + * http://code.google.com/p/easycap-somagic-linux/
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + * This driver is heavily influensed by the STK1160 driver.
> + * Copyright (C) 2012 Ezequiel Garcia
> + * <elezegarcia--a.t--gmail.com>
> + *
> + */
> +
> +#include "smi2021.h"
> +
> +#define VENDOR_ID 0x1c88
> +
> +static unsigned int imput;
> +module_param(imput, int, 0644);
> +MODULE_PARM_DESC(input, "Set default input");
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Jon Arne Jørgensen <jonjon.arnearne--a.t--gmail.com>");
> +MODULE_DESCRIPTION("SMI2021 - EasyCap");
> +MODULE_VERSION(SMI2021_DRIVER_VERSION);
> +
> +
> +struct usb_device_id smi2021_usb_device_id_table[] = {
> + { USB_DEVICE(VENDOR_ID, 0x003c) },
> + { USB_DEVICE(VENDOR_ID, 0x003d) },
> + { USB_DEVICE(VENDOR_ID, 0x003e) },
> + { USB_DEVICE(VENDOR_ID, 0x003f) },
> + { }
> +};
> +
> +MODULE_DEVICE_TABLE(usb, smi2021_usb_device_id_table);
> +
> +static unsigned short saa7113_addrs[] = {
> + 0x4a,
> + I2C_CLIENT_END
> +};
> +
> +/******************************************************************************/
> +/* */
> +/* Write to saa7113 */
> +/* */
> +/******************************************************************************/
> +
> +inline int transfer_usb_ctrl(struct smi2021_dev *dev, u8 *data, int len)
> +{
> + return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
> + 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> + 0x0b, 0x00,
> + data, len, 1000);
> +}
> +
> +int smi2021_write_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 val)
> +{
> + int rc;
> + u8 snd_data[8];
> +
> + memset(snd_data, 0x00, 8);
> +
> + snd_data[SMI2021_CTRL_HEAD] = 0x0b;
> + snd_data[SMI2021_CTRL_ADDR] = addr;
> + snd_data[SMI2021_CTRL_DATA_SIZE] = 0x01;
> +
> + if (addr) {
> + /* This is I2C data for the saa7113 chip */
> + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0xc0;
> + snd_data[SMI2021_CTRL_BM_DATA_OFFSET] = 0x01;
> +
> + snd_data[SMI2021_CTRL_I2C_REG] = reg;
> + snd_data[SMI2021_CTRL_I2C_VAL] = val;
> + } else {
> + /* This is register settings for the smi2021 chip */
> + snd_data[SMI2021_CTRL_BM_DATA_OFFSET] = 0x82;
> +
> + snd_data[SMI2021_CTRL_REG_HI] = __cpu_to_be16(reg) >> 8;
> + snd_data[SMI2021_CTRL_REG_LO] = __cpu_to_be16(reg);
> +
> + }
> +

Don't transfer memory from the stack. It always has to be k[mz]alloced memory.

> + rc = transfer_usb_ctrl(dev, snd_data, 8);
> + if (rc < 0) {
> + smi2021_warn("write failed on register 0x%x, errno: %d\n",
> + reg, rc);
> + return rc;
> + }
> +
> + return 0;
> +}
> +
> +int smi2021_read_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 *val)
> +{
> + int rc;
> + u8 rcv_data[13];
> + u8 snd_data[8];
> + memset(rcv_data, 0x00, 13);
> + memset(snd_data, 0x00, 8);
> +
> + snd_data[SMI2021_CTRL_HEAD] = 0x0b;
> + snd_data[SMI2021_CTRL_ADDR] = addr;
> + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0x84;
> + snd_data[SMI2021_CTRL_DATA_SIZE] = 0x01;
> + snd_data[SMI2021_CTRL_I2C_REG] = reg;
> +
> + *val = 0;
> +

Ditto.

> + rc = transfer_usb_ctrl(dev, snd_data, 8);
> + if (rc < 0) {
> + smi2021_warn(
> + "1st pass failing to read reg 0x%x, usb-errno: %d\n",
> + reg, rc);
> + return rc;
> + }
> +
> + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0xa0;
> + rc = transfer_usb_ctrl(dev, snd_data, 8);
> + if (rc < 0) {
> + smi2021_warn(
> + "2nd pass failing to read reg 0x%x, usb-errno: %d\n",
> + reg, rc);
> + return rc;
> + }
> +
> + rc = usb_control_msg(dev->udev,
> + usb_rcvctrlpipe(dev->udev, 0x80), 0x01,
> + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> + 0x0b, 0x00, rcv_data, 13, 1000);
> + if (rc < 0) {
> + smi2021_warn("Failed to read reg 0x%x, usb-errno: %d\n",
> + reg, rc);
> + return rc;
> + }
> +
> + *val = rcv_data[SMI2021_CTRL_I2C_RCV_VAL];
> + return 0;
> +}

Regards,

Hans

2013-03-18 08:59:15

by Bjørn Mork

[permalink] [raw]
Subject: Re: [RFC V1 5/8] smi2021: Add smi2021_video.c

Hans Verkuil <[email protected]> writes:

>> +/*
>> + *
>> + * The device delivers data in chunks of 0x400 bytes.
>> + * The four first bytes is a magic header to identify the chunks.
>> + * 0xaa 0xaa 0x00 0x00 = saa7113 Active Video Data
>> + * 0xaa 0xaa 0x00 0x01 = PCM - 24Bit 2 Channel audio data
>> + */
>> +static void process_packet(struct smi2021_dev *dev, u8 *p, int len)
>> +{
>> + int i;
>> + u32 *header;
>> +
>> + if (len % 0x400 != 0) {
>> + printk_ratelimited(KERN_INFO "smi2021::%s: len: %d\n",
>> + __func__, len);
>> + return;
>> + }
>> +
>> + for (i = 0; i < len; i += 0x400) {
>> + header = (u32 *)(p + i);
>> + switch (__cpu_to_be32(*header)) {
>
> That's not right. You probably mean __be32_to_cpu, that makes more sense.
>
>> + case 0xaaaa0000: {
>> + parse_video(dev, p+i+4, 0x400-4);
>> + break;
>> + }
>> + case 0xaaaa0001: {
>> + smi2021_audio(dev, p+i+4, 0x400-4);
>> + break;
>> + }

This could be just me, but I would have done it like this to take
advantage of compile time constant conversions (and also dropping the
noisy extra {}s):

switch (*header) {
case cpu_to_be32(0xaaaa0000):
parse_video(dev, p+i+4, 0x400-4);
break;
case cpu_to_be32(0xaaaa0001):
smi2021_audio(dev, p+i+4, 0x400-4);
break;
..


>From the name of the function I assume the difference may actually be
measurable here if this runs for every processed packet.


Bjørn

2013-03-18 09:31:31

by Bjørn Mork

[permalink] [raw]
Subject: Re: [RFC V1 7/8] smi2021: Add smi2021_bl.c

Jon Arne Jørgensen <[email protected]> writes:

> This is the smi2021-bootloader module.
> This module will upload the firmware for the different somagic devices.

I really don't understand why you want to make that a separate module.
Building both the bootlader driver and the real driver into the same
module will make sure that either both or none are available, document
the relationship to the end user, and allow you to tell the user that
firmware files may be needed for the real driver by using the
MODULE_FIRMWARE macro (which you should do, IMHO).

> +static unsigned int firmware_version;
> +module_param(firmware_version, int, 0644);
> +MODULE_PARM_DESC(firmware_version,
> + "Firmware version to be uploaded to device\n"
> + "if there are more than one firmware present");
> +
> +struct usb_device_id smi2021_bootloader_id_table[] = {
> + { USB_DEVICE(0x1c88, 0x0007) },
> + { }
> +};
> +
> +struct smi2021_firmware {
> + int id;
> + const char *name;
> + int found;
> +};
> +
> +struct smi2021_firmware available_fw[] = {
> + {
> + .id = 0x3c,
> + .name = "smi2021_3c.bin",
> + },
> + {
> + .id = 0x3e,
> + .name = "smi2021_3e.bin",
> + },
> + {
> + .id = 0x3f,
> + .name = "smi2021_3f.bin",
> + }
> +};


How does the user know which firmware to select? Will any of them work,
or are they device specific? Either way I believe you should publish all
three names using MODULE_FIRMWARE.


> +static int smi2021_load_firmware(struct usb_device *udev,
> + const struct firmware *firmware)
> +{
> + int i, size, rc = 0;
> + u8 *chunk;
> + u16 ack = 0x0000;
> +
> + if (udev == NULL)
> + goto end_out;

Is this possible?



> + size = FIRMWARE_CHUNK_SIZE + FIRMWARE_HEADER_SIZE;
> + chunk = kzalloc(size, GFP_KERNEL);
> + chunk[0] = 0x05;
> + chunk[1] = 0xff;
> +
> + if (chunk == NULL) {

This on the other hand, will happen. But you have already oopsed when
you test it here...


Bjørn

2013-03-20 08:56:43

by Jon Arne Jørgensen

[permalink] [raw]
Subject: Re: [RFC V1 7/8] smi2021: Add smi2021_bl.c

On Mon, Mar 18, 2013 at 10:31:14AM +0100, Bjørn Mork wrote:
> Jon Arne Jørgensen <[email protected]> writes:
>
> > This is the smi2021-bootloader module.
> > This module will upload the firmware for the different somagic devices.
>
> I really don't understand why you want to make that a separate module.
> Building both the bootlader driver and the real driver into the same
> module will make sure that either both or none are available, document
> the relationship to the end user, and allow you to tell the user that
> firmware files may be needed for the real driver by using the
> MODULE_FIRMWARE macro (which you should do, IMHO).
>

I made this a separate module because there is one version of this
device that doesn't need any firmware.
But I think it would be better to include the bootloader in the real driver
for simplicity.

> > +static unsigned int firmware_version;
> > +module_param(firmware_version, int, 0644);
> > +MODULE_PARM_DESC(firmware_version,
> > + "Firmware version to be uploaded to device\n"
> > + "if there are more than one firmware present");
> > +
> > +struct usb_device_id smi2021_bootloader_id_table[] = {
> > + { USB_DEVICE(0x1c88, 0x0007) },
> > + { }
> > +};
> > +
> > +struct smi2021_firmware {
> > + int id;
> > + const char *name;
> > + int found;
> > +};
> > +
> > +struct smi2021_firmware available_fw[] = {
> > + {
> > + .id = 0x3c,
> > + .name = "smi2021_3c.bin",
> > + },
> > + {
> > + .id = 0x3e,
> > + .name = "smi2021_3e.bin",
> > + },
> > + {
> > + .id = 0x3f,
> > + .name = "smi2021_3f.bin",
> > + }
> > +};
>
>
> How does the user know which firmware to select? Will any of them work,
> or are they device specific? Either way I believe you should publish all
> three names using MODULE_FIRMWARE.
>
>

The problem with this device is that there is no way for the user to
know what version of the device he's got before the firmware is uploaded
to the device.

Any device will accept any firmware, but it will not function without
the correct firmware.

The only way to see what version of the firmware you will need is to
test the driver on a Windows machine with the driver that came with the
device. I have no idea about what the windows driver will do if you have
more than one version of this device.

> > +static int smi2021_load_firmware(struct usb_device *udev,
> > + const struct firmware *firmware)
> > +{
> > + int i, size, rc = 0;
> > + u8 *chunk;
> > + u16 ack = 0x0000;
> > +
> > + if (udev == NULL)
> > + goto end_out;
>
> Is this possible?
>
>
>

Probably not, I'll remove this check :)

> > + size = FIRMWARE_CHUNK_SIZE + FIRMWARE_HEADER_SIZE;
> > + chunk = kzalloc(size, GFP_KERNEL);
> > + chunk[0] = 0x05;
> > + chunk[1] = 0xff;
> > +
> > + if (chunk == NULL) {
>
> This on the other hand, will happen. But you have already oopsed when
> you test it here...
>
>

Yes, that is true.
I will fix,

thank you for your review.

> Bjørn
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-03-20 09:27:41

by Jon Arne Jørgensen

[permalink] [raw]
Subject: Re: [RFC V1 2/8] smi2021: Add smi2021_main.c

On Mon, Mar 18, 2013 at 08:58:47AM +0100, Hans Verkuil wrote:
> Hi Jon Arne,
>
> Here is my review of this driver.
>

Great, thank you.

> Regards,
>
> Hans
>
> On Thu March 14 2013 15:06:58 Jon Arne Jørgensen wrote:
> > This is the core of the smi2021 module.
> > It will register the module with the kernel, and register the
> > usb probe function.
> >
> > Signed-off-by: Jon Arne Jørgensen <[email protected]>
> > ---
> > drivers/media/usb/smi2021/smi2021_main.c | 339 +++++++++++++++++++++++++++++++
> > 1 file changed, 339 insertions(+)
> > create mode 100644 drivers/media/usb/smi2021/smi2021_main.c
> >
> > diff --git a/drivers/media/usb/smi2021/smi2021_main.c b/drivers/media/usb/smi2021/smi2021_main.c
> > new file mode 100644
> > index 0000000..cc600e7
> > --- /dev/null
> > +++ b/drivers/media/usb/smi2021/smi2021_main.c
> > @@ -0,0 +1,339 @@
> > +/*******************************************************************************
> > + * smi2021_main.c *
> > + * *
> > + * USB Driver for SMI2021 - EasyCAP *
> > + * USB ID 1c88:003c *
> > + * *
> > + * *****************************************************************************
> > + *
> > + * Copyright 2011-2013 Jon Arne Jørgensen
> > + * <jonjon.arnearne--a.t--gmail.com>
> > + *
> > + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> > + *
> > + * This file is part of SMI2021
> > + * http://code.google.com/p/easycap-somagic-linux/
> > + *
> > + * This program is free software: you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation, either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> > + *
> > + * This driver is heavily influensed by the STK1160 driver.
> > + * Copyright (C) 2012 Ezequiel Garcia
> > + * <elezegarcia--a.t--gmail.com>
> > + *
> > + */
> > +
> > +#include "smi2021.h"
> > +
> > +#define VENDOR_ID 0x1c88
> > +
> > +static unsigned int imput;
> > +module_param(imput, int, 0644);
> > +MODULE_PARM_DESC(input, "Set default input");
> > +
> > +MODULE_LICENSE("GPL");
> > +MODULE_AUTHOR("Jon Arne Jørgensen <jonjon.arnearne--a.t--gmail.com>");
> > +MODULE_DESCRIPTION("SMI2021 - EasyCap");
> > +MODULE_VERSION(SMI2021_DRIVER_VERSION);
> > +
> > +
> > +struct usb_device_id smi2021_usb_device_id_table[] = {
>
> Should be static const
>

Ok.

> > + { USB_DEVICE(VENDOR_ID, 0x003c) },
> > + { USB_DEVICE(VENDOR_ID, 0x003d) },
> > + { USB_DEVICE(VENDOR_ID, 0x003e) },
> > + { USB_DEVICE(VENDOR_ID, 0x003f) },
> > + { }
> > +};
> > +
> > +MODULE_DEVICE_TABLE(usb, smi2021_usb_device_id_table);
> > +
> > +static unsigned short saa7113_addrs[] = {
> > + 0x4a,
> > + I2C_CLIENT_END
> > +};
>
> Don't use this. See below for more info.
>

Removed.

> > +
> > +/******************************************************************************/
> > +/* */
> > +/* Write to saa7113 */
> > +/* */
> > +/******************************************************************************/
> > +
> > +inline int transfer_usb_ctrl(struct smi2021_dev *dev, u8 *data, int len)
> > +{
> > + return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
> > + 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> > + 0x0b, 0x00,
> > + data, len, 1000);
> > +}
> > +
> > +int smi2021_write_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 val)
> > +{
> > + int rc;
> > + u8 snd_data[8];
> > +
> > + memset(snd_data, 0x00, 8);
> > +
> > + snd_data[SMI2021_CTRL_HEAD] = 0x0b;
> > + snd_data[SMI2021_CTRL_ADDR] = addr;
> > + snd_data[SMI2021_CTRL_DATA_SIZE] = 0x01;
> > +
> > + if (addr) {
> > + /* This is I2C data for the saa7113 chip */
> > + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0xc0;
> > + snd_data[SMI2021_CTRL_BM_DATA_OFFSET] = 0x01;
> > +
> > + snd_data[SMI2021_CTRL_I2C_REG] = reg;
> > + snd_data[SMI2021_CTRL_I2C_VAL] = val;
> > + } else {
> > + /* This is register settings for the smi2021 chip */
> > + snd_data[SMI2021_CTRL_BM_DATA_OFFSET] = 0x82;
> > +
> > + snd_data[SMI2021_CTRL_REG_HI] = __cpu_to_be16(reg) >> 8;
> > + snd_data[SMI2021_CTRL_REG_LO] = __cpu_to_be16(reg);
>
> This looks really weird. On a little endian system REG_HI is now initialized
> with the LSB, not MSB and the other way around on a big endian system.
> The _cpu_to_be16 certainly doesn't belong here.
>

The device expects the register address as a 16bit Big Endian number.
I though this code would move the HIGH byte of a big endian number into
REG_HI, and the LOW byte into REG_LO.

This works as expected on my little endian intel I7, but I will have
take a second look at my code, I guesss I can do this without the
conversion into be16.

> > +
> > + }
> > +
> > + rc = transfer_usb_ctrl(dev, snd_data, 8);
> > + if (rc < 0) {
> > + smi2021_warn("write failed on register 0x%x, errno: %d\n",
> > + reg, rc);
> > + return rc;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +int smi2021_read_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 *val)
> > +{
> > + int rc;
> > + u8 rcv_data[13];
> > + u8 snd_data[8];
> > + memset(rcv_data, 0x00, 13);
> > + memset(snd_data, 0x00, 8);
> > +
> > + snd_data[SMI2021_CTRL_HEAD] = 0x0b;
> > + snd_data[SMI2021_CTRL_ADDR] = addr;
> > + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0x84;
> > + snd_data[SMI2021_CTRL_DATA_SIZE] = 0x01;
> > + snd_data[SMI2021_CTRL_I2C_REG] = reg;
> > +
> > + *val = 0;
> > +
> > + rc = transfer_usb_ctrl(dev, snd_data, 8);
> > + if (rc < 0) {
> > + smi2021_warn(
> > + "1st pass failing to read reg 0x%x, usb-errno: %d\n",
> > + reg, rc);
> > + return rc;
> > + }
> > +
> > + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0xa0;
> > + rc = transfer_usb_ctrl(dev, snd_data, 8);
> > + if (rc < 0) {
> > + smi2021_warn(
> > + "2nd pass failing to read reg 0x%x, usb-errno: %d\n",
> > + reg, rc);
> > + return rc;
> > + }
> > +
> > + rc = usb_control_msg(dev->udev,
> > + usb_rcvctrlpipe(dev->udev, 0x80), 0x01,
> > + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> > + 0x0b, 0x00, rcv_data, 13, 1000);
> > + if (rc < 0) {
> > + smi2021_warn("Failed to read reg 0x%x, usb-errno: %d\n",
> > + reg, rc);
> > + return rc;
> > + }
> > +
> > + *val = rcv_data[SMI2021_CTRL_I2C_RCV_VAL];
> > + return 0;
> > +}
> > +
> > +static void smi2021_reset_device(struct smi2021_dev *dev)
> > +{
> > + smi2021_write_reg(dev, 0, 0x3a, 0x80);
> > + smi2021_write_reg(dev, 0, 0x3b, 0x80);
> > + smi2021_write_reg(dev, 0, 0x3b, 0x00);
> > +}
> > +
> > +static void release_v4l2_dev(struct v4l2_device *v4l2_dev)
> > +{
> > + struct smi2021_dev *dev = container_of(v4l2_dev, struct smi2021_dev,
> > + v4l2_dev);
> > + smi2021_dbg("Releasing all resources\n");
> > +
> > + smi2021_i2c_unregister(dev);
> > +
> > + v4l2_ctrl_handler_free(&dev->ctrl_handler);
> > + v4l2_device_unregister(&dev->v4l2_dev);
> > + kfree(dev);
> > +}
> > +
> > +#define hb_mult(w_max_packet_size) (1 + (((w_max_packet_size) >> 11) & 0x03))
> > +
> > +static int smi2021_scan_usb(struct usb_interface *intf, struct usb_device *udev)
> > +{
> > + int i, e, ifnum, sizedescr, size;
> > + const struct usb_endpoint_descriptor *desc;
> > + ifnum = intf->altsetting[0].desc.bInterfaceNumber;
> > +
> > + for (i = 0; i < intf->num_altsetting; i++) {
> > + for (e = 0; e < intf->altsetting[i].desc.bNumEndpoints; e++) {
> > + desc = &intf->altsetting[i].endpoint[e].desc;
> > + sizedescr = le16_to_cpu(desc->wMaxPacketSize);
> > + size = sizedescr & 0x7ff;
> > +
> > + if (udev->speed == USB_SPEED_HIGH)
> > + size = size * hb_mult(sizedescr);
> > + }
> > + }
> > + return 0;
> > +}
> > +
> > +/******************************************************************************/
> > +/* */
> > +/* DEVICE - PROBE & DISCONNECT */
> > +/* */
> > +/******************************************************************************/
> > +static int smi2021_usb_probe(struct usb_interface *intf,
> > + const struct usb_device_id *devid)
> > +{
> > + int rc = 0;
> > + struct usb_device *udev = interface_to_usbdev(intf);
> > + struct smi2021_dev *dev;
> > +
> > + if (udev == (struct usb_device *)NULL) {
> > + smi2021_err("device is NULL\n");
> > + return -EFAULT;
> > + }
> > +
> > + smi2021_scan_usb(intf, udev);
> > +
> > + dev = kzalloc(sizeof(struct smi2021_dev), GFP_KERNEL);
> > + if (dev == NULL)
> > + return -ENOMEM;
> > +
> > + dev->udev = udev;
> > + dev->dev = &intf->dev;
> > + usb_set_intfdata(intf, dev);
> > +
> > + /* Initialize videobuf2 stuff */
> > + rc = smi2021_vb2_setup(dev);
> > + if (rc < 0)
> > + goto free_err;
> > +
> > + spin_lock_init(&dev->buf_lock);
> > + mutex_init(&dev->v4l2_lock);
> > + mutex_init(&dev->vb_queue_lock);
> > +
> > + rc = v4l2_ctrl_handler_init(&dev->ctrl_handler, 0);
> > + if (rc) {
> > + smi2021_err("v4l2_ctrl_handler_init failed with: %d\n", rc);
> > + goto free_err;
> > + }
> > +
> > + dev->v4l2_dev.release = release_v4l2_dev;
> > + dev->v4l2_dev.ctrl_handler = &dev->ctrl_handler;
> > + rc = v4l2_device_register(dev->dev, &dev->v4l2_dev);
> > + if (rc) {
> > + smi2021_err("v4l2_device_register failed with %d\n", rc);
> > + goto free_ctrl;
> > + }
> > +
> > + smi2021_reset_device(dev);
> > +
> > + rc = smi2021_i2c_register(dev);
> > + if (rc < 0)
> > + goto unreg_v4l2;
> > +
> > + dev->sd_saa7113 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
> > + "saa7113", 0, saa7113_addrs);
>
> Fill in the addr argument directly and use NULL for probe_addrs. Probing is
> not something new drivers should do.
>
> See also my comments in the i2c.c review.
>

I will do this as you suggested in the ic2 review.

> > +
> > + smi2021_dbg("Driver version %s successfully loaded\n",
> > + SMI2021_DRIVER_VERSION);
> > +
> > + v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0);
> > + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
> > +
> > + rc = smi2021_snd_register(dev);
> > + if (rc < 0)
> > + goto unreg_i2c;
> > +
> > + rc = smi2021_video_register(dev);
> > + if (rc < 0)
> > + goto unreg_snd;
> > +
> > + return 0;
> > +
> > +unreg_snd:
> > + smi2021_snd_unregister(dev);
> > +unreg_i2c:
> > + smi2021_i2c_unregister(dev);
> > +unreg_v4l2:
> > + v4l2_device_unregister(&dev->v4l2_dev);
> > +free_ctrl:
> > + v4l2_ctrl_handler_free(&dev->ctrl_handler);
> > +free_err:
> > + kfree(dev);
> > +
> > + return rc;
> > +}
> > +
> > +static void smi2021_usb_disconnect(struct usb_interface *intf)
> > +{
> > + struct smi2021_dev *dev = usb_get_intfdata(intf);
> > +
> > + smi2021_dbg("Going for release!\n");
> > +
> > + usb_set_intfdata(intf, NULL);
> > +
> > + mutex_lock(&dev->vb_queue_lock);
> > + mutex_lock(&dev->v4l2_lock);
> > +
> > + smi2021_uninit_isoc(dev);
> > + smi2021_clear_queue(dev);
> > +
> > + video_unregister_device(&dev->vdev);
> > + v4l2_device_disconnect(&dev->v4l2_dev);
> > +
> > + /* This way current users can detect device is gone */
> > + dev->udev = NULL;
> > +
> > + mutex_unlock(&dev->v4l2_lock);
> > + mutex_unlock(&dev->vb_queue_lock);
> > +
> > + smi2021_snd_unregister(dev);
> > +
> > + /*
> > + * This calls release_v4l2_dev if it's the last reference.
> > + * Otherwise, the release is postponed until there are no users left.
> > + */
> > + v4l2_device_put(&dev->v4l2_dev);
> > +}
> > +
> > +/******************************************************************************/
> > +/* */
> > +/* MODULE - INIT & EXIT */
> > +/* */
> > +/******************************************************************************/
> > +
> > +struct usb_driver smi2021_usb_driver = {
> > + .name = "smi2021",
> > + .id_table = smi2021_usb_device_id_table,
> > + .probe = smi2021_usb_probe,
> > + .disconnect = smi2021_usb_disconnect
> > +};
> > +
> > +module_usb_driver(smi2021_usb_driver);
> > +
> >
>
> Regards,
>
> Hans
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-03-20 09:28:43

by Jon Arne Jørgensen

[permalink] [raw]
Subject: Re: [RFC V1 2/8] smi2021: Add smi2021_main.c

On Mon, Mar 18, 2013 at 09:30:38AM +0100, Hans Verkuil wrote:
> On Thu March 14 2013 15:06:58 Jon Arne Jørgensen wrote:
> > This is the core of the smi2021 module.
> > It will register the module with the kernel, and register the
> > usb probe function.
> >
> > Signed-off-by: Jon Arne Jørgensen <[email protected]>
> > ---
> > drivers/media/usb/smi2021/smi2021_main.c | 339 +++++++++++++++++++++++++++++++
> > 1 file changed, 339 insertions(+)
> > create mode 100644 drivers/media/usb/smi2021/smi2021_main.c
> >
> > diff --git a/drivers/media/usb/smi2021/smi2021_main.c b/drivers/media/usb/smi2021/smi2021_main.c
> > new file mode 100644
> > index 0000000..cc600e7
> > --- /dev/null
> > +++ b/drivers/media/usb/smi2021/smi2021_main.c
> > @@ -0,0 +1,339 @@
> > +/*******************************************************************************
> > + * smi2021_main.c *
> > + * *
> > + * USB Driver for SMI2021 - EasyCAP *
> > + * USB ID 1c88:003c *
> > + * *
> > + * *****************************************************************************
> > + *
> > + * Copyright 2011-2013 Jon Arne Jørgensen
> > + * <jonjon.arnearne--a.t--gmail.com>
> > + *
> > + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> > + *
> > + * This file is part of SMI2021
> > + * http://code.google.com/p/easycap-somagic-linux/
> > + *
> > + * This program is free software: you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation, either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> > + *
> > + * This driver is heavily influensed by the STK1160 driver.
> > + * Copyright (C) 2012 Ezequiel Garcia
> > + * <elezegarcia--a.t--gmail.com>
> > + *
> > + */
> > +
> > +#include "smi2021.h"
> > +
> > +#define VENDOR_ID 0x1c88
> > +
> > +static unsigned int imput;
> > +module_param(imput, int, 0644);
> > +MODULE_PARM_DESC(input, "Set default input");
> > +
> > +MODULE_LICENSE("GPL");
> > +MODULE_AUTHOR("Jon Arne Jørgensen <jonjon.arnearne--a.t--gmail.com>");
> > +MODULE_DESCRIPTION("SMI2021 - EasyCap");
> > +MODULE_VERSION(SMI2021_DRIVER_VERSION);
> > +
> > +
> > +struct usb_device_id smi2021_usb_device_id_table[] = {
> > + { USB_DEVICE(VENDOR_ID, 0x003c) },
> > + { USB_DEVICE(VENDOR_ID, 0x003d) },
> > + { USB_DEVICE(VENDOR_ID, 0x003e) },
> > + { USB_DEVICE(VENDOR_ID, 0x003f) },
> > + { }
> > +};
> > +
> > +MODULE_DEVICE_TABLE(usb, smi2021_usb_device_id_table);
> > +
> > +static unsigned short saa7113_addrs[] = {
> > + 0x4a,
> > + I2C_CLIENT_END
> > +};
> > +
> > +/******************************************************************************/
> > +/* */
> > +/* Write to saa7113 */
> > +/* */
> > +/******************************************************************************/
> > +
> > +inline int transfer_usb_ctrl(struct smi2021_dev *dev, u8 *data, int len)
> > +{
> > + return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
> > + 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> > + 0x0b, 0x00,
> > + data, len, 1000);
> > +}
> > +
> > +int smi2021_write_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 val)
> > +{
> > + int rc;
> > + u8 snd_data[8];
> > +
> > + memset(snd_data, 0x00, 8);
> > +
> > + snd_data[SMI2021_CTRL_HEAD] = 0x0b;
> > + snd_data[SMI2021_CTRL_ADDR] = addr;
> > + snd_data[SMI2021_CTRL_DATA_SIZE] = 0x01;
> > +
> > + if (addr) {
> > + /* This is I2C data for the saa7113 chip */
> > + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0xc0;
> > + snd_data[SMI2021_CTRL_BM_DATA_OFFSET] = 0x01;
> > +
> > + snd_data[SMI2021_CTRL_I2C_REG] = reg;
> > + snd_data[SMI2021_CTRL_I2C_VAL] = val;
> > + } else {
> > + /* This is register settings for the smi2021 chip */
> > + snd_data[SMI2021_CTRL_BM_DATA_OFFSET] = 0x82;
> > +
> > + snd_data[SMI2021_CTRL_REG_HI] = __cpu_to_be16(reg) >> 8;
> > + snd_data[SMI2021_CTRL_REG_LO] = __cpu_to_be16(reg);
> > +
> > + }
> > +
>
> Don't transfer memory from the stack. It always has to be k[mz]alloced memory.
>

Will fix...

> > + rc = transfer_usb_ctrl(dev, snd_data, 8);
> > + if (rc < 0) {
> > + smi2021_warn("write failed on register 0x%x, errno: %d\n",
> > + reg, rc);
> > + return rc;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +int smi2021_read_reg(struct smi2021_dev *dev, u8 addr, u16 reg, u8 *val)
> > +{
> > + int rc;
> > + u8 rcv_data[13];
> > + u8 snd_data[8];
> > + memset(rcv_data, 0x00, 13);
> > + memset(snd_data, 0x00, 8);
> > +
> > + snd_data[SMI2021_CTRL_HEAD] = 0x0b;
> > + snd_data[SMI2021_CTRL_ADDR] = addr;
> > + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0x84;
> > + snd_data[SMI2021_CTRL_DATA_SIZE] = 0x01;
> > + snd_data[SMI2021_CTRL_I2C_REG] = reg;
> > +
> > + *val = 0;
> > +
>
> Ditto.
>

... this also.

> > + rc = transfer_usb_ctrl(dev, snd_data, 8);
> > + if (rc < 0) {
> > + smi2021_warn(
> > + "1st pass failing to read reg 0x%x, usb-errno: %d\n",
> > + reg, rc);
> > + return rc;
> > + }
> > +
> > + snd_data[SMI2021_CTRL_BM_DATA_TYPE] = 0xa0;
> > + rc = transfer_usb_ctrl(dev, snd_data, 8);
> > + if (rc < 0) {
> > + smi2021_warn(
> > + "2nd pass failing to read reg 0x%x, usb-errno: %d\n",
> > + reg, rc);
> > + return rc;
> > + }
> > +
> > + rc = usb_control_msg(dev->udev,
> > + usb_rcvctrlpipe(dev->udev, 0x80), 0x01,
> > + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> > + 0x0b, 0x00, rcv_data, 13, 1000);
> > + if (rc < 0) {
> > + smi2021_warn("Failed to read reg 0x%x, usb-errno: %d\n",
> > + reg, rc);
> > + return rc;
> > + }
> > +
> > + *val = rcv_data[SMI2021_CTRL_I2C_RCV_VAL];
> > + return 0;
> > +}
>
> Regards,
>
> Hans
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-03-20 09:29:41

by Jon Arne Jørgensen

[permalink] [raw]
Subject: Re: [RFC V1 3/8] smi2021: Add smi2021_i2c.c

On Mon, Mar 18, 2013 at 09:04:56AM +0100, Hans Verkuil wrote:
> On Thu March 14 2013 15:06:59 Jon Arne Jørgensen wrote:
> > This file is responsible for registering the device
> > with the kernel i2c subsystem.
> > v4l2 talks to the saa7113 chip of the device via i2c.
> >
> > Signed-off-by: Jon Arne Jørgensen <[email protected]>
> > ---
> > drivers/media/usb/smi2021/smi2021_i2c.c | 160 ++++++++++++++++++++++++++++++++
> > 1 file changed, 160 insertions(+)
> > create mode 100644 drivers/media/usb/smi2021/smi2021_i2c.c
> >
> > diff --git a/drivers/media/usb/smi2021/smi2021_i2c.c b/drivers/media/usb/smi2021/smi2021_i2c.c
> > new file mode 100644
> > index 0000000..5b6f3f5
> > --- /dev/null
> > +++ b/drivers/media/usb/smi2021/smi2021_i2c.c
> > @@ -0,0 +1,160 @@
> > +/*******************************************************************************
> > + * smi2021_i2c.c *
> > + * *
> > + * USB Driver for SMI2021 - EasyCAP *
> > + * USB ID 1c88:003c *
> > + * *
> > + * *****************************************************************************
> > + *
> > + * Copyright 2011-2013 Jon Arne Jørgensen
> > + * <jonjon.arnearne--a.t--gmail.com>
> > + *
> > + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> > + *
> > + * This file is part of SMI2021
> > + * http://code.google.com/p/easycap-somagic-linux/
> > + *
> > + * This program is free software: you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation, either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> > + *
> > + * This driver is heavily influensed by the STK1160 driver.
> > + * Copyright (C) 2012 Ezequiel Garcia
> > + * <elezegarcia--a.t--gmail.com>
> > + *
> > + */
> > +
> > +#include "smi2021.h"
> > +
> > +/* The device will not return the chip_name on address 0x00.
> > + * But the saa7115 i2c driver needs the chip id to match "f7113"
> > + * if we want to use it,
> > + * so we have to fake the return of this value
> > + */
> > +
> > +static char chip_id[] = { 'x', 255, 55, 49, 49, 115, 0 };
> > +static int id_ptr;
>
> Don't do this! Instead modify saa7115.c and add a new chip_id (the name of
> this saa7113 clone) and specify that in v4l2_i2c_new_subdev instead of "saa7113".
> The saa7115.c driver should skip the chip ID test if that name is specified.
>
> Then you don't need this hack anymore and everything will look much cleaner.
>

I'll do this.

> > +
> > +static unsigned int i2c_debug;
> > +module_param(i2c_debug, int, 0644);
> > +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
> > +
> > +#define dprint_i2c(fmt, args...) \
> > +do { \
> > + if (i2c_debug) \
> > + pr_debug("smi2021[i2c]::%s: " fmt, __func__, ##args); \
> > +} while (0)
> > +
> > +
> > +static int i2c_xfer(struct i2c_adapter *i2c_adap,
> > + struct i2c_msg msgs[], int num)
> > +{
> > + struct smi2021_dev *dev = i2c_adap->algo_data;
> > +
> > + switch (num) {
> > + case 2: { /* Read reg */
> > + if (msgs[0].len != 1 || msgs[1].len != 1) {
> > + dprint_i2c("both messages must be 1 byte\n");
> > + goto err_out;
> > +
> > + if ((msgs[1].flags & I2C_M_RD) != I2C_M_RD)
> > + dprint_i2c("last message should have rd flag\n");
> > + goto err_out;
> > + }
> > +
> > + if (msgs[0].buf[0] == 0) {
> > + msgs[1].buf[0] = chip_id[id_ptr];
> > + if (chip_id[id_ptr] != 0)
> > + id_ptr += 1;
> > + } else {
> > + smi2021_read_reg(dev, msgs[0].addr, msgs[0].buf[0],
> > + msgs[1].buf);
> > + }
> > + break;
> > + }
> > + case 1: { /* Write reg */
> > + if (msgs[0].len == 0) {
> > + break;
> > + } else if (msgs[0].len != 2) {
> > + dprint_i2c("unsupported len\n");
> > + goto err_out;
> > + }
> > + if (msgs[0].buf[0] == 0) {
> > + /* We don't handle writing to addr 0x00 */
> > + break;
> > + }
> > +
> > + smi2021_write_reg(dev, msgs[0].addr, msgs[0].buf[0],
> > + msgs[0].buf[1]);
> > + break;
> > + }
> > + default: {
> > + dprint_i2c("driver can only handle 1 or 2 messages\n");
> > + goto err_out;
> > + }
> > + }
> > + return num;
> > +
> > +err_out:
> > + return -EOPNOTSUPP;
> > +}
> > +
> > +static u32 functionality(struct i2c_adapter *adap)
> > +{
> > + return I2C_FUNC_SMBUS_EMUL;
> > +}
> > +
> > +static struct i2c_algorithm algo = {
> > + .master_xfer = i2c_xfer,
> > + .functionality = functionality,
> > +};
> > +
> > +static struct i2c_adapter adap_template = {
> > + .owner = THIS_MODULE,
> > + .name = "smi2021_easycap_dc60",
> > + .algo = &algo,
> > +};
> > +
> > +static struct i2c_client client_template = {
> > + .name = "smi2021 internal",
> > +};
> > +
> > +int smi2021_i2c_register(struct smi2021_dev *dev)
> > +{
> > + int rc;
> > +
> > + id_ptr = 0;
> > +
> > + dev->i2c_adap = adap_template;
> > + dev->i2c_adap.dev.parent = dev->dev;
> > + strcpy(dev->i2c_adap.name, "smi2021");
> > + dev->i2c_adap.algo_data = dev;
> > +
> > + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
> > +
> > + rc = i2c_add_adapter(&dev->i2c_adap);
> > + if (rc < 0) {
> > + smi2021_err("can't add i2c adapter, errno: %d\n", rc);
> > + return rc;
> > + }
> > +
> > + dev->i2c_client = client_template;
> > + dev->i2c_client.adapter = &dev->i2c_adap;
> > +
> > + return 0;
> > +}
> > +
> > +int smi2021_i2c_unregister(struct smi2021_dev *dev)
> > +{
> > + i2c_del_adapter(&dev->i2c_adap);
> > + return 0;
> > +}
> >
>
> Regards,
>
> Hans
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-03-20 09:40:10

by Jon Arne Jørgensen

[permalink] [raw]
Subject: Re: [RFC V1 4/8] smi2021: Add smi2021_v4l2.c

On Mon, Mar 18, 2013 at 09:12:31AM +0100, Hans Verkuil wrote:
> On Thu March 14 2013 15:07:00 Jon Arne Jørgensen wrote:
> > This file is responsible for registering the device with the v4l2 subsystem,
> > and the communication with v4l2.
> > Most of the v4l2 ioctls are just passed on to vidbuf2.
> >
> > Signed-off-by: Jon Arne Jørgensen <[email protected]>
> > ---
> > drivers/media/usb/smi2021/smi2021_v4l2.c | 566 +++++++++++++++++++++++++++++++
> > 1 file changed, 566 insertions(+)
> > create mode 100644 drivers/media/usb/smi2021/smi2021_v4l2.c
> >
> > diff --git a/drivers/media/usb/smi2021/smi2021_v4l2.c b/drivers/media/usb/smi2021/smi2021_v4l2.c
> > new file mode 100644
> > index 0000000..d402093
> > --- /dev/null
> > +++ b/drivers/media/usb/smi2021/smi2021_v4l2.c
> > @@ -0,0 +1,566 @@
> > +/*******************************************************************************
> > + * smi2021_v4l2.c *
> > + * *
> > + * USB Driver for smi2021 - EasyCap *
> > + * USB ID 1c88:003c *
> > + * *
> > + * *****************************************************************************
> > + *
> > + * Copyright 2011-2013 Jon Arne Jørgensen
> > + * <jonjon.arnearne--a.t--gmail.com>
> > + *
> > + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> > + *
> > + * This file is part of SMI2021
> > + * http://code.google.com/p/easycap-somagic-linux/
> > + *
> > + * This program is free software: you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation, either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> > + *
> > + * This driver is heavily influensed by the STK1160 driver.
> > + * Copyright (C) 2012 Ezequiel Garcia
> > + * <elezegarcia--a.t--gmail.com>
> > + *
> > + */
> > +
> > +#include "smi2021.h"
> > +
> > +static struct smi2021_fmt format[] = {
>
> const.
>

Ok

> > + {
> > + .name = "16bpp YU2, 4:2:2, packed",
> > + .fourcc = V4L2_PIX_FMT_UYVY,
> > + .depth = 16,
> > + }
> > +};
> > +
> > +static const int inputs = 2;
> > +static struct smi2021_input input[] = {
> > + {
> > + .name = "Composite",
> > + .type = SAA7115_COMPOSITE0,
> > + },
> > + {
> > + .name = "S-Video",
> > + .type = SAA7115_SVIDEO1,
> > + }
> > +};
>
> const
>

Ditto

> > +
> > +static void smi2021_set_input(struct smi2021_dev *dev)
> > +{
> > + if (dev->udev == NULL)
> > + return;
>
> These udev tests are unnecessary. Once the video_unregister is called no
> new ioctls or file operations (except release()) can ever be issued.
>

Ok.

> > +
> > + if (dev->ctl_input >= inputs) {
> > + smi2021_err("BUG: ctl_input to big!\n");
> > + return;
> > + }
> > +
> > + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
> > + input[dev->ctl_input].type, 0, 0);
> > +}
> > +
> > +static int smi2021_start_streaming(struct smi2021_dev *dev)
> > +{
> > + u8 data[2];
> > + int i, rc = 0;
>
> Extra newline.
>

Ok.

I guess I also have to move this data array to the heap?

> > + data[0] = 0x01;
> > + data[1] = 0x05;
> > +
> > + if (!dev->udev)
> > + return -ENODEV;
> > +
> > + dev->sync_state = HSYNC;
> > + dev->buf_count = 0;
> > +
> > + if (mutex_lock_interruptible(&dev->v4l2_lock))
> > + return -ERESTARTSYS;
> > +
> > + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1);
> > +
> > + /* The saa 7115 driver sets these wrong,
> > + * this leads to sync issues.
> > + * V_GATE1_START
> > + * V_GATE1_STOP
> > + * V_GATE1_MSB
> > + * All these should be 0x00 for this device.
> > + */
> > + smi2021_write_reg(dev, 0x4a, 0x15, 0x00);
> > + smi2021_write_reg(dev, 0x4a, 0x16, 0x00);
> > + smi2021_write_reg(dev, 0x4a, 0x17, 0x00);
> > +
> > + rc = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
> > + 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> > + 0x01, 0x00, data, sizeof(data), 1000);
> > + if (rc < 0) {
> > + smi2021_err("Could not start device!\n");
> > + goto out_unlock;
> > + }
> > +
> > + /* It's mandatory to set alt interface before allocating isoc buffer */
> > + usb_set_interface(dev->udev, 0, 2);
> > +
> > + smi2021_write_reg(dev, 0, 0x1740, 0x1d);
> > +
> > + if (!dev->isoc_ctl.num_bufs) {
> > + rc = smi2021_alloc_isoc(dev);
> > + if (rc < 0)
> > + goto out_stop_hw;
> > + }
> > +
> > + /* submit urbs and enable IRQ */
> > + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
> > + rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_KERNEL);
> > + if (rc) {
> > + smi2021_err("cannot submit urb[%d] (%d)\n", i, rc);
> > + goto out_uninit;
> > + }
> > + }
> > +
> > + mutex_unlock(&dev->v4l2_lock);
> > +
> > + smi2021_dbg("Streaming started!");
> > + return 0;
> > +
> > +out_uninit:
> > + smi2021_uninit_isoc(dev);
> > +out_stop_hw:
> > + usb_set_interface(dev->udev, 0, 0);
> > + smi2021_clear_queue(dev);
> > +
> > +out_unlock:
> > + mutex_unlock(&dev->v4l2_lock);
> > +
> > + return rc;
> > +}
> > +
> > +/* Must be called with v4l2_lock hold */
> > +static void smi2021_stop_hw(struct smi2021_dev *dev)
> > +{
> > + int rc = 0;
> > + u8 data[] = { 0x01, 0x03 };
> > +
> > + if (!dev->udev)
> > + return;
> > +
> > + usb_set_interface(dev->udev, 0, 0);
> > +
> > + rc = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
> > + 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> > + 0x01, 0x00, data, sizeof(data), 1000);
> > + if (rc < 0)
> > + smi2021_err("Could not stop device!\n");
> > +
> > + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
> > +}
> > +
> > +static int smi2021_stop_streaming(struct smi2021_dev *dev)
> > +{
> > + /* HACK: Stop the audio subsystem,
> > + * without this, the pcm middle-layer will hang waiting for more data.
> > + *
> > + * Is there a better way to do this?
> > + */
> > + if (dev->pcm_substream && dev->pcm_substream->runtime) {
> > + struct snd_pcm_runtime *runtime = dev->pcm_substream->runtime;
> > + if (runtime->status) {
> > + runtime->status->state = SNDRV_PCM_STATE_DRAINING;
> > + wake_up(&runtime->sleep);
> > + }
> > + }
> > +
> > + if (mutex_lock_interruptible(&dev->v4l2_lock))
> > + return -ERESTARTSYS;
> > +
> > + smi2021_cancel_isoc(dev);
> > + smi2021_free_isoc(dev);
> > + smi2021_stop_hw(dev);
> > + smi2021_clear_queue(dev);
> > +
> > + smi2021_dbg("Streaming stopped!\n");
> > +
> > + mutex_unlock(&dev->v4l2_lock);
> > +
> > + return 0;
> > +}
> > +
> > +static struct v4l2_file_operations smi2021_fops = {
> > + .owner = THIS_MODULE,
> > + .open = v4l2_fh_open,
> > + .release = vb2_fop_release,
> > + .read = vb2_fop_read,
> > + .poll = vb2_fop_poll,
> > + .mmap = vb2_fop_mmap,
> > + .unlocked_ioctl = video_ioctl2,
> > +};
> > +
> > +/******************************************************************************/
> > +/* */
> > +/* Vidioc IOCTLS */
> > +/* */
> > +/******************************************************************************/
> > +
> > +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
> > + struct v4l2_fmtdesc *f)
> > +{
> > + if (f->index != 0)
> > + return -EINVAL;
> > +
> > + strlcpy(f->description, format[f->index].name, sizeof(f->description));
> > + f->pixelformat = format[f->index].fourcc;
> > +
> > + return 0;
> > +}
> > +
> > +static int vidioc_querycap(struct file *file, void *priv,
> > + struct v4l2_capability *cap)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > +
> > + strcpy(cap->driver, "smi2021");
> > + strcpy(cap->card, "smi2021");
> > + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
> > + cap->device_caps =
> > + V4L2_CAP_VIDEO_CAPTURE |
> > + V4L2_CAP_STREAMING |
> > + V4L2_CAP_READWRITE;
> > + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
> > +
> > + return 0;
> > +}
> > +
> > +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > +
> > + f->fmt.pix.pixelformat = dev->fmt->fourcc;
> > + f->fmt.pix.width = dev->width;
> > + f->fmt.pix.height = dev->height;
> > + f->fmt.pix.field = V4L2_FIELD_INTERLACED;
> > + f->fmt.pix.bytesperline = dev->width * 2;
> > + f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline;
> > + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
> > + f->fmt.pix.priv = 0;
> > +
> > + return 0;
> > +}
> > +
> > +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > +
> > + f->fmt.pix.pixelformat = dev->fmt->fourcc;
> > + f->fmt.pix.width = dev->width;
> > + f->fmt.pix.height = dev->height;
> > + f->fmt.pix.field = V4L2_FIELD_INTERLACED;
> > + f->fmt.pix.bytesperline = dev->width * 2;
> > + f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline;
> > + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
> > + f->fmt.pix.priv = 0;
> > +
> > + return 0;
> > +}
> > +
> > +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > + struct vb2_queue *q = &dev->vb_vidq;
> > +
> > + if (vb2_is_busy(q))
> > + return -EBUSY;
> > +
> > + vidioc_try_fmt_vid_cap(file, priv, f);
> > + return 0;
> > +}
> > +
> > +static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > +
> > + v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm);
> > + return 0;
> > +}
> > +
> > +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > +
> > + *norm = dev->norm;
> > + return 0;
> > +}
> > +
> > +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > + struct vb2_queue *q = &dev->vb_vidq;
> > +
> > + if (vb2_is_busy(q))
> > + return -EBUSY;
> > +
> > + if (!dev->udev)
> > + return -ENODEV;
> > +
> > + dev->norm = *norm;
> > + if (dev->norm & V4L2_STD_525_60) {
> > + dev->width = SMI2021_BYTES_PER_LINE / 2;
> > + dev->height = SMI2021_NTSC_LINES;
> > + } else if (dev->norm & V4L2_STD_625_50) {
> > + dev->width = SMI2021_BYTES_PER_LINE / 2;
> > + dev->height = SMI2021_PAL_LINES;
> > + } else {
> > + smi2021_err("Invalid standard\n");
> > + return -EINVAL;
> > + }
> > +
> > + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
> > + return 0;
> > +}
> > +
> > +static int vidioc_enum_input(struct file *file, void *priv,
> > + struct v4l2_input *i)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > +
> > + if (i->index >= inputs)
> > + return -EINVAL;
> > +
> > + strlcpy(i->name, input[i->index].name, sizeof(i->name));
> > + i->type = V4L2_INPUT_TYPE_CAMERA;
> > + i->std = dev->vdev.tvnorms;
> > + return 0;
> > +}
> > +
> > +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
>
> newline
>
> > + *i = dev->ctl_input;
> > + return 0;
> > +}
> > +
> > +static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
> > +{
> > + struct smi2021_dev *dev = video_drvdata(file);
> > +
> > + if (i >= inputs)
> > + return -EINVAL;
> > +
> > + dev->ctl_input = i;
> > + smi2021_set_input(dev);
> > +
> > + return 0;
> > +}
> > +
> > +static int vidioc_g_chip_ident(struct file *file, void *priv,
> > + struct v4l2_dbg_chip_ident *chip)
> > +{
> > + switch (chip->match.type) {
> > + case V4L2_CHIP_MATCH_HOST:
> > + chip->ident = V4L2_IDENT_NONE;
> > + chip->revision = 0;
> > + return 0;
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static const struct v4l2_ioctl_ops smi2021_ioctl_ops = {
> > + .vidioc_querycap = vidioc_querycap,
> > + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
> > + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
> > + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
> > + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
> > + .vidioc_querystd = vidioc_querystd,
> > + .vidioc_g_std = vidioc_g_std,
> > + .vidioc_s_std = vidioc_s_std,
> > + .vidioc_enum_input = vidioc_enum_input,
> > + .vidioc_g_input = vidioc_g_input,
> > + .vidioc_s_input = vidioc_s_input,
> > +
> > + /* vb2 handle these */
> > + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> > + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> > + .vidioc_querybuf = vb2_ioctl_querybuf,
> > + .vidioc_qbuf = vb2_ioctl_qbuf,
> > + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> > + .vidioc_streamon = vb2_ioctl_streamon,
> > + .vidioc_streamoff = vb2_ioctl_streamoff,
> > +
> > + .vidioc_log_status = v4l2_ctrl_log_status,
> > + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> > + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> > + .vidioc_g_chip_ident = vidioc_g_chip_ident,
> > +
> > +};
> > +
> > +/******************************************************************************/
> > +/* */
> > +/* Videobuf2 operations */
> > +/* */
> > +/******************************************************************************/
> > +static int queue_setup(struct vb2_queue *vq,
> > + const struct v4l2_format *v4l2_fmt,
> > + unsigned int *nbuffers, unsigned int *nplanes,
> > + unsigned int sizes[], void *alloc_ctxs[])
> > +{
> > + struct smi2021_dev *dev = vb2_get_drv_priv(vq);
> > + unsigned long size;
> > +
> > + size = dev->width * dev->height * 2;
> > +
> > + *nbuffers = clamp_t(unsigned int, *nbuffers, 2, 4);
> > +
> > + /* Packed color format */
> > + *nplanes = 1;
> > + sizes[0] = size;
> > +
> > + return 0;
> > +}
> > +
> > +static void buffer_queue(struct vb2_buffer *vb)
> > +{
> > + unsigned long flags;
> > + struct smi2021_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
> > + struct smi2021_buffer *buf = container_of(vb, struct smi2021_buffer,
> > + vb);
>
> Just put this in one line, the dangling vb looks odd.
>

Ok,
are you sure no one will kill me for breaking the 80
characters per line limit?

> > +
> > + spin_lock_irqsave(&dev->buf_lock, flags);
> > + if (!dev->udev) {
> > + /*
> > + * If the device is disconnected return the buffer to userspace
> > + * directly. The next QBUF call will fail with -ENODEV.
> > + */
> > + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> > + } else {
> > + buf->mem = vb2_plane_vaddr(vb, 0);
> > + buf->length = vb2_plane_size(vb, 0);
> > +
> > + buf->pos = 0;
> > + buf->trc_av = 0;
> > + buf->in_blank = true;
> > + buf->second_field = false;
> > +
> > + if (buf->length < dev->width * dev->height * 2)
> > + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> > + else
> > + list_add_tail(&buf->list, &dev->avail_bufs);
> > + }
> > + spin_unlock_irqrestore(&dev->buf_lock, flags);
> > +}
> > +
> > +static int start_streaming(struct vb2_queue *vq, unsigned int count)
> > +{
> > + struct smi2021_dev *dev = vb2_get_drv_priv(vq);
>
> newline
>
> > + return smi2021_start_streaming(dev);
>
> Why not move smi2021_start_streaming in here?
>

That is a good question, I'll do that.

> > +}
> > +
> > +static int stop_streaming(struct vb2_queue *vq)
> > +{
> > + struct smi2021_dev *dev = vb2_get_drv_priv(vq);
>
> newline
>
> > + return smi2021_stop_streaming(dev);
>
> same question.
>

Same answer.

> > +}
> > +
> > +static struct vb2_ops smi2021_video_qops = {
> > + .queue_setup = queue_setup,
> > + .buf_queue = buffer_queue,
> > + .start_streaming = start_streaming,
> > + .stop_streaming = stop_streaming,
> > + .wait_prepare = vb2_ops_wait_prepare,
> > + .wait_finish = vb2_ops_wait_finish,
> > +};
> > +
> > +static struct video_device v4l2_template = {
> > + .name = "easycap_smi2021_dc60",
> > + .tvnorms = V4L2_STD_625_50 | V4L2_STD_525_60,
>
> Use V4L2_STD_ALL.
>

Ok

> > + .fops = &smi2021_fops,
> > + .ioctl_ops = &smi2021_ioctl_ops,
> > + .release = video_device_release_empty,
> > +};
> > +
> > +/* Must be called with both v4l2_lock and vb_queue_lock hold */
> > +void smi2021_clear_queue(struct smi2021_dev *dev)
> > +{
> > + struct smi2021_buffer *buf;
> > + unsigned long flags;
> > +
> > + spin_lock_irqsave(&dev->buf_lock, flags);
> > + while (!list_empty(&dev->avail_bufs)) {
> > + buf = list_first_entry(&dev->avail_bufs,
> > + struct smi2021_buffer, list);
> > + list_del(&buf->list);
> > + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> > + }
> > + dev->isoc_ctl.buf = NULL;
> > + spin_unlock_irqrestore(&dev->buf_lock, flags);
> > +}
> > +
> > +int smi2021_vb2_setup(struct smi2021_dev *dev)
> > +{
> > + int rc;
> > + struct vb2_queue *q;
> > +
> > + q = &dev->vb_vidq;
> > + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> > + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
> > + q->drv_priv = dev;
> > + q->buf_struct_size = sizeof(struct smi2021_buffer);
> > + q->ops = &smi2021_video_qops;
> > + q->mem_ops = &vb2_vmalloc_memops;
> > +
> > + rc = vb2_queue_init(q);
> > + if (rc < 0)
> > + return rc;
> > +
> > + /* Initialize video dma queue */
> > + INIT_LIST_HEAD(&dev->avail_bufs);
> > +
> > + return 0;
> > +}
> > +
> > +int smi2021_video_register(struct smi2021_dev *dev)
> > +{
> > + int rc;
> > +
> > + dev->vdev = v4l2_template;
> > + dev->vdev.debug = 0;
>
> No need for this, it's already 0.
>

I had it there to make it easy for me to enable debug :)

> > + dev->vdev.queue = &dev->vb_vidq;
> > +
> > + dev->vdev.lock = &dev->v4l2_lock;
> > + dev->vdev.queue->lock = &dev->vb_queue_lock;
> > +
> > + dev->vdev.v4l2_dev = &dev->v4l2_dev;
> > + set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags);
> > +
> > + /* PAL is default */
> > + dev->norm = V4L2_STD_PAL;
> > + dev->width = SMI2021_BYTES_PER_LINE / 2;
> > + dev->height = SMI2021_PAL_LINES;
> > +
> > + dev->fmt = &format[0];
> > +
> > + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
> > + smi2021_set_input(dev);
> > +
> > + video_set_drvdata(&dev->vdev, dev);
> > + rc = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1);
> > + if (rc < 0) {
> > + smi2021_err("video_register_device failed %d\n", rc);
> > + return rc;
> > + }
> > +
> > + v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
> > + video_device_node_name(&dev->vdev));
> > +
> > + return 0;
> > +}
> >
>
> Regards,
>
> Hans
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-03-20 09:45:25

by Jon Arne Jørgensen

[permalink] [raw]
Subject: Re: [RFC V1 4/8] smi2021: Add smi2021_v4l2.c

On Mon, Mar 18, 2013 at 09:29:07AM +0100, Hans Verkuil wrote:
> On Thu March 14 2013 15:07:00 Jon Arne Jørgensen wrote:
> > This file is responsible for registering the device with the v4l2 subsystem,
> > and the communication with v4l2.
> > Most of the v4l2 ioctls are just passed on to vidbuf2.
> >
> > Signed-off-by: Jon Arne Jørgensen <[email protected]>
> > ---
> > drivers/media/usb/smi2021/smi2021_v4l2.c | 566 +++++++++++++++++++++++++++++++
> > 1 file changed, 566 insertions(+)
> > create mode 100644 drivers/media/usb/smi2021/smi2021_v4l2.c
> >
> > diff --git a/drivers/media/usb/smi2021/smi2021_v4l2.c b/drivers/media/usb/smi2021/smi2021_v4l2.c
> > new file mode 100644
> > index 0000000..d402093
> > --- /dev/null
> > +++ b/drivers/media/usb/smi2021/smi2021_v4l2.c
> > @@ -0,0 +1,566 @@
>
> ...
>
> > +int smi2021_vb2_setup(struct smi2021_dev *dev)
> > +{
> > + int rc;
> > + struct vb2_queue *q;
> > +
> > + q = &dev->vb_vidq;
> > + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> > + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
> > + q->drv_priv = dev;
> > + q->buf_struct_size = sizeof(struct smi2021_buffer);
> > + q->ops = &smi2021_video_qops;
> > + q->mem_ops = &vb2_vmalloc_memops;
>
> q->timestamp_type isn't filled in.
>
I'll add that

> For that matter, neither the sequence number nor the timestamp are filled in
> in v4l2_buffer during capturing.
>
> You need to add a buf_finish op to fill those in (use v4l2_timestamp() for the
> timestamp).
>

I'm filling these variables in the smi2021_buffer_done function in
smi2021_video.c?

Should I do that somewhere else?


> Regards,
>
> Hans
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-03-20 10:03:23

by Jon Arne Jørgensen

[permalink] [raw]
Subject: Re: [RFC V1 5/8] smi2021: Add smi2021_video.c

On Mon, Mar 18, 2013 at 09:58:32AM +0100, Bjørn Mork wrote:
> Hans Verkuil <[email protected]> writes:
>
> >> +/*
> >> + *
> >> + * The device delivers data in chunks of 0x400 bytes.
> >> + * The four first bytes is a magic header to identify the chunks.
> >> + * 0xaa 0xaa 0x00 0x00 = saa7113 Active Video Data
> >> + * 0xaa 0xaa 0x00 0x01 = PCM - 24Bit 2 Channel audio data
> >> + */
> >> +static void process_packet(struct smi2021_dev *dev, u8 *p, int len)
> >> +{
> >> + int i;
> >> + u32 *header;
> >> +
> >> + if (len % 0x400 != 0) {
> >> + printk_ratelimited(KERN_INFO "smi2021::%s: len: %d\n",
> >> + __func__, len);
> >> + return;
> >> + }
> >> +
> >> + for (i = 0; i < len; i += 0x400) {
> >> + header = (u32 *)(p + i);
> >> + switch (__cpu_to_be32(*header)) {
> >
> > That's not right. You probably mean __be32_to_cpu, that makes more sense.
> >
> >> + case 0xaaaa0000: {
> >> + parse_video(dev, p+i+4, 0x400-4);
> >> + break;
> >> + }
> >> + case 0xaaaa0001: {
> >> + smi2021_audio(dev, p+i+4, 0x400-4);
> >> + break;
> >> + }
>
> This could be just me, but I would have done it like this to take
> advantage of compile time constant conversions (and also dropping the
> noisy extra {}s):
>
> switch (*header) {
> case cpu_to_be32(0xaaaa0000):
> parse_video(dev, p+i+4, 0x400-4);
> break;
> case cpu_to_be32(0xaaaa0001):
> smi2021_audio(dev, p+i+4, 0x400-4);
> break;
> ..
>
>
> >From the name of the function I assume the difference may actually be
> measurable here if this runs for every processed packet.
>
>

You are both right, I will have a second look at this code.
I guess I'll try to implement Bjørns suggestion.

As I'm working with a byte-array, I could probably change this code to:

if (header[0] == 0xaa && header[1] == 0xaa
&& header[2] == 0x00 && header[3] == 0x00) {

{...}

} else if (header[0] == 0xaa && header[1] == 0xaa
&& header[2] == 0x00 && header[3] == 0x01) {

{...}
}

But I hope you agree that the switch statement is cleaner?

(I just find it hard to wrap my head around the big vs. little endian
differences when dealing with hexadecimal integer notation :) )

> Bjørn
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-03-20 10:08:29

by Jon Arne Jørgensen

[permalink] [raw]
Subject: Re: [RFC V1 0/8] Add a driver for somagic smi2021

On Sun, Mar 17, 2013 at 09:05:08PM -0300, Ezequiel Garcia wrote:
> Hi Jon,
>
> On Sun, Mar 17, 2013 at 09:01:58PM +0100, Jon Arne Jørgensen wrote:
> > On Fri, Mar 15, 2013 at 09:08:58AM -0300, Ezequiel Garcia wrote:
> > > On Thu, Mar 14, 2013 at 03:06:56PM +0100, Jon Arne Jørgensen wrote:
> > > > This patch-set will add a driver for the Somagic SMI2021 chip.
> > > >
> > > > This chip is found inside different usb video-capture devices.
> > > > Most of them are branded as EasyCap, but there also seems to be
> > > > some other brands selling devices with this chip.
> > > >
> > > > This driver is split into two modules, where one is called smi2021-bootloader,
> > > > and the other is just called smi2021.
> > > >
> > > > The bootloader is responsible for the upload of a firmware that is needed by some
> > > > versions of the devices.
> > > >
> > > > All Somagic devices that need firmware seems to identify themselves
> > > > with the usb product id 0x0007. There is no way for the kernel to know
> > > > what firmware to upload to the device without user interaction.
> > > >
> > > > If there is only one firmware present on the computer, the kernel
> > > > will upload that firmware to any device that identifies as 0x0007.
> > > > If there are multiple Somagic firmwares present, the user will have to pass
> > > > a module parameter to the smi2021-bootloader module to tell what firmware to use.
> > > >
> > >
> > > Nice job!
> > >
> > Thanks :)
> >
> > > I have some minor comments on each patch, but also I don't agree
> > > with the patch splitting: what's the point in splitting and sending
> > > one patch per file?
> > >
> > > It doesn't make it any easier to review, so why don't you just
> > > send one patch: "Introduce smi2021 driver"?
> > >
> > > The rule is one patch per change, and I believe this whole patchset
> > > is just one change: adding a new driver.
> > >
> >
> > I think I read another patch to this mailinglist, where someone was told
> > to split his patch into one mail per file, but I can't find that thread
> > now :)
> >
> > I will send the next version as a single mail, and see what happens...
> >
>
> As you will soon realize, the patch preparation is equally important as the
> patch content itself. Often, it takes the same time to implement or
> fix something, as it takes to prepare the patchset carefully.
>

I'm beginning to realize that yes :)

> When deciding how to prepare your patches you have to keep two main things in mind:
>
> * The kernel build can never be broken, in any configuration and in any
> point of the kernel history (in other words, by any patch of a patchset).
> This is called 'keep the bisectability' because it's essential
> to make 'git bisect' work properly.
>

Then it does make good sense to send this whole driver as a singe email.

> * Do as much as possible to facilitate reviews from other people.
> This is also important because patches tend to be accepted quicker
> if they recieve attention (reviews, testing, etc.).
>
> In this particular case, I think that the easier way to review is to be
> able to see the complete driver in a single patch. Of course, I can be
> wrong, so feel free to correct me.
>
> Please note that the reviews I made where almost nitpicks, and the
> driver looks good in general. I cannot provide any testing for lack of hardware.
>
> Also, for your next patch, add the output of v4l2-compliance tool,
> showing it passes all the tests. This shows your driver is in good shape.
> Get v4l2-compliancefrom the git repo, as distribution often provide
> an outdated version.
>

I'll do this.

> (And another thing, please fix your mail client: the reply-to is pointing
> to '[email protected]'.)
>

Yes, my first attempts to send mail from mutt, classic PEBCAP...

Again, thank you for your time.

> Thanks for your work and best regards,
> --
> Ezequiel García, Free Electrons
> Embedded Linux, Kernel and Android Engineering
> http://free-electrons.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-03-20 10:13:09

by Jon Arne Jørgensen

[permalink] [raw]
Subject: Re: [RFC V1 4/8] smi2021: Add smi2021_v4l2.c

On Wed, Mar 20, 2013 at 11:10:57AM +0100, Hans Verkuil wrote:
> On Wed 20 March 2013 10:48:42 Jon Arne Jørgensen wrote:
> > On Mon, Mar 18, 2013 at 09:29:07AM +0100, Hans Verkuil wrote:
> > > On Thu March 14 2013 15:07:00 Jon Arne Jørgensen wrote:
> > > > This file is responsible for registering the device with the v4l2 subsystem,
> > > > and the communication with v4l2.
> > > > Most of the v4l2 ioctls are just passed on to vidbuf2.
> > > >
> > > > Signed-off-by: Jon Arne Jørgensen <[email protected]>
> > > > ---
> > > > drivers/media/usb/smi2021/smi2021_v4l2.c | 566 +++++++++++++++++++++++++++++++
> > > > 1 file changed, 566 insertions(+)
> > > > create mode 100644 drivers/media/usb/smi2021/smi2021_v4l2.c
> > > >
> > > > diff --git a/drivers/media/usb/smi2021/smi2021_v4l2.c b/drivers/media/usb/smi2021/smi2021_v4l2.c
> > > > new file mode 100644
> > > > index 0000000..d402093
> > > > --- /dev/null
> > > > +++ b/drivers/media/usb/smi2021/smi2021_v4l2.c
> > > > @@ -0,0 +1,566 @@
> > >
> > > ...
> > >
> > > > +int smi2021_vb2_setup(struct smi2021_dev *dev)
> > > > +{
> > > > + int rc;
> > > > + struct vb2_queue *q;
> > > > +
> > > > + q = &dev->vb_vidq;
> > > > + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> > > > + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
> > > > + q->drv_priv = dev;
> > > > + q->buf_struct_size = sizeof(struct smi2021_buffer);
> > > > + q->ops = &smi2021_video_qops;
> > > > + q->mem_ops = &vb2_vmalloc_memops;
> > >
> > > q->timestamp_type isn't filled in.
> > >
> > I'll add that
> >
> > > For that matter, neither the sequence number nor the timestamp are filled in
> > > in v4l2_buffer during capturing.
> > >
> > > You need to add a buf_finish op to fill those in (use v4l2_timestamp() for the
> > > timestamp).
> > >
> >
> > I'm filling these variables in the smi2021_buffer_done function in
> > smi2021_video.c?
>
> Ah, I missed that. Sorry about that.
>
> Just replace gettimeofday with v4l2_timestamp(), though. We no longer use
> gettimeofday() in new drivers, but instead we use the monotonic clock.
>

No problem,
I'll fix this.

> Regards,
>
> Hans
>
> > Should I do that somewhere else?
> >
> >
> > > Regards,
> > >
> > > Hans
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-media" in
> > > the body of a message to [email protected]
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-03-20 10:15:08

by Hans Verkuil

[permalink] [raw]
Subject: Re: [RFC V1 4/8] smi2021: Add smi2021_v4l2.c

On Wed 20 March 2013 10:48:42 Jon Arne Jørgensen wrote:
> On Mon, Mar 18, 2013 at 09:29:07AM +0100, Hans Verkuil wrote:
> > On Thu March 14 2013 15:07:00 Jon Arne Jørgensen wrote:
> > > This file is responsible for registering the device with the v4l2 subsystem,
> > > and the communication with v4l2.
> > > Most of the v4l2 ioctls are just passed on to vidbuf2.
> > >
> > > Signed-off-by: Jon Arne Jørgensen <[email protected]>
> > > ---
> > > drivers/media/usb/smi2021/smi2021_v4l2.c | 566 +++++++++++++++++++++++++++++++
> > > 1 file changed, 566 insertions(+)
> > > create mode 100644 drivers/media/usb/smi2021/smi2021_v4l2.c
> > >
> > > diff --git a/drivers/media/usb/smi2021/smi2021_v4l2.c b/drivers/media/usb/smi2021/smi2021_v4l2.c
> > > new file mode 100644
> > > index 0000000..d402093
> > > --- /dev/null
> > > +++ b/drivers/media/usb/smi2021/smi2021_v4l2.c
> > > @@ -0,0 +1,566 @@
> >
> > ...
> >
> > > +int smi2021_vb2_setup(struct smi2021_dev *dev)
> > > +{
> > > + int rc;
> > > + struct vb2_queue *q;
> > > +
> > > + q = &dev->vb_vidq;
> > > + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> > > + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
> > > + q->drv_priv = dev;
> > > + q->buf_struct_size = sizeof(struct smi2021_buffer);
> > > + q->ops = &smi2021_video_qops;
> > > + q->mem_ops = &vb2_vmalloc_memops;
> >
> > q->timestamp_type isn't filled in.
> >
> I'll add that
>
> > For that matter, neither the sequence number nor the timestamp are filled in
> > in v4l2_buffer during capturing.
> >
> > You need to add a buf_finish op to fill those in (use v4l2_timestamp() for the
> > timestamp).
> >
>
> I'm filling these variables in the smi2021_buffer_done function in
> smi2021_video.c?

Ah, I missed that. Sorry about that.

Just replace gettimeofday with v4l2_timestamp(), though. We no longer use
gettimeofday() in new drivers, but instead we use the monotonic clock.

Regards,

Hans

> Should I do that somewhere else?
>
>
> > Regards,
> >
> > Hans
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-media" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>

2013-03-20 10:18:39

by Hans Verkuil

[permalink] [raw]
Subject: Re: [RFC V1 4/8] smi2021: Add smi2021_v4l2.c

On Wed 20 March 2013 10:43:26 Jon Arne Jørgensen wrote:
> On Mon, Mar 18, 2013 at 09:12:31AM +0100, Hans Verkuil wrote:
> > On Thu March 14 2013 15:07:00 Jon Arne Jørgensen wrote:
> > > This file is responsible for registering the device with the v4l2 subsystem,
> > > and the communication with v4l2.
> > > Most of the v4l2 ioctls are just passed on to vidbuf2.
> > >
> > > Signed-off-by: Jon Arne Jørgensen <[email protected]>
> > > ---
> > > drivers/media/usb/smi2021/smi2021_v4l2.c | 566 +++++++++++++++++++++++++++++++
> > > 1 file changed, 566 insertions(+)
> > > create mode 100644 drivers/media/usb/smi2021/smi2021_v4l2.c
> > >
> > > diff --git a/drivers/media/usb/smi2021/smi2021_v4l2.c b/drivers/media/usb/smi2021/smi2021_v4l2.c
> > > new file mode 100644
> > > index 0000000..d402093
> > > --- /dev/null
> > > +++ b/drivers/media/usb/smi2021/smi2021_v4l2.c
> > > @@ -0,0 +1,566 @@
> > > +/*******************************************************************************
> > > + * smi2021_v4l2.c *
> > > + * *
> > > + * USB Driver for smi2021 - EasyCap *
> > > + * USB ID 1c88:003c *
> > > + * *
> > > + * *****************************************************************************
> > > + *
> > > + * Copyright 2011-2013 Jon Arne Jørgensen
> > > + * <jonjon.arnearne--a.t--gmail.com>
> > > + *
> > > + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston
> > > + *
> > > + * This file is part of SMI2021
> > > + * http://code.google.com/p/easycap-somagic-linux/
> > > + *
> > > + * This program is free software: you can redistribute it and/or modify
> > > + * it under the terms of the GNU General Public License as published by
> > > + * the Free Software Foundation, either version 2 of the License, or
> > > + * (at your option) any later version.
> > > + *
> > > + * This program is distributed in the hope that it will be useful,
> > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > > + * GNU General Public License for more details.
> > > + *
> > > + * You should have received a copy of the GNU General Public License
> > > + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> > > + *
> > > + * This driver is heavily influensed by the STK1160 driver.
> > > + * Copyright (C) 2012 Ezequiel Garcia
> > > + * <elezegarcia--a.t--gmail.com>
> > > + *
> > > + */
> > > +
> > > +#include "smi2021.h"
> > > +
> > > +static struct smi2021_fmt format[] = {
> >
> > const.
> >
>
> Ok
>
> > > + {
> > > + .name = "16bpp YU2, 4:2:2, packed",
> > > + .fourcc = V4L2_PIX_FMT_UYVY,
> > > + .depth = 16,
> > > + }
> > > +};
> > > +
> > > +static const int inputs = 2;
> > > +static struct smi2021_input input[] = {
> > > + {
> > > + .name = "Composite",
> > > + .type = SAA7115_COMPOSITE0,
> > > + },
> > > + {
> > > + .name = "S-Video",
> > > + .type = SAA7115_SVIDEO1,
> > > + }
> > > +};
> >
> > const
> >
>
> Ditto
>
> > > +
> > > +static void smi2021_set_input(struct smi2021_dev *dev)
> > > +{
> > > + if (dev->udev == NULL)
> > > + return;
> >
> > These udev tests are unnecessary. Once the video_unregister is called no
> > new ioctls or file operations (except release()) can ever be issued.
> >
>
> Ok.
>
> > > +
> > > + if (dev->ctl_input >= inputs) {
> > > + smi2021_err("BUG: ctl_input to big!\n");
> > > + return;
> > > + }
> > > +
> > > + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
> > > + input[dev->ctl_input].type, 0, 0);
> > > +}
> > > +
> > > +static int smi2021_start_streaming(struct smi2021_dev *dev)
> > > +{
> > > + u8 data[2];
> > > + int i, rc = 0;
> >
> > Extra newline.
> >
>
> Ok.
>
> I guess I also have to move this data array to the heap?

Yes.

>
> > > + data[0] = 0x01;
> > > + data[1] = 0x05;
> > > +
> > > + if (!dev->udev)
> > > + return -ENODEV;
> > > +
> > > + dev->sync_state = HSYNC;
> > > + dev->buf_count = 0;
> > > +
> > > + if (mutex_lock_interruptible(&dev->v4l2_lock))
> > > + return -ERESTARTSYS;
> > > +
> > > + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1);
> > > +
> > > + /* The saa 7115 driver sets these wrong,
> > > + * this leads to sync issues.
> > > + * V_GATE1_START
> > > + * V_GATE1_STOP
> > > + * V_GATE1_MSB
> > > + * All these should be 0x00 for this device.
> > > + */
> > > + smi2021_write_reg(dev, 0x4a, 0x15, 0x00);
> > > + smi2021_write_reg(dev, 0x4a, 0x16, 0x00);
> > > + smi2021_write_reg(dev, 0x4a, 0x17, 0x00);
> > > +
> > > + rc = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
> > > + 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> > > + 0x01, 0x00, data, sizeof(data), 1000);
> > > + if (rc < 0) {
> > > + smi2021_err("Could not start device!\n");
> > > + goto out_unlock;
> > > + }
> > > +
> > > + /* It's mandatory to set alt interface before allocating isoc buffer */
> > > + usb_set_interface(dev->udev, 0, 2);
> > > +
> > > + smi2021_write_reg(dev, 0, 0x1740, 0x1d);
> > > +
> > > + if (!dev->isoc_ctl.num_bufs) {
> > > + rc = smi2021_alloc_isoc(dev);
> > > + if (rc < 0)
> > > + goto out_stop_hw;
> > > + }
> > > +
> > > + /* submit urbs and enable IRQ */
> > > + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
> > > + rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_KERNEL);
> > > + if (rc) {
> > > + smi2021_err("cannot submit urb[%d] (%d)\n", i, rc);
> > > + goto out_uninit;
> > > + }
> > > + }
> > > +
> > > + mutex_unlock(&dev->v4l2_lock);
> > > +
> > > + smi2021_dbg("Streaming started!");
> > > + return 0;
> > > +
> > > +out_uninit:
> > > + smi2021_uninit_isoc(dev);
> > > +out_stop_hw:
> > > + usb_set_interface(dev->udev, 0, 0);
> > > + smi2021_clear_queue(dev);
> > > +
> > > +out_unlock:
> > > + mutex_unlock(&dev->v4l2_lock);
> > > +
> > > + return rc;
> > > +}
> > > +
> > > +/* Must be called with v4l2_lock hold */
> > > +static void smi2021_stop_hw(struct smi2021_dev *dev)
> > > +{
> > > + int rc = 0;
> > > + u8 data[] = { 0x01, 0x03 };
> > > +
> > > + if (!dev->udev)
> > > + return;
> > > +
> > > + usb_set_interface(dev->udev, 0, 0);
> > > +
> > > + rc = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0x00),
> > > + 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> > > + 0x01, 0x00, data, sizeof(data), 1000);
> > > + if (rc < 0)
> > > + smi2021_err("Could not stop device!\n");
> > > +
> > > + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
> > > +}
> > > +
> > > +static int smi2021_stop_streaming(struct smi2021_dev *dev)
> > > +{
> > > + /* HACK: Stop the audio subsystem,
> > > + * without this, the pcm middle-layer will hang waiting for more data.
> > > + *
> > > + * Is there a better way to do this?
> > > + */
> > > + if (dev->pcm_substream && dev->pcm_substream->runtime) {
> > > + struct snd_pcm_runtime *runtime = dev->pcm_substream->runtime;
> > > + if (runtime->status) {
> > > + runtime->status->state = SNDRV_PCM_STATE_DRAINING;
> > > + wake_up(&runtime->sleep);
> > > + }
> > > + }
> > > +
> > > + if (mutex_lock_interruptible(&dev->v4l2_lock))
> > > + return -ERESTARTSYS;
> > > +
> > > + smi2021_cancel_isoc(dev);
> > > + smi2021_free_isoc(dev);
> > > + smi2021_stop_hw(dev);
> > > + smi2021_clear_queue(dev);
> > > +
> > > + smi2021_dbg("Streaming stopped!\n");
> > > +
> > > + mutex_unlock(&dev->v4l2_lock);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static struct v4l2_file_operations smi2021_fops = {
> > > + .owner = THIS_MODULE,
> > > + .open = v4l2_fh_open,
> > > + .release = vb2_fop_release,
> > > + .read = vb2_fop_read,
> > > + .poll = vb2_fop_poll,
> > > + .mmap = vb2_fop_mmap,
> > > + .unlocked_ioctl = video_ioctl2,
> > > +};
> > > +
> > > +/******************************************************************************/
> > > +/* */
> > > +/* Vidioc IOCTLS */
> > > +/* */
> > > +/******************************************************************************/
> > > +
> > > +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
> > > + struct v4l2_fmtdesc *f)
> > > +{
> > > + if (f->index != 0)
> > > + return -EINVAL;
> > > +
> > > + strlcpy(f->description, format[f->index].name, sizeof(f->description));
> > > + f->pixelformat = format[f->index].fourcc;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int vidioc_querycap(struct file *file, void *priv,
> > > + struct v4l2_capability *cap)
> > > +{
> > > + struct smi2021_dev *dev = video_drvdata(file);
> > > +
> > > + strcpy(cap->driver, "smi2021");
> > > + strcpy(cap->card, "smi2021");
> > > + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
> > > + cap->device_caps =
> > > + V4L2_CAP_VIDEO_CAPTURE |
> > > + V4L2_CAP_STREAMING |
> > > + V4L2_CAP_READWRITE;
> > > + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
> > > + struct v4l2_format *f)
> > > +{
> > > + struct smi2021_dev *dev = video_drvdata(file);
> > > +
> > > + f->fmt.pix.pixelformat = dev->fmt->fourcc;
> > > + f->fmt.pix.width = dev->width;
> > > + f->fmt.pix.height = dev->height;
> > > + f->fmt.pix.field = V4L2_FIELD_INTERLACED;
> > > + f->fmt.pix.bytesperline = dev->width * 2;
> > > + f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline;
> > > + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
> > > + f->fmt.pix.priv = 0;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
> > > + struct v4l2_format *f)
> > > +{
> > > + struct smi2021_dev *dev = video_drvdata(file);
> > > +
> > > + f->fmt.pix.pixelformat = dev->fmt->fourcc;
> > > + f->fmt.pix.width = dev->width;
> > > + f->fmt.pix.height = dev->height;
> > > + f->fmt.pix.field = V4L2_FIELD_INTERLACED;
> > > + f->fmt.pix.bytesperline = dev->width * 2;
> > > + f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline;
> > > + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
> > > + f->fmt.pix.priv = 0;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
> > > + struct v4l2_format *f)
> > > +{
> > > + struct smi2021_dev *dev = video_drvdata(file);
> > > + struct vb2_queue *q = &dev->vb_vidq;
> > > +
> > > + if (vb2_is_busy(q))
> > > + return -EBUSY;
> > > +
> > > + vidioc_try_fmt_vid_cap(file, priv, f);
> > > + return 0;
> > > +}
> > > +
> > > +static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm)
> > > +{
> > > + struct smi2021_dev *dev = video_drvdata(file);
> > > +
> > > + v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm);
> > > + return 0;
> > > +}
> > > +
> > > +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
> > > +{
> > > + struct smi2021_dev *dev = video_drvdata(file);
> > > +
> > > + *norm = dev->norm;
> > > + return 0;
> > > +}
> > > +
> > > +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
> > > +{
> > > + struct smi2021_dev *dev = video_drvdata(file);
> > > + struct vb2_queue *q = &dev->vb_vidq;
> > > +
> > > + if (vb2_is_busy(q))
> > > + return -EBUSY;
> > > +
> > > + if (!dev->udev)
> > > + return -ENODEV;
> > > +
> > > + dev->norm = *norm;
> > > + if (dev->norm & V4L2_STD_525_60) {
> > > + dev->width = SMI2021_BYTES_PER_LINE / 2;
> > > + dev->height = SMI2021_NTSC_LINES;
> > > + } else if (dev->norm & V4L2_STD_625_50) {
> > > + dev->width = SMI2021_BYTES_PER_LINE / 2;
> > > + dev->height = SMI2021_PAL_LINES;
> > > + } else {
> > > + smi2021_err("Invalid standard\n");
> > > + return -EINVAL;
> > > + }
> > > +
> > > + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
> > > + return 0;
> > > +}
> > > +
> > > +static int vidioc_enum_input(struct file *file, void *priv,
> > > + struct v4l2_input *i)
> > > +{
> > > + struct smi2021_dev *dev = video_drvdata(file);
> > > +
> > > + if (i->index >= inputs)
> > > + return -EINVAL;
> > > +
> > > + strlcpy(i->name, input[i->index].name, sizeof(i->name));
> > > + i->type = V4L2_INPUT_TYPE_CAMERA;
> > > + i->std = dev->vdev.tvnorms;
> > > + return 0;
> > > +}
> > > +
> > > +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
> > > +{
> > > + struct smi2021_dev *dev = video_drvdata(file);
> >
> > newline
> >
> > > + *i = dev->ctl_input;
> > > + return 0;
> > > +}
> > > +
> > > +static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
> > > +{
> > > + struct smi2021_dev *dev = video_drvdata(file);
> > > +
> > > + if (i >= inputs)
> > > + return -EINVAL;
> > > +
> > > + dev->ctl_input = i;
> > > + smi2021_set_input(dev);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int vidioc_g_chip_ident(struct file *file, void *priv,
> > > + struct v4l2_dbg_chip_ident *chip)
> > > +{
> > > + switch (chip->match.type) {
> > > + case V4L2_CHIP_MATCH_HOST:
> > > + chip->ident = V4L2_IDENT_NONE;
> > > + chip->revision = 0;
> > > + return 0;
> > > + default:
> > > + return -EINVAL;
> > > + }
> > > +}
> > > +
> > > +static const struct v4l2_ioctl_ops smi2021_ioctl_ops = {
> > > + .vidioc_querycap = vidioc_querycap,
> > > + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
> > > + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
> > > + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
> > > + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
> > > + .vidioc_querystd = vidioc_querystd,
> > > + .vidioc_g_std = vidioc_g_std,
> > > + .vidioc_s_std = vidioc_s_std,
> > > + .vidioc_enum_input = vidioc_enum_input,
> > > + .vidioc_g_input = vidioc_g_input,
> > > + .vidioc_s_input = vidioc_s_input,
> > > +
> > > + /* vb2 handle these */
> > > + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> > > + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> > > + .vidioc_querybuf = vb2_ioctl_querybuf,
> > > + .vidioc_qbuf = vb2_ioctl_qbuf,
> > > + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> > > + .vidioc_streamon = vb2_ioctl_streamon,
> > > + .vidioc_streamoff = vb2_ioctl_streamoff,
> > > +
> > > + .vidioc_log_status = v4l2_ctrl_log_status,
> > > + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> > > + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> > > + .vidioc_g_chip_ident = vidioc_g_chip_ident,
> > > +
> > > +};
> > > +
> > > +/******************************************************************************/
> > > +/* */
> > > +/* Videobuf2 operations */
> > > +/* */
> > > +/******************************************************************************/
> > > +static int queue_setup(struct vb2_queue *vq,
> > > + const struct v4l2_format *v4l2_fmt,
> > > + unsigned int *nbuffers, unsigned int *nplanes,
> > > + unsigned int sizes[], void *alloc_ctxs[])
> > > +{
> > > + struct smi2021_dev *dev = vb2_get_drv_priv(vq);
> > > + unsigned long size;
> > > +
> > > + size = dev->width * dev->height * 2;
> > > +
> > > + *nbuffers = clamp_t(unsigned int, *nbuffers, 2, 4);
> > > +
> > > + /* Packed color format */
> > > + *nplanes = 1;
> > > + sizes[0] = size;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static void buffer_queue(struct vb2_buffer *vb)
> > > +{
> > > + unsigned long flags;
> > > + struct smi2021_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
> > > + struct smi2021_buffer *buf = container_of(vb, struct smi2021_buffer,
> > > + vb);
> >
> > Just put this in one line, the dangling vb looks odd.
> >
>
> Ok,
> are you sure no one will kill me for breaking the 80
> characters per line limit?

I won't :-) This is always a contentious issue. The basic rule is keep to 80
chars per line unless it degrades readability. IMHO breaking the line degrades
readability in this case, but since what is readable for one person is
unreadable for another you get different opinions.

My basic rule is that I do what I think is best, and if Mauro complains about
it then I just fix it rather than argue about it. I don't generally have
problems with this.

>
> > > +
> > > + spin_lock_irqsave(&dev->buf_lock, flags);
> > > + if (!dev->udev) {
> > > + /*
> > > + * If the device is disconnected return the buffer to userspace
> > > + * directly. The next QBUF call will fail with -ENODEV.
> > > + */
> > > + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> > > + } else {
> > > + buf->mem = vb2_plane_vaddr(vb, 0);
> > > + buf->length = vb2_plane_size(vb, 0);
> > > +
> > > + buf->pos = 0;
> > > + buf->trc_av = 0;
> > > + buf->in_blank = true;
> > > + buf->second_field = false;
> > > +
> > > + if (buf->length < dev->width * dev->height * 2)
> > > + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> > > + else
> > > + list_add_tail(&buf->list, &dev->avail_bufs);
> > > + }
> > > + spin_unlock_irqrestore(&dev->buf_lock, flags);
> > > +}
> > > +
> > > +static int start_streaming(struct vb2_queue *vq, unsigned int count)
> > > +{
> > > + struct smi2021_dev *dev = vb2_get_drv_priv(vq);
> >
> > newline
> >
> > > + return smi2021_start_streaming(dev);
> >
> > Why not move smi2021_start_streaming in here?
> >
>
> That is a good question, I'll do that.
>
> > > +}
> > > +
> > > +static int stop_streaming(struct vb2_queue *vq)
> > > +{
> > > + struct smi2021_dev *dev = vb2_get_drv_priv(vq);
> >
> > newline
> >
> > > + return smi2021_stop_streaming(dev);
> >
> > same question.
> >
>
> Same answer.
>
> > > +}
> > > +
> > > +static struct vb2_ops smi2021_video_qops = {
> > > + .queue_setup = queue_setup,
> > > + .buf_queue = buffer_queue,
> > > + .start_streaming = start_streaming,
> > > + .stop_streaming = stop_streaming,
> > > + .wait_prepare = vb2_ops_wait_prepare,
> > > + .wait_finish = vb2_ops_wait_finish,
> > > +};
> > > +
> > > +static struct video_device v4l2_template = {
> > > + .name = "easycap_smi2021_dc60",
> > > + .tvnorms = V4L2_STD_625_50 | V4L2_STD_525_60,
> >
> > Use V4L2_STD_ALL.
> >
>
> Ok
>
> > > + .fops = &smi2021_fops,
> > > + .ioctl_ops = &smi2021_ioctl_ops,
> > > + .release = video_device_release_empty,
> > > +};
> > > +
> > > +/* Must be called with both v4l2_lock and vb_queue_lock hold */
> > > +void smi2021_clear_queue(struct smi2021_dev *dev)
> > > +{
> > > + struct smi2021_buffer *buf;
> > > + unsigned long flags;
> > > +
> > > + spin_lock_irqsave(&dev->buf_lock, flags);
> > > + while (!list_empty(&dev->avail_bufs)) {
> > > + buf = list_first_entry(&dev->avail_bufs,
> > > + struct smi2021_buffer, list);
> > > + list_del(&buf->list);
> > > + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> > > + }
> > > + dev->isoc_ctl.buf = NULL;
> > > + spin_unlock_irqrestore(&dev->buf_lock, flags);
> > > +}
> > > +
> > > +int smi2021_vb2_setup(struct smi2021_dev *dev)
> > > +{
> > > + int rc;
> > > + struct vb2_queue *q;
> > > +
> > > + q = &dev->vb_vidq;
> > > + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> > > + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
> > > + q->drv_priv = dev;
> > > + q->buf_struct_size = sizeof(struct smi2021_buffer);
> > > + q->ops = &smi2021_video_qops;
> > > + q->mem_ops = &vb2_vmalloc_memops;
> > > +
> > > + rc = vb2_queue_init(q);
> > > + if (rc < 0)
> > > + return rc;
> > > +
> > > + /* Initialize video dma queue */
> > > + INIT_LIST_HEAD(&dev->avail_bufs);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +int smi2021_video_register(struct smi2021_dev *dev)
> > > +{
> > > + int rc;
> > > +
> > > + dev->vdev = v4l2_template;
> > > + dev->vdev.debug = 0;
> >
> > No need for this, it's already 0.
> >
>
> I had it there to make it easy for me to enable debug :)

Note that you can set the debug level yourself:

echo 1 (or 2) >/sys/class/video4linux/video0/debug

Drivers shouldn't touch vdev.debug anymore.

Regards,

Hans

2013-03-20 10:19:56

by Hans Verkuil

[permalink] [raw]
Subject: Re: [RFC V1 5/8] smi2021: Add smi2021_video.c

On Wed 20 March 2013 11:06:36 Jon Arne Jørgensen wrote:
> On Mon, Mar 18, 2013 at 09:58:32AM +0100, Bjørn Mork wrote:
> > Hans Verkuil <[email protected]> writes:
> >
> > >> +/*
> > >> + *
> > >> + * The device delivers data in chunks of 0x400 bytes.
> > >> + * The four first bytes is a magic header to identify the chunks.
> > >> + * 0xaa 0xaa 0x00 0x00 = saa7113 Active Video Data
> > >> + * 0xaa 0xaa 0x00 0x01 = PCM - 24Bit 2 Channel audio data
> > >> + */
> > >> +static void process_packet(struct smi2021_dev *dev, u8 *p, int len)
> > >> +{
> > >> + int i;
> > >> + u32 *header;
> > >> +
> > >> + if (len % 0x400 != 0) {
> > >> + printk_ratelimited(KERN_INFO "smi2021::%s: len: %d\n",
> > >> + __func__, len);
> > >> + return;
> > >> + }
> > >> +
> > >> + for (i = 0; i < len; i += 0x400) {
> > >> + header = (u32 *)(p + i);
> > >> + switch (__cpu_to_be32(*header)) {
> > >
> > > That's not right. You probably mean __be32_to_cpu, that makes more sense.
> > >
> > >> + case 0xaaaa0000: {
> > >> + parse_video(dev, p+i+4, 0x400-4);
> > >> + break;
> > >> + }
> > >> + case 0xaaaa0001: {
> > >> + smi2021_audio(dev, p+i+4, 0x400-4);
> > >> + break;
> > >> + }
> >
> > This could be just me, but I would have done it like this to take
> > advantage of compile time constant conversions (and also dropping the
> > noisy extra {}s):
> >
> > switch (*header) {
> > case cpu_to_be32(0xaaaa0000):
> > parse_video(dev, p+i+4, 0x400-4);
> > break;
> > case cpu_to_be32(0xaaaa0001):
> > smi2021_audio(dev, p+i+4, 0x400-4);
> > break;
> > ..
> >
> >
> > >From the name of the function I assume the difference may actually be
> > measurable here if this runs for every processed packet.
> >
> >
>
> You are both right, I will have a second look at this code.
> I guess I'll try to implement Bjørns suggestion.
>
> As I'm working with a byte-array, I could probably change this code to:
>
> if (header[0] == 0xaa && header[1] == 0xaa
> && header[2] == 0x00 && header[3] == 0x00) {
>
> {...}
>
> } else if (header[0] == 0xaa && header[1] == 0xaa
> && header[2] == 0x00 && header[3] == 0x01) {
>
> {...}
> }
>
> But I hope you agree that the switch statement is cleaner?
>
> (I just find it hard to wrap my head around the big vs. little endian
> differences when dealing with hexadecimal integer notation :) )

Bjørn's solution is best in this case.

Regards,

Hans

>
> > Bjørn
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-media" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>

2013-03-20 10:22:47

by Hans Verkuil

[permalink] [raw]
Subject: Re: [RFC V1 4/8] smi2021: Add smi2021_v4l2.c

On Wed 20 March 2013 11:16:26 Jon Arne Jørgensen wrote:
> On Wed, Mar 20, 2013 at 11:10:57AM +0100, Hans Verkuil wrote:
> > On Wed 20 March 2013 10:48:42 Jon Arne Jørgensen wrote:
> > > On Mon, Mar 18, 2013 at 09:29:07AM +0100, Hans Verkuil wrote:
> > > > On Thu March 14 2013 15:07:00 Jon Arne Jørgensen wrote:
> > > > > This file is responsible for registering the device with the v4l2 subsystem,
> > > > > and the communication with v4l2.
> > > > > Most of the v4l2 ioctls are just passed on to vidbuf2.
> > > > >
> > > > > Signed-off-by: Jon Arne Jørgensen <[email protected]>
> > > > > ---
> > > > > drivers/media/usb/smi2021/smi2021_v4l2.c | 566 +++++++++++++++++++++++++++++++
> > > > > 1 file changed, 566 insertions(+)
> > > > > create mode 100644 drivers/media/usb/smi2021/smi2021_v4l2.c
> > > > >
> > > > > diff --git a/drivers/media/usb/smi2021/smi2021_v4l2.c b/drivers/media/usb/smi2021/smi2021_v4l2.c
> > > > > new file mode 100644
> > > > > index 0000000..d402093
> > > > > --- /dev/null
> > > > > +++ b/drivers/media/usb/smi2021/smi2021_v4l2.c
> > > > > @@ -0,0 +1,566 @@
> > > >
> > > > ...
> > > >
> > > > > +int smi2021_vb2_setup(struct smi2021_dev *dev)
> > > > > +{
> > > > > + int rc;
> > > > > + struct vb2_queue *q;
> > > > > +
> > > > > + q = &dev->vb_vidq;
> > > > > + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> > > > > + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
> > > > > + q->drv_priv = dev;
> > > > > + q->buf_struct_size = sizeof(struct smi2021_buffer);
> > > > > + q->ops = &smi2021_video_qops;
> > > > > + q->mem_ops = &vb2_vmalloc_memops;
> > > >
> > > > q->timestamp_type isn't filled in.
> > > >
> > > I'll add that
> > >
> > > > For that matter, neither the sequence number nor the timestamp are filled in
> > > > in v4l2_buffer during capturing.
> > > >
> > > > You need to add a buf_finish op to fill those in (use v4l2_timestamp() for the
> > > > timestamp).
> > > >
> > >
> > > I'm filling these variables in the smi2021_buffer_done function in
> > > smi2021_video.c?
> >
> > Ah, I missed that. Sorry about that.
> >
> > Just replace gettimeofday with v4l2_timestamp(), though. We no longer use
> > gettimeofday() in new drivers, but instead we use the monotonic clock.
> >
>
> No problem,
> I'll fix this.

BTW, I've tried your driver with my somagic USB device, but it doesn't work
for me. I get -71 errors on the USB bus. I do seem to have all the right chips
including the gm7113 (saa7113 replacement).

I need to double-check under Windows whether it is really working...

Regards,

Hans

2013-03-20 11:06:19

by Jon Arne Jørgensen

[permalink] [raw]
Subject: Re: [RFC V1 4/8] smi2021: Add smi2021_v4l2.c

On Wed, Mar 20, 2013 at 11:21:48AM +0100, Hans Verkuil wrote:
> On Wed 20 March 2013 11:16:26 Jon Arne Jørgensen wrote:
> > On Wed, Mar 20, 2013 at 11:10:57AM +0100, Hans Verkuil wrote:
> > > On Wed 20 March 2013 10:48:42 Jon Arne Jørgensen wrote:
> > > > On Mon, Mar 18, 2013 at 09:29:07AM +0100, Hans Verkuil wrote:
> > > > > On Thu March 14 2013 15:07:00 Jon Arne Jørgensen wrote:
> > > > > > This file is responsible for registering the device with the v4l2 subsystem,
> > > > > > and the communication with v4l2.
> > > > > > Most of the v4l2 ioctls are just passed on to vidbuf2.
> > > > > >
> > > > > > Signed-off-by: Jon Arne Jørgensen <[email protected]>
> > > > > > ---
> > > > > > drivers/media/usb/smi2021/smi2021_v4l2.c | 566 +++++++++++++++++++++++++++++++
> > > > > > 1 file changed, 566 insertions(+)
> > > > > > create mode 100644 drivers/media/usb/smi2021/smi2021_v4l2.c
> > > > > >
> > > > > > diff --git a/drivers/media/usb/smi2021/smi2021_v4l2.c b/drivers/media/usb/smi2021/smi2021_v4l2.c
> > > > > > new file mode 100644
> > > > > > index 0000000..d402093
> > > > > > --- /dev/null
> > > > > > +++ b/drivers/media/usb/smi2021/smi2021_v4l2.c
> > > > > > @@ -0,0 +1,566 @@
> > > > >
> > > > > ...
> > > > >
> > > > > > +int smi2021_vb2_setup(struct smi2021_dev *dev)
> > > > > > +{
> > > > > > + int rc;
> > > > > > + struct vb2_queue *q;
> > > > > > +
> > > > > > + q = &dev->vb_vidq;
> > > > > > + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> > > > > > + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
> > > > > > + q->drv_priv = dev;
> > > > > > + q->buf_struct_size = sizeof(struct smi2021_buffer);
> > > > > > + q->ops = &smi2021_video_qops;
> > > > > > + q->mem_ops = &vb2_vmalloc_memops;
> > > > >
> > > > > q->timestamp_type isn't filled in.
> > > > >
> > > > I'll add that
> > > >
> > > > > For that matter, neither the sequence number nor the timestamp are filled in
> > > > > in v4l2_buffer during capturing.
> > > > >
> > > > > You need to add a buf_finish op to fill those in (use v4l2_timestamp() for the
> > > > > timestamp).
> > > > >
> > > >
> > > > I'm filling these variables in the smi2021_buffer_done function in
> > > > smi2021_video.c?
> > >
> > > Ah, I missed that. Sorry about that.
> > >
> > > Just replace gettimeofday with v4l2_timestamp(), though. We no longer use
> > > gettimeofday() in new drivers, but instead we use the monotonic clock.
> > >
> >
> > No problem,
> > I'll fix this.
>
> BTW, I've tried your driver with my somagic USB device, but it doesn't work
> for me. I get -71 errors on the USB bus. I do seem to have all the right chips
> including the gm7113 (saa7113 replacement).
>
> I need to double-check under Windows whether it is really working...

Hm, I sometimes get two of these errors when starting a capture, but
after that my device keeps running smoothly.

If you have time, can you please also test your device with the userspace
(libusb) tool for the somagic devices.

You'll find it here:
http://code.google.com/p/easycap-somagic-linux/

>
> Regards,
>
> Hans