The speedtch driver offended me by needing a userspace tool 'modem_run' to load its firmware and -- worse -- to monitor the line state as a dÃmon too.
So I tried to convert it to use request_firmware() and monitor the line for itself. The results are below -- but it doesn't actually work. Sending the firmware boot block times out, whereas when modem_run does the same thing it works fine. Can anyone point out what I've done wrong? (Don't look too hard -- I'll clean it up when it actually _works_) --- Makefile.orig 2004-09-27 21:39:00.301060536 +0100 +++ Makefile 2004-09-27 21:11:37.000000000 +0100 @@ -0,0 +1,10 @@ + +ifndef TOPDIR +LINUXDIR=/lib/modules/`uname -r`/build + +modules clean: + make -C $(LINUXDIR) SUBDIRS=`pwd` $@ EXTRA_CFLAGS=-DDEBUG + +else +obj-m := speedtch.o +endif --- speedtch.c.orig 2004-09-04 00:08:25.000000000 +0100 +++ speedtch.c 2004-09-27 21:41:44.628079032 +0100 @@ -78,6 +78,7 @@ #include <linux/atmdev.h> #include <linux/crc32.h> #include <linux/init.h> +#include <linux/firmware.h> /* #define DEBUG @@ -127,12 +128,31 @@ #define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */ #define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */ +/* Timeout in jiffies */ +#define CTRL_TIMEOUT (2*HZ) +#define DATA_TIMEOUT (2*HZ) + +#define OFFSET_7 0 /* size 1 */ +#define OFFSET_b 1 /* size 8 */ +#define OFFSET_d 9 /* size 4 */ +#define OFFSET_e 13 /* size 1 */ +#define OFFSET_f 14 /* size 1 */ +#define TOTAL 15 + +#define SIZE_7 1 +#define SIZE_b 8 +#define SIZE_d 4 +#define SIZE_e 1 +#define SIZE_f 1 + static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS; static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS; static unsigned int num_rcv_bufs = UDSL_DEFAULT_RCV_BUFS; static unsigned int num_snd_bufs = UDSL_DEFAULT_SND_BUFS; static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE; static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE; +static int dl_512_first = 1; +static int sw_buffering = 0; module_param (num_rcv_urbs, uint, 0444); MODULE_PARM_DESC (num_rcv_urbs, "Number of urbs used for reception (range: 0-" __MODULE_STRING (UDSL_MAX_RCV_URBS) ", default: " __MODULE_STRING (UDSL_DEFAULT_RCV_URBS) ")"); @@ -152,9 +172,20 @@ module_param (snd_buf_size, uint, 0444); MODULE_PARM_DESC (snd_buf_size, "Size of the buffers used for transmission (range: 0-" __MODULE_STRING (UDSL_MAX_SND_BUF_SIZE) ", default: " __MODULE_STRING (UDSL_DEFAULT_SND_BUF_SIZE) ")"); +module_param (dl_512_first, bool, 0444); +MODULE_PARM_DESC (dl_512_first, "Read 512 bytes before sending firmware"); + +module_param (sw_buffering, uint, 0444); +MODULE_PARM_DESC (sw_buffering, "Enable software buffering"); + #define UDSL_IOCTL_LINE_UP 1 #define UDSL_IOCTL_LINE_DOWN 2 +#define UDSL_ENDPOINT_INT 0x81 + +#define UDSL_ENDPOINT_CODE_OUT 0x05 +#define UDSL_ENDPOINT_CODE_IN 0x85 + #define UDSL_ENDPOINT_DATA_OUT 0x07 #define UDSL_ENDPOINT_DATA_IN 0x87 @@ -232,6 +263,13 @@ struct usb_device *usb_dev; char description [64]; int firmware_loaded; + int revision; + + /* Status */ + struct urb *int_urb; + unsigned char int_data[16]; + struct work_struct poll_work; + unsigned char dev_state[TOTAL]; /* ATM device part */ struct atm_dev *atm_dev; @@ -1048,6 +1086,333 @@ return -ENOTTY; } } +#include "stfirmware.c" + +static int udsl_upload_firmware(struct udsl_instance_data *instance, const stusb_firmware_t *stfirmware) +{ + struct usb_device *dev = instance->usb_dev; + unsigned char *buf; + int ret, rlen; + + msleep(5000); + + buf = kmalloc(0x1000, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* URB 7 */ + if (dl_512_first) { + ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, UDSL_ENDPOINT_CODE_IN), + buf, 512, &rlen, DATA_TIMEOUT); + if (ret) { + printk(KERN_WARNING "SpeedTouch: BLOCK0 read failed %d\n", ret); + } else { + dbg("udsl_upload_firmware: BLOCK0 : %6d bytes downloaded OK\n", rlen); + } + } + + /* URB 8 : both leds are static green */ + ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, UDSL_ENDPOINT_CODE_OUT), + (char *)stfirmware->phase1, stfirmware->phase1_length, + &rlen, DATA_TIMEOUT); + if (ret) { + printk(KERN_WARNING "SpeedTouch: BLOCK1 write (boot block) of %ld bytes failed %d\n", + stfirmware->phase1_length, ret); + return ret; + } + /* The userspace modem_run doesn't check rlen. Surely we should though? */ + dbg("udsl_upload_firmware: BLOCK1 : %6d bytes uploaded OK\n", rlen); + + /* + * After usb_bulk_write : + * + USB led : blinking green + * + ADSL led : off + */ + + /* URB 11 */ + ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, UDSL_ENDPOINT_CODE_IN), + buf, 512, &rlen, DATA_TIMEOUT); + if (ret) { + printk(KERN_WARNING "SpeedTouch: BLOCK2 read failed %d\n", ret); + return ret; + } + dbg("udsl_upload_firmware: BLOCK2 : %6d bytes downloaded OK\n", rlen); + + /* URB 12 to 139 */ + ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, UDSL_ENDPOINT_CODE_OUT), + (char *)stfirmware->phase2, stfirmware->phase2_length, + &rlen, DATA_TIMEOUT); + if (ret) { + printk(KERN_WARNING "SpeedTouch: BLOCK3 write (main firmware) failed %d\n", ret); + return ret; + } + /* The userspace modem_run doesn't check rlen. Surely we should though? */ + dbg("udsl_upload_firmware: BLOCK3 : %6d bytes uploaded OK\n", rlen); + + + /* + * Before downloading microcode : + * + USB led : blinking + * + ADSL led : off + * + * During downloading: + * + USB led : blinking green + * + ADSL led : off + * + * After downloading microcode : + * + USB led : static green + * + ADSL led : static red + */ + + /* URB 142 */ + ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, UDSL_ENDPOINT_CODE_IN), + buf, 512, &rlen, DATA_TIMEOUT); + if (ret) { + printk(KERN_WARNING "SpeedTouch: BLOCK3 read failed %d\n", ret); + return ret; + } + dbg("udsl_upload_firmware: BLOCK3 : %6d bytes downloaded OK\n", rlen); + + return 0; +} + +static int udsl_load_firmware (struct udsl_instance_data *instance) +{ + const struct firmware *firmware; + const stusb_firmware_t *stfirmware; + struct usb_device *dev = instance->usb_dev; + int ret; + + ret = request_firmware(&firmware, "speedtouch", &dev->dev); + if (ret < 0) { + printk(KERN_WARNING "Speedtouch firmware request failed %d\n", ret); + return ret; + } + stfirmware = udsl_extract_firmware(firmware, instance->revision == 4); + if (!stfirmware) + return -EIO; + + ret = udsl_upload_firmware(instance, stfirmware); + + release_firmware(firmware); + kfree(stfirmware); + + if (ret) + return ret; + + ret = udsl_set_alternate(instance); + return ret; +} + +static int udsl_set_swbuff (struct udsl_instance_data *instance, int state) +{ + struct usb_device *dev = instance->usb_dev; + int ret; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x32, 0x40, state?0x01:0x00, + 0x00, NULL, 0, 100); + if (ret < 0) { + printk("Warning: %sabling SW buffering: usb_control_msg returned %d\n", + state?"En":"Dis", ret); + return ret; + } + + dbg("udsl_set_swbuff: %sbled SW buffering\n", state?"En":"Dis"); + return 0; +} + +static void udsl_test_sequence(struct udsl_instance_data *instance) +{ + struct usb_device *dev = instance->usb_dev; + unsigned char buf[10]; + int ret; + + /* URB 147 */ + buf[0] = 0x1c; buf[1] = 0x50; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, 0x40, 0x0b, 0x00, buf, 2, 100); + if (ret < 0) + printk(KERN_WARNING "%s failed on URB147: %d\n", __func__, ret); + + /* URB 148 */ + buf[0] = 0x32; buf[1] = 0x00; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, 0x40, 0x02, 0x00, buf, 2, 100); + if (ret < 0) + printk(KERN_WARNING "%s failed on URB148: %d\n", __func__, ret); + + /* URB 149 */ + buf[0] = 0x01; buf[1] = 0x00; buf[2] = 0x01; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, 0x40, 0x03, 0x00, buf, 3, 100); + if (ret < 0) + printk(KERN_WARNING "%s failed on URB149: %d\n", __func__, ret); + + /* URB 150 */ + buf[0] = 0x01; buf[1] = 0x00; buf[2] = 0x01; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, 0x40, 0x04, 0x00, buf, 3, 100); + if (ret < 0) + printk(KERN_WARNING "%s failed on URB150: %d\n", __func__, ret); +} + +static int udsl_start_synchro (struct udsl_instance_data *instance) +{ + struct usb_device *dev = instance->usb_dev; + unsigned char buf[2]; + int ret; + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x04, 0x00, + buf, sizeof(buf), CTRL_TIMEOUT); + if (ret < 0) { + printk(KERN_WARNING "SpeedTouch: Failed to start ADSL synchronisation: %d\n", ret); + return ret; + } + + dbg("udsl_start_synchro: modem prodded. %d Bytes returned: %02x %02x\n", ret, buf[0], buf[1]); + return 0; +} + +static void udsl_handle_int (struct urb *urb, struct pt_regs *regs) +{ + struct udsl_instance_data *instance = urb->context; + unsigned int count = urb->actual_length; + int ret; + + /* The magic interrupt for "up state" */ + const static unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00}; + /* The magic interrupt for "down state" */ + const static unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00}; + + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __func__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __func__, urb->status); + goto exit; + } + + if (count < 6) { + dbg("%s - int packet too short", __func__); + goto exit; + } + + if (!memcmp(up_int, instance->int_data, 6)) { + printk(KERN_NOTICE "DSL line goes up\n"); + } else if (!memcmp(down_int, instance->int_data, 6)) { + printk(KERN_NOTICE "DSL line goes down\n"); + } else { + int i; + + printk(KERN_DEBUG "Unknown interrupt packet of %d bytes:", count); + for (i=0; i < count; i++) + printk(" %02x", instance->int_data[i]); + printk("\n"); + } + schedule_work(&instance->poll_work); + + exit: + ret = usb_submit_urb (urb, GFP_ATOMIC); + if (ret) + err ("%s - usb_submit_urb failed with result %d", + __func__, ret); +} + +static int udsl_get_status(struct udsl_instance_data *instance, unsigned char *buf) +{ + struct usb_device *dev = instance->usb_dev; + int ret; + + memset(buf,0,TOTAL); + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x07, 0x00, buf+OFFSET_7, SIZE_7, CTRL_TIMEOUT); + if (ret<0) + return(ret); + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x0b, 0x00, buf+OFFSET_b, SIZE_b, CTRL_TIMEOUT); + if (ret<0) + return(ret); + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x0d, 0x00, buf+OFFSET_d, SIZE_d, CTRL_TIMEOUT); + if (ret<0) + return(ret); + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x01, 0xc0, 0x0e, 0x00, buf+OFFSET_e, SIZE_e, CTRL_TIMEOUT); + if (ret<0) + return(ret); + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x01, 0xc0, 0x0f, 0x00, buf+OFFSET_f, SIZE_f, CTRL_TIMEOUT); + + return ret; +} + +static void udsl_poll_status(struct udsl_instance_data *instance) +{ + unsigned char buf[TOTAL]; + unsigned char old_linestate; + int down_speed, up_speed; + int ret; + + ret = udsl_get_status(instance, buf); + if (ret) { + printk(KERN_WARNING "SpeedTouch: Error %d fetching device status\n", ret); + return; + } + old_linestate = instance->dev_state[OFFSET_7]; + memcpy(instance->dev_state, buf, TOTAL); + + if (old_linestate != instance->dev_state[OFFSET_7]) { + /* Status changed. Tell the world */ + switch (buf[OFFSET_7]) { + case 0: + printk(KERN_NOTICE "ADSL line is down\n"); + break; + case 0x08: + printk(KERN_NOTICE "ADSL line is blocked?\n"); + break; + case 0x10: + printk(KERN_NOTICE "ADSL line is synchronising\n"); + break; + case 0x20: + down_speed = buf[OFFSET_b] | (buf[OFFSET_b+1]<<8) + | (buf[OFFSET_b+2]<<16) | (buf[OFFSET_b+3]<<24); + up_speed = buf[OFFSET_b+4] | (buf[OFFSET_b+5]<<8) + | (buf[OFFSET_b+6]<<16) | (buf[OFFSET_b+7]<<24); + + if( ((down_speed & 0x0000ffff) == 0) && + ((up_speed & 0x0000ffff) == 0)) { + + down_speed>>=16; + up_speed>>=16; + } + printk(KERN_NOTICE "ADSL line is up (%d kb/s down | %d kb/s up)\n", + down_speed, up_speed); + default: + printk(KERN_NOTICE "Unknown line state %02x\n", buf[OFFSET_7]); + break; + } + if (old_linestate == 0x20 && buf[OFFSET_7] != 0x20) { + instance->atm_dev->signal = ATM_PHY_SIG_LOST; + } else if (old_linestate != 0x20 || buf[OFFSET_7] == 0x20) { + instance->atm_dev->signal = ATM_PHY_SIG_FOUND; + } + } +} static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_id *id) { @@ -1076,6 +1441,26 @@ memset (instance, 0, sizeof (struct udsl_instance_data)); + INIT_WORK(&instance->poll_work, (void *)udsl_poll_status, instance); + instance->dev_state[OFFSET_7] = 0xff; + + switch (dev->descriptor.bcdDevice) { + case 0x0000: + instance->revision = 0; + break; + case 0x0200: + instance->revision = 2; + break; + case 0x0400: + instance->revision = 4; + break; + default: + instance->revision = 2; + printk(KERN_INFO "Unexpected SpeedTouch revision %04x, treating as Rev 0200.\n", + dev->descriptor.bcdDevice); + break; + } + init_MUTEX (&instance->serialize); instance->usb_dev = dev; @@ -1148,6 +1533,50 @@ list_add (&buf->list, &instance->spare_send_buffers); } + /* Hardware init */ + if (udsl_load_firmware(instance)) + goto fail; + + /* Delay to allow firmware to start up */ + msleep(1000); + + if (usb_set_interface(dev, 1, instance->revision?1:2)) { + printk("Set interface 1 failed\n"); + goto fail; + } + if (usb_set_interface(dev, 2, 0)) { + printk("Set interface 2 failed\n"); + goto fail; + } + + /* Enable software buffering, if requested */ + if (sw_buffering) { + udsl_set_swbuff(instance, 1); + } + + /* Set up interrupt endpoint */ + instance->int_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!instance->int_urb) + goto fail; + + usb_fill_int_urb(instance->int_urb, dev, usb_sndintpipe(dev, UDSL_ENDPOINT_INT), + instance->int_data, sizeof(instance->int_data), + udsl_handle_int, instance, 50); + if (usb_submit_urb(instance->int_urb, GFP_KERNEL)) { + dbg ("udsl_usb_probe: Submission of interrupt URB failed\n"); + goto fail; + } + + /* Magic spell; don't ask us what this does */ + udsl_test_sequence(instance); + + /* Start modem synchronisation */ + if (udsl_start_synchro(instance)) + goto fail; + + /* Poll once to display status (presumably down) */ + udsl_poll_status(instance); + /* ATM init */ if (!(instance->atm_dev = atm_dev_register (udsl_driver_name, &udsl_atm_devops, -1, NULL))) { dbg ("udsl_usb_probe: failed to register ATM device!"); @@ -1195,6 +1624,7 @@ instance->atm_dev->dev_data = instance; usb_set_intfdata (intf, instance); + usb_driver_claim_interface (&udsl_usb_driver, usb_ifnum_to_if(dev, 2), instance); return 0; @@ -1211,6 +1641,10 @@ for (i = 0; i < num_rcv_urbs; i++) usb_free_urb (instance->receivers [i].urb); + if (instance->int_urb) { + usb_unlink_urb(instance->int_urb); + usb_free_urb(instance->int_urb); + } kfree (instance); return -ENOMEM; --- stfirmware.c.orig 2004-09-27 21:39:03.869518048 +0100 +++ stfirmware.c 2004-09-27 16:53:12.000000000 +0100 @@ -0,0 +1,390 @@ +/* +* ALCATEL SpeedTouch USB modem microcode extract utility +* Copyright (C) 2001 Benoit PAPILLAULT +* +* 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, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +* Author : Edouard Gomez <[EMAIL PROTECTED]> +* Creation : 14/02/2004 +* +* Searching for the microcode is done in a two step process: +* - the boot code. +* - the main firmware. +* +* The bootcode is always loaded at 0x00000000 in the modem RAM, so we look for +* this base address mixed in a special file format signature. The end of the +* boot code is marked by a command that tells the ARM to jump at the base +* address and execute (aka boot) +* +* The base address of the main firmware depends on the modem revision (as of +* 2004-02-14, only rev4 is different). The end pattern is also a jump command. +* +* Based on: +* $Id: firmware.c,v 1.4 2004/02/17 21:45:17 edgomez Exp $ +*/ + +typedef struct +{ + unsigned long crc; + unsigned long length; + const char *id; +} stusb_firmware_id_t; + + +typedef struct +{ + const unsigned char *phase1; + const unsigned char *phase2; + unsigned long phase1_length; + unsigned long phase2_length; +} stusb_firmware_t; + +static int search_pattern(const unsigned char *buffer, const unsigned char *pattern, + const int buffer_length, const int pattern_length); + +static const unsigned char * +extract_firmware_phase(const unsigned char *buf, + const int length, + const unsigned char *start_pattern, + const unsigned char *stop_pattern, + const int pattern_length, + const stusb_firmware_id_t *ids, + unsigned long *rlen); + +/****************************************************************************** +* Main Lib Function +******************************************************************************/ + +/* + * Function : check_firmware + * Results : return 1 if the firmware is OK, 0 otherwise. + * Description : check that the firmware found is valid + */ + +static int check_firmware(const unsigned char *buf, int len) +{ + const unsigned char * current_block; + int remaining_len; + + unsigned int addr; + unsigned int size, extra_size; + + int boot_block_seen = 0; + int last_block_seen = 0; + + current_block = buf; + remaining_len = len; + + while (remaining_len > 0) { + if (remaining_len < 8) { + /* not enougth data to make a block, abort */ + return(0); + } + + addr = current_block[2] | (current_block[3]<<8) | + (current_block[4]<<16) | (current_block[5]<<24); + + size = current_block[6] | (current_block[7]<<8); + extra_size = current_block[1] | ((current_block[0]&0x80)<<1); + + switch (current_block[0]) { + case 0x08: + case 0x88: + /* determine the type of block */ + if (extra_size <= 0x1f8 && extra_size > 6) { + /* last data block & last data block */ + if (boot_block_seen || last_block_seen) + return 0; + + if (extra_size < 0x1f8) + last_block_seen = 1; + + if (extra_size != size + 6) + return 0; + + /* skip over header */ + current_block += 2; + remaining_len -= 2; + + if (remaining_len < extra_size) + return(0); + + /* skip over data */ + current_block += extra_size; + remaining_len -= extra_size; + + if (remaining_len < 3) + return(0); + + if (current_block[0] != 0x40 || + current_block[1] != 0x01 || + current_block[2] != 0x12) + return(0); + + /* skip over trailer */ + current_block += 3; + remaining_len -= 3; + } else if (extra_size == 4) { + /* boot block */ + if (boot_block_seen) + return(0); + + boot_block_seen = 1; + + /* skip over header */ + current_block += 2; + remaining_len -= 2; + + if (remaining_len < extra_size) + return(0); + + /* skip over data */ + current_block += extra_size; + remaining_len -= extra_size; + + if (remaining_len < 3) + return(0); + + if (current_block[0] != 0x00 || + current_block[1] != 0x01 || + current_block[2] != 0x14) + return(0); + + /* skip over trailer */ + current_block += 3; + remaining_len -= 3; + } else { + /* unknown block again, abort */ + return(0); + } + + break; + default: + /* unknown block, abort */ + return(0); + } + } + + return(1); +} + +/* +* Function : extract_microcode +* Return Value : NULL on error +* valid pointer to the microcode on success +* Description : We are searching for the best match of a start and end +* sequence. +* Those sequence delimits the microcode. You won't get 100% +* probability, but it will work anyway, for all the microcode +* I've tested. +*/ + +#define PATTERN_LENGTH 8 + +static stusb_firmware_t *udsl_extract_firmware(const struct firmware *fw, int rev4) +{ + /* Specific byte sequences that mark the start/end of the encapsulated ARM code in firmware files */ + static const unsigned char boot_patterns[2][PATTERN_LENGTH] = { + /* The base address is always 0x00000000 */ + {0x88, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x01}, /* start */ + {0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x14} /* stop */ + }; + + static const unsigned char firm_patterns[2][2][PATTERN_LENGTH] = { + { /* The base address is 0x00400000 */ + {0x88, 0xf8, 0x00, 0x40, 0x00, 0x00, 0xf2, 0x01}, /* start for stusb rev 1,2,3 */ + {0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x14} /* stop for stusb rev 1,2,3 */ + }, { /* The base address is 0x00000010 */ + {0x88, 0xf8, 0x00, 0x00, 0x00, 0x10, 0xf2, 0x01}, /* start for stusb rev 4 */ + {0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x14} /* stop for stusb rev 4 */ + } + }; + + static const stusb_firmware_id_t stusb_phase1_ids[] = { + {0xd3e33990, 883, "Ian's Free Software Boot block"}, + {0xd80bf9f7, 991, "Alcatel/Thomson Boot block (old)"}, + {0x69636579, 935, "Alcatel/Thomson Boot block (new)"}, + {0x00000000, 0, NULL} + }; + + static const stusb_firmware_id_t stusb_phase2_ids[] = { + {0xae3ff81f, 526239, "1.3.1 - GNU/Linux"}, + {0xa719bc0e, 523436, "1.3.1 - Win32"}, + {0x94a45435, 526187, "1.3.3 - GNU/Linux - Win32"}, + {0x61914198, 527093, "1.4.0 - Win32(3.0.1800)"}, + {0x37c189ed, 527752, "1.4.0 - Win32(4.0.100)"}, + {0x99cc1c1a, 528738, "1.4.2 - Win32(2.0.500)"}, + {0xe0251a5e, 671665, "1.6.1 - Win32(5.0.1801)"}, + {0x3b4a5854, 671653, "1.6.1 - MacOSX - Win32(1.0.1800)"}, + {0xd673923f, 672264, "2.0.0 - Win32(07)"}, + {0x5bca7d16, 677641, "2.0.1 - MacOSX - Win32(2.0.0)"}, + {0x78039fed, 762650, "3.0.6 - MacOSX - Win32"}, + {0x698eb734, 761389, "3.0.0 - Win32"}, + {0xd7864c39, 774192, "3.0.0 - Win32 (rev 4)"}, + {0x0223733c, 775509, "1.0.10 - Win32 Rev 0400 SACHU3"}, + {0x41d4143c, 775545, "0.0.0 - testing firmware from Thomson"}, + {0x00000000, 0, NULL} + }; + + stusb_firmware_t *result; + + result = kmalloc(sizeof *result, GFP_KERNEL); + if (!result) + return NULL; + + /* Extract the phase1 (boot block) part */ + result->phase1 = extract_firmware_phase(fw->data, fw->size, + boot_patterns[0], + boot_patterns[1], + PATTERN_LENGTH, + stusb_phase1_ids, + &result->phase1_length); + if (!result->phase1) { + printk(KERN_WARNING "No boot block found\n"); + kfree(result); + return NULL; + } + + + /* Extract the modem's firmware now */ + result->phase2 = extract_firmware_phase(fw->data, fw->size, + firm_patterns[(rev4)?1:0][0], + firm_patterns[(rev4)?1:0][1], + PATTERN_LENGTH, + stusb_phase2_ids, + &result->phase2_length); + if (!result->phase2) { + printk(KERN_WARNING "No main firmware image (rev 4: %d) found\n", rev4); + kfree(result); + return NULL; + } + + return result; +} + +static const unsigned char * +extract_firmware_phase(const unsigned char *buf, + int length, + const unsigned char *start_pattern, + const unsigned char *stop_pattern, + const int pattern_length, + const stusb_firmware_id_t *ids, + unsigned long *rlen) +{ + int start_offset; + const char *start_ptr; + + /* Searches the start and end offsets */ + start_offset = search_pattern(buf, + start_pattern, + length, + pattern_length); + if (start_offset < 0) + return(NULL); + start_ptr = buf + start_offset; + + length = search_pattern(start_ptr, + stop_pattern, + length - start_offset, + pattern_length); + if (length < 0) + return(NULL); + + length += PATTERN_LENGTH; + + { + unsigned long crc; + const stusb_firmware_id_t *id = ids; + const char *idstr = + "Unknown revision - Please report the CRC " + "and length with the revision number to " + "[EMAIL PROTECTED]"; + int checked; + + /* Computes this value once */ + crc = ~crc32_be(~0, start_ptr, length); + + /* Browse the "known" firmware array */ + while (id->length != 0) { + if(id->length == length && id->crc == crc) { + idstr = id->id; + break; + } + id++; + } + + checked = check_firmware(start_ptr, length); + + printk(KERN_NOTICE "Firmware info (CRC:0x%08lx, Size:%d, Checked: %s, %s)\n", + crc, length, checked?"Yes":"No",idstr); + } + + /* Returns a pointer to the start address */ + *rlen = length; + return start_ptr; +} + + +/* +* Function : search_match +* Return value : -1 in case of error +* best match offset on success +* Description : Search for the longest match of buf2 inside buf1. +* +*/ +static int +search_pattern(const unsigned char *buffer, + const unsigned char *pattern, + const int buffer_length, + const int pattern_length) +{ + /* Number of bytes match with pattern */ + int best_match = 0; + + /* Best offset*/ + int best_offset = -1; + + /* Zone where the pattern can reside */ + int potential_length = (buffer_length - pattern_length + 1); + + /* Loop counter */ + int cur_offset; + + for(cur_offset=0; cur_offset< potential_length; cur_offset++) { + int i; + int cur_match; + + /* Compute the matching number of bytes at this offset */ + for(i=0, cur_match=0;i<pattern_length;i++) + if (buffer[cur_offset+i] == pattern[i]) + cur_match++; + + /* Compare to the best known matching offset */ + if(cur_match > best_match) { + best_offset = cur_offset; + best_match = cur_match; + + /* Stops as soon as a perfect match is found */ + if (best_match == pattern_length) + break; + } + } + + dbg("Best offset %6d with probability %3d%%\n", + best_offset, + (100*best_match)/pattern_length); + + return((best_match == pattern_length)?best_offset:-1); +} -- dwmw2 ------------------------------------------------------- This SF.Net email is sponsored by: YOU BE THE JUDGE. Be one of 170 Project Admins to receive an Apple iPod Mini FREE for your judgement on who ports your project to Linux PPC the best. Sponsored by IBM. Deadline: Sept. 24. Go here: http://sf.net/ppc_contest.php _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel