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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

Signed-off-by: Jon Arne Jørgensen <jonarne@xxxxxxxxxx>
---
 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

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux