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/

Reply via email to