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