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

[patch 04/06] ti_usb, changing firmware: adopting userspace helper



pp-by: Oleg Verych
---
 drivers/usb/serial/ti_usb_3410_5052.c |  148 +++++++++++++++++++++-------------
 drivers/usb/serial/ti_usb_3410_5052.h |   27 +++---
 2 files changed, 109 insertions(+), 66 deletions(-)

Index: linux-source-2.6.18/drivers/usb/serial/ti_usb_3410_5052.h
===================================================================
--- linux-source-2.6.18.orig/drivers/usb/serial/ti_usb_3410_5052.h	2007-02-23 09:12:43.276763750 +0100
+++ linux-source-2.6.18/drivers/usb/serial/ti_usb_3410_5052.h	2007-02-23 09:13:05.310140750 +0100
@@ -21,8 +21,8 @@
 #define _TI_3410_5052_H_
 
 /* Configuration ids */
-#define TI_BOOT_CONFIG			1
-#define TI_ACTIVE_CONFIG		2
+#define TI_BOOT_CONFIG			1 /* boot config to get firmware  */
+#define TI_ACTIVE_CONFIG		2 /* actual working device config */
 
 /* Vendor and product ids */
 #define TI_VENDOR_ID			0x0451
@@ -206,19 +206,24 @@
 #define TI_CODE_DATA_ERROR		0x03
 #define TI_CODE_MODEM_STATUS		0x04
 
-/* Download firmware max packet size */
-#define TI_DOWNLOAD_MAX_PACKET_SIZE	64
-
-/* Firmware image header */
-struct ti_firmware_header {
-	__le16	wLength;
-	__u8	bCheckSum;
-} __attribute__((packed));
-
 /* UART addresses */
 #define TI_UART1_BASE_ADDR		0xFFA0	/* UART 1 base address */
 #define TI_UART2_BASE_ADDR		0xFFB0	/* UART 2 base address */
 #define TI_UART_OFFSET_LCR		0x0002	/* UART MCR register offset */
 #define TI_UART_OFFSET_MCR		0x0004	/* UART MCR register offset */
 
+/* Firmware */
+#define TI_FW_PACKET_SIZE		64
+#define TI_MAX_FIRMWARE_SIZE		16284
+
+#define ti_fw_file_3410			"umpf3410.i51"
+#define ti_fw_file_5052			"umpf5052.i51"
+
+typedef union {
+	__le32 a; /* all */
+	struct {
+		__le32 sz : 16, cs : 8;
+	} d;
+} ti_firmware_header_t;
+
 #endif /* _TI_3410_5052_H_ */
Index: linux-source-2.6.18/drivers/usb/serial/ti_usb_3410_5052.c
===================================================================
--- linux-source-2.6.18.orig/drivers/usb/serial/ti_usb_3410_5052.c	2007-02-23 09:12:43.276763750 +0100
+++ linux-source-2.6.18/drivers/usb/serial/ti_usb_3410_5052.c	2007-02-23 09:13:05.314141000 +0100
@@ -33,20 +33,15 @@
 #include <asm/semaphore.h>
 #include <linux/usb.h>
 #include <linux/usb/serial.h>
-
+#include <linux/firmware.h>
 #include "ti_usb_3410_5052.h"
-#include "ti_fw_3410.h"		/* firmware image for 3410 */
-#include "ti_fw_5052.h"		/* firmware image for 5052 */
-
 
 /* Defines */
 
-#define TI_DRIVER_VERSION	"v0.9"
+#define TI_DRIVER_VERSION	"v0.92"
 #define TI_DRIVER_AUTHOR	"Al Borchers <alborchers@steinerpoint.com>"
 #define TI_DRIVER_DESC		"TI USB 3410/5052 Serial Driver"
 
-#define TI_FIRMWARE_BUF_SIZE	16284
-
 #define TI_WRITE_BUF_SIZE	1024
 
 #define TI_TRANSFER_TIMEOUT	2
@@ -143,8 +138,7 @@
 static int ti_write_byte(struct ti_device *tdev, unsigned long addr,
 	__u8 mask, __u8 byte);
 
-static int ti_download_firmware(struct ti_device *tdev,
-	unsigned char *firmware, unsigned int firmware_size);
+static int ti_fw_change(struct ti_device *tdev, const char *filename);
 
 /* circular buffer */
 static struct circ_buf *ti_buf_alloc(void);
@@ -382,13 +376,8 @@
 
 	/* if we have only 1 configuration, download firmware */
 	if (dev->descriptor.bNumConfigurations == 1) {
-
-		if (tdev->td_is_3410)
-			status = ti_download_firmware(tdev, ti_fw_3410,
-				sizeof(ti_fw_3410));
-		else
-			status = ti_download_firmware(tdev, ti_fw_5052,
-				sizeof(ti_fw_5052));
+		status = ti_fw_change(tdev, tdev->td_is_3410 ?
+				      ti_fw_file_3410 : ti_fw_file_5052);
 		if (status)
 			goto free_tdev;
 
@@ -1594,57 +1583,106 @@
 }
 
 
-static int ti_download_firmware(struct ti_device *tdev,
-	unsigned char *firmware, unsigned int firmware_size)
+static int ti_fw_change(struct ti_device *tdev, const char *filename)
 {
-	int status = 0;
-	int buffer_size;
-	int pos;
-	int len;
-	int done;
-	__u8 cs = 0;
-	__u8 *buffer;
-	struct usb_device *dev = tdev->td_serial->dev;
-	struct ti_firmware_header *header;
-	unsigned int pipe = usb_sndbulkpipe(dev,
-		tdev->td_serial->port[0]->bulk_out_endpointAddress);
+#define udev		(tdev->td_serial->dev)
+#define fw_endpoint	(tdev->td_serial->port[0]->bulk_out_endpointAddress)
 
+	const struct firmware *fw_data_ptr;
+	size_t size;
+	int w;
 
-	buffer_size = TI_FIRMWARE_BUF_SIZE + sizeof(struct ti_firmware_header);
-	buffer = kmalloc(buffer_size, GFP_KERNEL);
-	if (!buffer) {
-		dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__);
-		return -ENOMEM;
+	dbg("%s() start; requesting firmware from userspace.",__FUNCTION__);
+
+	w = request_firmware(&fw_data_ptr, filename, &udev->dev);
+	if (w) {
+		dev_err(&udev->dev, "userspace firmware helper failed.\n");
+		return w;
 	}
 
-	memcpy(buffer, firmware, firmware_size);
-	memset(buffer+firmware_size, 0xff, buffer_size-firmware_size);
+	size = fw_data_ptr->size;
 
-	for(pos = sizeof(struct ti_firmware_header); pos < buffer_size; pos++)
-		cs = (__u8)(cs + buffer[pos]);
+	if (likely(size <= TI_MAX_FIRMWARE_SIZE)) {
+		ti_firmware_header_t h;
 
-	header = (struct ti_firmware_header *)buffer;
-	header->wLength = cpu_to_le16((__u16)(buffer_size - sizeof(struct ti_firmware_header)));
-	header->bCheckSum = cs;
-
-	dbg("%s - downloading firmware", __FUNCTION__);
-	for (pos = 0; pos < buffer_size; pos += done) {
-		len = min(buffer_size - pos, TI_DOWNLOAD_MAX_PACKET_SIZE);
-		status = usb_bulk_msg(dev, pipe, buffer+pos, len, &done, 1000);
-		if (status)
-			break;
+		if (size % TI_FW_PACKET_SIZE) {
+			dev_err(&udev->dev, "firmware size isn't %u modulo.\n"
+				"Care to provide one.\n", TI_FW_PACKET_SIZE);
+			return -EIO;
+		}
+
+		/* constructing header: size_LSB size_MSB CRCB */
+		h.a = 0x000000;
+		h.d.sz = cpu_to_le16(size);
+
+		/* w is zero here and used as index */
+		do {
+			h.d.cs += fw_data_ptr->data[w++];
+		} while (w < size);
+
+		/* using `w' as buffer */
+		w = (int) h.a;
+		dbg("cs: %#x; w: %#x", h.d.cs, w);
+	} else {
+		dev_err(&udev->dev,"firmware is too big.\n");
+		return -EFBIG;
 	}
 
-	kfree(buffer);
+	dbg("starting downloading %#zx bytes of firmware.", size);
 
-	if (status) {
-		dev_err(&dev->dev, "%s - error downloading firmware, %d\n", __FUNCTION__, status);
-		return status;
-	}
+	do {
+		u8 *fw;
+		int gone;
+		unsigned int pipe = usb_sndbulkpipe(udev, fw_endpoint);
+
+		/* XXX implement retry? */
+		w = usb_bulk_msg(udev, pipe, &w, 3, &gone, 1024);
+		if (gone != 3) {
+			if (!w)
+				w = -EIO;
+			break;
+		}
 
-	dbg("%s - download successful", __FUNCTION__);
+		/*
+		 * 3-4 12-bit pages, this is not much for kmalloc(),
+		 * why request_firmware() doesn't allocate with it?
+		 */
+		fw = kmalloc(size, GFP_KERNEL);
+		if (!fw) {
+			w = -ENOMEM;
+			break;
+		}
 
-	return 0;
+		memcpy(fw, fw_data_ptr->data, size);
+
+		size /= TI_FW_PACKET_SIZE;
+
+		do {
+			w = usb_bulk_msg(udev, pipe, fw, TI_FW_PACKET_SIZE,
+					 &gone, 1024);
+			if (gone != TI_FW_PACKET_SIZE) {
+			/*
+			 * unless bulk_msg can be sent partially,
+			 * `if' above can be removed
+			 */
+				if (!w)
+					w = -EIO;
+				break;
+			} else {
+				fw += TI_FW_PACKET_SIZE;
+			}
+		} while (--size);
+
+		kfree(fw);
+	} while (0);
+
+	release_firmware(fw_data_ptr);
+
+	dbg("%s() done with result %#x.\n",__FUNCTION__, w);
+
+	return w;
+#undef fw_endpoint
+#undef dev
 }
 
 

--



Reply to: