RFC: ti_usb-serial: userspace firmware, internal cfg. change, cleanup

Oleg Verych <[email protected]>
Newsgroups gmane.linux.usb.devel
Organization Palacky University in Olomouc, experimental physics department
Message-ID <[email protected]>
Al, Greg.

This is all-in-one patch for show.

o  File 'tiusb3410.i51' or  'tiusb5052.i51' is being requested from the
   userspace, usually '/lib/firmware/'.

o  Reconfiguration is done internally.

o  kmalloc() isn't used for copying-copying of the binary. Optimistic
   allocation always requires 4 pages for maximum 16k of data.

o  git-nuke ti_fw_3410.h ti_fw_5052.h

o  Base Linux version is 2.6.22 with Oliver's 'usb-serial.c' fix. Tested
   with addition of pl2303-based cabel.

Please review.
_____________

--- ti_usb_3410_5052.c.orig	2007-09-22 06:52:30.000000000 +0200
+++ ti_usb_3410_5052.c	2007-11-14 16:15:16.284468500 +0100
@@ -1,8 +1,8 @@
-/* vi: ts=8 sw=8
- *
+/*
  * TI 3410/5052 USB Serial Driver
  *
  * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2007 Oleg Verych
  *
  * This driver is based on the Linux io_ti driver, which is
  *   Copyright (C) 2000-2002 Inside Out Networks
@@ -13,59 +13,8 @@
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
- * For questions or problems with this driver, contact Texas Instruments
- * technical support, or Al Borchers <[email protected]>, or
- * Peter Berger <[email protected]>.
- * 
- * This driver needs this hotplug script in /etc/hotplug/usb/ti_usb_3410_5052
- * or in /etc/hotplug.d/usb/ti_usb_3410_5052.hotplug to set the device
- * configuration.
- *
- * #!/bin/bash
- *
- * BOOT_CONFIG=1
- * ACTIVE_CONFIG=2
- *
- * if [[ "$ACTION" != "add" ]]
- * then
- * 	exit
- * fi
- *
- * CONFIG_PATH=/sys${DEVPATH%/?*}/bConfigurationValue
- *
- * if [[ 0`cat $CONFIG_PATH` -ne $BOOT_CONFIG ]]
- * then
- * 	exit
- * fi
- *
- * PRODUCT=${PRODUCT%/?*}		# delete version
- * VENDOR_ID=`printf "%d" 0x${PRODUCT%/?*}`
- * PRODUCT_ID=`printf "%d" 0x${PRODUCT#*?/}`
- *
- * PARAM_PATH=/sys/module/ti_usb_3410_5052/parameters
- *
- * function scan() {
- * 	s=$1
- * 	shift
- * 	for i
- * 	do
- * 		if [[ $s -eq $i ]]
- * 		then
- * 			return 0
- * 		fi
- * 	done
- * 	return 1
- * }
- *
- * IFS=$IFS,
- *
- * if (scan $VENDOR_ID 1105 `cat $PARAM_PATH/vendor_3410` &&
- * scan $PRODUCT_ID 13328 `cat $PARAM_PATH/product_3410`) ||
- * (scan $VENDOR_ID 1105 `cat $PARAM_PATH/vendor_5052` &&
- * scan $PRODUCT_ID 20562 20818 20570 20575 `cat $PARAM_PATH/product_5052`)
- * then
- * 	echo $ACTIVE_CONFIG > $CONFIG_PATH
- * fi
+ * For questions or problems with this driver, you may try to contact
+ * Al Borchers <[email protected]>.
  */
 
 #include <linux/kernel.h>
@@ -84,20 +33,13 @@
 #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.94"
 #define TI_DRIVER_AUTHOR	"Al Borchers <[email protected]>"
 #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
@@ -194,8 +136,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);
@@ -253,7 +194,7 @@
 	.probe			= usb_serial_probe,
 	.disconnect		= usb_serial_disconnect,
 	.id_table		= ti_id_table_combined,
-	.no_dynamic_id = 	1,
+	.no_dynamic_id =	1,
 };
 
 static struct usb_serial_driver ti_1port_device = {
@@ -413,7 +354,7 @@
 	int i;
 
 
-	dbg("%s - product 0x%4X, num configurations %d, configuration value %d",
+	dbg("%s - product %#4x, num configurations %d, configuration value %d",
 	    __FUNCTION__, le16_to_cpu(dev->descriptor.idProduct),
 	    dev->descriptor.bNumConfigurations,
 	    dev->actconfig->desc.bConfigurationValue);
@@ -431,44 +372,38 @@
 	/* determine device type */
 	if (usb_match_id(serial->interface, ti_id_table_3410))
 		tdev->td_is_3410 = 1;
-	dbg("%s - device type is %s", __FUNCTION__, tdev->td_is_3410 ? "3410" : "5052");
+	dbg("device: %s", tdev->td_is_3410 ? "3410" : "5052");
 
 	/* 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_3410 : ti_fw_5052);
 		if (status)
 			goto free_tdev;
 
 		/* 3410 must be reset, 5052 resets itself */
 		if (tdev->td_is_3410) {
-			msleep_interruptible(100);
+			msleep_interruptible(128);
 			usb_reset_device(dev);
 		}
 
-		status = -ENODEV;
+		status = 0x01E; /* positive status, device to be reconfigured */
 		goto free_tdev;
-	} 
+	}
 
-	/* the second configuration must be set (in sysfs by hotplug script) */
 	if (dev->actconfig->desc.bConfigurationValue == TI_BOOT_CONFIG) {
-		status = -ENODEV;
+		/* reconfigure outside; even if it fails, nothing can be done */
+		(void) usb_driver_set_configuration(dev, TI_ACTIVE_CONFIG);
+		status = 0xA1B;
 		goto free_tdev;
 	}
 
 	/* set up port structures */
 	for (i = 0; i < serial->num_ports; ++i) {
 		tport = kzalloc(sizeof(struct ti_port), GFP_KERNEL);
-		if (tport == NULL) {
-			dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__);
-			status = -ENOMEM;
+		if (tport == NULL)
 			goto free_tports;
-		}
+
 		spin_lock_init(&tport->tp_lock);
 		tport->tp_uart_base_addr = (i == 0 ? TI_UART1_BASE_ADDR : TI_UART2_BASE_ADDR);
 		tport->tp_flags = low_latency ? ASYNC_LOW_LATENCY : 0;
@@ -477,9 +412,7 @@
 		init_waitqueue_head(&tport->tp_write_wait);
 		tport->tp_write_buf = ti_buf_alloc();
 		if (tport->tp_write_buf == NULL) {
-			dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__);
 			kfree(tport);
-			status = -ENOMEM;
 			goto free_tports;
 		}
 		tport->tp_port = serial->port[i];
@@ -487,7 +420,7 @@
 		usb_set_serial_port_data(serial->port[i], tport);
 		tport->tp_uart_mode = 0;	/* default is RS232 */
 	}
-	
+
 	return 0;
 
 free_tports:
@@ -497,6 +430,8 @@
 		kfree(tport);
 		usb_set_serial_port_data(serial->port[i], NULL);
 	}
+	dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__);
+	status = -ENOMEM;
 free_tdev:
 	kfree(tdev);
 	usb_set_serial_data(serial, NULL);
@@ -534,8 +469,8 @@
 	struct urb *urb;
 	int port_number;
 	int status;
-	__u16 open_settings = (__u8)(TI_PIPE_MODE_CONTINOUS | 
-			     TI_PIPE_TIMEOUT_ENABLE | 
+	__u16 open_settings = (__u8)(TI_PIPE_MODE_CONTINOUS |
+			     TI_PIPE_TIMEOUT_ENABLE |
 			     (TI_TRANSFER_TIMEOUT << 2));
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
@@ -551,7 +486,7 @@
 		return -ERESTARTSYS;
 
 	if (port->tty)
-		port->tty->low_latency = 
+		port->tty->low_latency =
 			(tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 
 	port_number = port->number - port->serial->minor;
@@ -677,7 +612,7 @@
 	int do_up;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
-			 
+
 	tdev = usb_get_serial_data(port->serial);
 	tport = usb_get_serial_port_data(port);
 	if (tdev == NULL || tport == NULL)
@@ -750,7 +685,7 @@
 
 	if (tport == NULL)
 		return -ENODEV;
-	
+
 	spin_lock_irqsave(&tport->tp_lock, flags);
 	room = ti_buf_space_avail(tport->tp_write_buf);
 	spin_unlock_irqrestore(&tport->tp_lock, flags);
@@ -957,7 +892,7 @@
 		}
 	} else {
 		config->wFlags &= ~TI_UART_ENABLE_PARITY_CHECKING;
-		config->bParity = TI_UART_NO_PARITY; 	
+		config->bParity = TI_UART_NO_PARITY;
 	}
 
 	if (cflag & CSTOPB)
@@ -1337,7 +1272,7 @@
 	result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
 	if (result) {
 		dev_err(&port->dev, "%s - submit write urb failed, %d\n", __FUNCTION__, result);
-		tport->tp_write_urb_in_use = 0; 
+		tport->tp_write_urb_in_use = 0;
 		/* TODO: reschedule ti_send */
 	} else {
 		spin_lock_irqsave(&tport->tp_lock, flags);
@@ -1648,57 +1583,69 @@
 }
 
 
-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);
-
-
-	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;
-	}
-
-	memcpy(buffer, firmware, firmware_size);
-	memset(buffer+firmware_size, 0xff, buffer_size-firmware_size);
-
-	for(pos = sizeof(struct ti_firmware_header); pos < buffer_size; pos++)
-		cs = (__u8)(cs + buffer[pos]);
-
-	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;
-	}
-
-	kfree(buffer);
-
-	if (status) {
-		dev_err(&dev->dev, "%s - error downloading firmware, %d\n", __FUNCTION__, status);
-		return status;
+#define usb	(tdev->td_serial->dev)
+	const struct firmware *fw_data_ptr;
+	u8 *fw;
+	size_t size;
+	int w;
+
+	/* XXX this interface has to have required data size limit */
+	w = request_firmware(&fw_data_ptr, filename, &usb->dev);
+	if (w) {
+		dev_err(&usb->dev, "userspace firmware loader failed.\n");
+		return w;
+	}
+	size = fw_data_ptr->size;
+
+	if (size <= TI_FW_MAX) {
+		/* optimistic allocation */
+		fw = (u8 *) __get_free_pages(GFP_KERNEL, get_order(size));
+		if (fw) {
+			uint cp = 0;
+			/*
+			 * FIXME: checksum to userspace!
+			 * w is zero here and used as index
+			 */
+			do {
+				cp += fw_data_ptr->data[w++];
+			} while (w < size);
+			memcpy(fw, fw_data_ptr->data, size);
+			/*
+			 * 3 byte header: le16 size, u8 checksum 
+			 * doing it separately for aligment/speed of fw data
+			 */
+			 w = cpu_to_le32((cp << 16) | size);
+			size /= TI_FW_SEND;
+			cp = usb_sndbulkpipe(usb, 
+			tdev->td_serial->port[0]->bulk_out_endpointAddress);
+
+			w  = usb_bulk_msg(usb, cp, &w, 3, NULL, 1024);
+			do {
+				/* any errors, we are out */
+				if (w)
+					break;
+				w = usb_bulk_msg(usb, cp, fw, TI_FW_SEND,
+						 NULL, 1024);
+				fw += TI_FW_SEND;
+			} while (--size);
+			/* tail */
+			size = fw_data_ptr->size % TI_FW_SEND;
+			if (!w && size)
+				w = usb_bulk_msg(usb, cp, fw, size, NULL, 1024);
+				
+			free_pages((ulong) fw, get_order(fw_data_ptr->size));
+	ret_cleanup_firmware:
+			release_firmware(fw_data_ptr);
+			return w;
+		}
+		w = -ENOMEM;
 	}
-
-	dbg("%s - download successful", __FUNCTION__);
-
-	return 0;
+	w = w ? w : -EFBIG;
+	dev_err(&usb->dev, "too big firmware.\n");
+	goto ret_cleanup_firmware;
+#undef usb
 }
 
 
--- ti_usb_3410_5052.h.orig	2007-09-22 06:52:30.000000000 +0200
+++ ti_usb_3410_5052.h	2007-11-12 10:22:50.000000000 +0100
@@ -1,29 +1,13 @@
-/* vi: ts=8 sw=8
- *
+/*
  * TI 3410/5052 USB Serial Driver Header
- *
- * Copyright (C) 2004 Texas Instruments
- *
- * This driver is based on the Linux io_ti driver, which is
- *   Copyright (C) 2000-2002 Inside Out Networks
- *   Copyright (C) 2001-2002 Greg Kroah-Hartman
- *
- * 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.
- *
- * For questions or problems with this driver, contact Texas Instruments
- * technical support, or Al Borchers <[email protected]>, or
- * Peter Berger <[email protected]>.
  */
 
 #ifndef _TI_3410_5052_H_
 #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
@@ -207,19 +191,17 @@
 #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_SEND			64
+#define TI_FW_MAX			16284
+
+#define ti_fw_3410			"tiusb3410.i51"
+#define ti_fw_5052			"tiusb5052.i51"
+
 #endif /* _TI_3410_5052_H_ */
____

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >> http://get.splunk.com/
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.