Adding firmware_load_ihex, to load IHEX formatted firmware images. Signed-off-by: Jan Harkes <[EMAIL PROTECTED]>
drivers/base/firmware_class.c | 151 ++++++++++++++++++++++++++++++++++++++++++ include/linux/firmware.h | 26 +++++++ 2 files changed, 177 insertions(+) Index: linux/include/linux/firmware.h =================================================================== --- linux.orig/include/linux/firmware.h 2005-03-29 16:32:45.000000000 -0500 +++ linux/include/linux/firmware.h 2005-03-29 16:33:11.000000000 -0500 @@ -1,12 +1,34 @@ +/* + * Definitions for hotplug firmware loader + * + * Copyright (C) 2003 Manuel Estrada Sainz <[EMAIL PROTECTED]> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 2005-03-10 Jan Harkes <[EMAIL PROTECTED]> + * Added parser for IHEX formatted firmware files. + */ + #ifndef _LINUX_FIRMWARE_H #define _LINUX_FIRMWARE_H + #include <linux/module.h> #include <linux/types.h> #define FIRMWARE_NAME_MAX 30 + struct firmware { size_t size; u8 *data; }; + +struct ihex_record { + __u32 address; + __u8 len; + __u8 data[255]; +}; + struct device; int request_firmware(const struct firmware **fw, const char *name, struct device *device); @@ -15,6 +37,10 @@ int request_firmware_nowait( const char *name, struct device *device, void *context, void (*cont)(const struct firmware *fw, void *context)); +int firmware_load_ihex(const struct firmware *fw, void *context, + int (*load)(struct ihex_record *record, void *context)); + void release_firmware(const struct firmware *fw); void register_firmware(const char *name, const u8 *data, size_t size); + #endif Index: linux/drivers/base/firmware_class.c =================================================================== --- linux.orig/drivers/base/firmware_class.c 2005-03-29 16:32:45.000000000 -0500 +++ linux/drivers/base/firmware_class.c 2005-03-29 16:33:11.000000000 -0500 @@ -5,6 +5,8 @@ * * Please see Documentation/firmware_class/ for more information. * + * 2005-03-10 Jan Harkes <[EMAIL PROTECTED]> + * Added parser for IHEX formatted firmware files. */ #include <linux/device.h> @@ -553,6 +555,153 @@ request_firmware_nowait( return 0; } +#ifdef VERBOSE_MSGS +#define dbg(msg, ...) printk(KERN_WARNING "%s: line %d: " msg, __FUNCTION__, line, ## __VA_ARGS__) +#else +#define dbg(msg, ...) do {} while(0) +#endif + +/** + * nibble/hex are little helpers to parse hexadecimal numbers to a byte value + **/ +static __u8 nibble(__u8 n) +{ + if (n >= '0' && n <= '9') return n - '0'; + else if (n >= 'A' && n <= 'F') return n - ('A' - 10); + else if (n >= 'a' && n <= 'f') return n - ('a' - 10); + return 0; +} +static __u8 hex(__u8 *data, __u8 *crc) +{ + __u8 val = (nibble(data[0]) << 4) | nibble(data[1]); + *crc += val; + return val; +} + +/** + * firmware_load_ihex: + * + * Description: + * @fw is expected to contain Intel HEX formatted data. + * + * @load will be called for each record that is found in the IHEX data. + * + * @context will be passed on to @load. + * + **/ +int firmware_load_ihex(const struct firmware *fw, void *context, + int (*load)(struct ihex_record *record, void *context)) +{ + struct ihex_record *record; + __u32 offset = 0; + __u8 type, crc = 0; + int i, j, err = 0; + int line = 1; + + record = kmalloc(sizeof(*record), GFP_KERNEL); + if (!record) { + dbg("Allocation failed\n"); + return -ENOMEM; + } + + i = 0; +next_record: + /* search for the start of record character */ + while (i < fw->size) { + if (fw->data[i] == '\n') line++; + if (fw->data[i++] == ':') break; + } + + /* Minimum record length would be about 10 characters */ + if (i + 10 > fw->size) { + dbg("Can't find valid record\n"); + err = -EINVAL; + goto done; + } + + record->len = hex(fw->data + i, &crc); + i += 2; + + /* now check if we have enough data to read everything */ + if (i + 8 + (record->len * 2) > fw->size) { + dbg("Not enough data to read complete record\n"); + err = -EINVAL; + goto done; + } + + record->address = hex(fw->data + i, &crc) << 8; i += 2; + record->address |= hex(fw->data + i, &crc); i += 2; + record->address += offset; + type = hex(fw->data + i, &crc); i += 2; + + for (j = 0; j < record->len; j++, i += 2) + record->data[j] = hex(fw->data + i, &crc); + + /* check CRC */ + (void)hex(fw->data + i, &crc); i += 2; + if (crc != 0) { + dbg("CRC failure\n"); + err = -EINVAL; + goto done; + } + + /* Done reading the record */ + switch (type) { + case 0: + /* old style EOF record? */ + if (!record->len) + break; + + err = load(record, context); + if (err < 0) { + dbg("Firmware load failed (err %d)\n", err); + break; + } + goto next_record; + + case 1: /* End-Of-File Record */ + if (record->address || record->len) { + dbg("Bad EOF record (type 01) format\n"); + err = -EINVAL; + } + break; + + case 2: /* Extended Segment Address Record (HEX86) */ + case 4: /* Extended Linear Address Record (HEX386) */ + if (record->address || record->len != 2) { + dbg("Bad HEX86/HEX386 record (type %02X)\n", type); + err = -EINVAL; + break; + } + + /* We shouldn't really be using the offset for HEX86 because + * the wraparound case is specified quite differently. */ + offset = record->data[0] << 8 | record->data[1]; + offset <<= (type == 2 ? 4 : 16); + goto next_record; + + case 3: /* Start Segment Address Record */ + case 5: /* Start Linear Address Record */ + if (record->address || record->len != 4) { + dbg("Bad Start Address record (type %02X)\n", type); + err = -EINVAL; + break; + } + + /* These records contain the CS/IP or EIP where execution + * starts. Don't really know what to do with them. */ + goto next_record; + + default: + dbg("Unknown record (type %02X)\n", type); + err = -EINVAL; + break; /* unknown record type */ + } +done: + kfree(record); + return err; +} + static int __init firmware_class_init(void) { @@ -584,3 +733,5 @@ EXPORT_SYMBOL(release_firmware); EXPORT_SYMBOL(request_firmware); EXPORT_SYMBOL(request_firmware_nowait); EXPORT_SYMBOL(register_firmware); +EXPORT_SYMBOL(firmware_load_ihex); + - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/