[PATCH net-next 4/4] r8152: support firmware files

From: Hayes Wang
Date: Wed Aug 20 2014 - 05:00:01 EST


The firmware file is composed of the fw header and the commands. Each
command has the following type.

cmd(2 bytes) + length(2 bytes) + data(variable bytes)

Before applying the firmware, the driver would check the fw header and
each command.

Signed-off-by: Hayes Wang <hayeswang@xxxxxxxxxxx>
---
drivers/net/usb/r8152.c | 867 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 866 insertions(+), 1 deletion(-)

diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 937d132..63542cc 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -21,10 +21,11 @@
#include <linux/list.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
+#include <linux/firmware.h>
#include <net/ip6_checksum.h>

/* Version Information */
-#define DRIVER_VERSION "v1.06.0 (2014/03/03)"
+#define DRIVER_VERSION "v1.07.0 (2014/08/20)"
#define DRIVER_AUTHOR "Realtek linux nic maintainers <nic_swsd@xxxxxxxxxxx>"
#define DRIVER_DESC "Realtek RTL8152/RTL8153 Based USB Ethernet Adapters"
#define MODULENAME "r8152"
@@ -577,6 +578,16 @@ struct r8152 {
void (*unload)(struct r8152 *);
} rtl_ops;

+ struct rtl_fw {
+ const struct firmware *fw;
+
+#define RTL_VER_SIZE 32
+
+ char version[RTL_VER_SIZE];
+ u8 *code;
+ size_t code_size;
+ } rtl_fw;
+
int intr_interval;
u32 saved_wolopts;
u32 msg_enable;
@@ -1321,6 +1332,852 @@ err1:
return -ENOMEM;
}

+#define FW_SIGNATURE 0x0bda8152
+
+enum fw_cmd {
+ FW_CMD_INVALID = 0,
+
+ FW_CMD_GENERIC_WRITE,
+ FW_CMD_WRITE_BYTE,
+ FW_CMD_WRITE_WORD,
+ FW_CMD_WRITE_DWORD,
+ FW_CMD_READ_BYTE,
+ FW_CMD_READ_WORD,
+ FW_CMD_READ_DWORD,
+ FW_CMD_W0W1_BYTE,
+ FW_CMD_W0W1_WORD,
+ FW_CMD_W0W1_DWORD,
+ FW_CMD_W0W1_CURRENT,
+ FW_CMD_WRITE_CURRENT_BYTE,
+ FW_CMD_WRITE_CURRENT_WORD,
+ FW_CMD_WRITE_CURRENT_DWORD,
+ FW_CMD_CMP,
+ FW_CMD_JMP,
+ FW_CMD_JE,
+ FW_CMD_JNE,
+ FW_CMD_JA,
+ FW_CMD_JAE,
+ FW_CMD_JB,
+ FW_CMD_JBE,
+ FW_CMD_CX,
+ FW_CMD_LOOP,
+ FW_CMD_LOOPE,
+ FW_CMD_LOOPNE,
+ FW_CMD_USLEEP,
+
+ FW_CMD_END,
+ FW_CMD_MAX
+};
+
+struct fw_cmd_generic {
+ __le16 cmd;
+ __le16 length;
+} __packed;
+
+struct fw_cmd_most_used {
+ __le16 type;
+ __le16 addr;
+} __packed;
+
+struct fw_header {
+ __le32 signature;
+ char version[RTL_VER_SIZE];
+ __le32 fw_start;
+ __le32 fw_len;
+} __packed;
+
+static bool rtl_fw_format_ok(struct rtl_fw *rtl_fw)
+{
+ const struct firmware *fw = rtl_fw->fw;
+ struct fw_header *fw_header = (struct fw_header *)fw->data;
+ char *version = rtl_fw->version;
+ size_t i, size, start;
+ u8 checksum = 0;
+ bool rc = false;
+
+ if (fw->size < sizeof(*fw_header))
+ goto out;
+
+ if (__le32_to_cpu(fw_header->signature) != FW_SIGNATURE)
+ goto out;
+
+ start = le32_to_cpu(fw_header->fw_start);
+ if (start > fw->size)
+ goto out;
+
+ size = le32_to_cpu(fw_header->fw_len);
+ if (size > (fw->size - start))
+ goto out;
+
+ for (i = 0; i < fw->size; i++)
+ checksum += fw->data[i];
+ if (checksum != 0)
+ goto out;
+
+ memcpy(version, fw_header->version, RTL_VER_SIZE);
+
+ rtl_fw->code = (u8 *)(fw->data + start);
+ rtl_fw->code_size = size;
+
+ version[RTL_VER_SIZE - 1] = 0;
+
+ rc = true;
+out:
+ return rc;
+}
+
+static void rtl_fw_get_info2(u8 *d2, u16 *ptype, u16 *paddr)
+{
+ struct fw_cmd_most_used info2;
+
+ memcpy(&info2, d2, sizeof(info2));
+ *ptype = __le16_to_cpu(info2.type);
+ *paddr = __le16_to_cpu(info2.addr);
+}
+
+static bool rtl_fw_data_ok(u8 *d, size_t total)
+{
+ u16 cmd, len, type, addr, byteen, size, cx = 0;
+ struct fw_cmd_generic op;
+ bool result = false;
+ __le16 le16_data;
+ __le32 le32_data;
+ size_t i = 0, j;
+
+ while (i < total) {
+ if (i + sizeof(op) > total)
+ goto result_return;
+
+ memcpy(&op, &d[i], sizeof(op));
+ cmd = __le16_to_cpu(op.cmd);
+ len = __le16_to_cpu(op.length);
+ j = i + sizeof(op);
+
+ switch (cmd) {
+ case FW_CMD_GENERIC_WRITE:
+ /* struct fw_cmd_generic_write {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * __le16 data_length;
+ * };
+ */
+
+ if ((j + sizeof(struct fw_cmd_most_used)) > total)
+ goto result_return;
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+ j += sizeof(struct fw_cmd_most_used);
+
+ /* addr size must be the multiple of 4 */
+ if (addr & 3)
+ goto result_return;
+
+ byteen = type & 0xff;
+ type = type & ~0xff;
+
+ memcpy(&le16_data, &d[j], sizeof(le16_data));
+ j += sizeof(le16_data);
+ size = __le16_to_cpu(le16_data);
+
+ /* data size must be the multiple of 4 */
+ if (size & 3)
+ goto result_return;
+
+ size += sizeof(struct fw_cmd_most_used) + sizeof(size);
+
+ break;
+
+ case FW_CMD_WRITE_BYTE:
+ /* struct fw_cmd_generic_write {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * u8 data;
+ * };
+ */
+
+ if ((j + sizeof(struct fw_cmd_most_used)) > total)
+ goto result_return;
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+
+ if (type & 0xff)
+ goto result_return;
+
+ size = sizeof(struct fw_cmd_most_used) + 1;
+ break;
+
+ case FW_CMD_WRITE_WORD:
+ /* struct fw_cmd_ocp_write_word {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * __le16 data;
+ * };
+ */
+
+ if ((j + sizeof(struct fw_cmd_most_used)) > total)
+ goto result_return;
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+
+ if ((type & 0xff) || (addr & 1))
+ goto result_return;
+
+ size = sizeof(struct fw_cmd_most_used) +
+ sizeof(le16_data);
+ break;
+
+ case FW_CMD_WRITE_DWORD:
+ /* struct fw_cmd_ocp_write_dword {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * __le32 data;
+ * };
+ */
+
+ if ((j + sizeof(struct fw_cmd_most_used)) > total)
+ goto result_return;
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+
+ if ((type & 0xff) || (addr & 3))
+ goto result_return;
+
+ size = sizeof(struct fw_cmd_most_used) +
+ sizeof(le32_data);
+ break;
+
+ case FW_CMD_READ_BYTE:
+ case FW_CMD_WRITE_CURRENT_BYTE:
+ /* struct fw_cmd_write_current {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * };
+ */
+
+ if ((j + sizeof(struct fw_cmd_most_used)) > total)
+ goto result_return;
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+
+ if (type & 0xff)
+ goto result_return;
+
+ size = sizeof(struct fw_cmd_most_used);
+ break;
+
+ case FW_CMD_READ_WORD:
+ case FW_CMD_WRITE_CURRENT_WORD:
+ /* struct fw_cmd_write_current {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * };
+ */
+
+ if ((j + sizeof(struct fw_cmd_most_used)) > total)
+ goto result_return;
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+
+ if ((type & 0xff) || (addr & 1))
+ goto result_return;
+
+ size = sizeof(struct fw_cmd_most_used);
+ break;
+
+ case FW_CMD_READ_DWORD:
+ case FW_CMD_WRITE_CURRENT_DWORD:
+ /* struct fw_cmd_write_current {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * };
+ */
+
+ if ((j + sizeof(struct fw_cmd_most_used)) > total)
+ goto result_return;
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+
+ if ((type & 0xff) || (addr & 3))
+ goto result_return;
+
+ size = sizeof(struct fw_cmd_most_used);
+ break;
+
+ case FW_CMD_W0W1_BYTE:
+ /* struct fw_cmd_ocp_w0w1_byte {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * u8 w0;
+ * u8 w1;
+ * };
+ */
+
+ if ((j + sizeof(struct fw_cmd_most_used)) > total)
+ goto result_return;
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+
+ if (type & 0xff)
+ goto result_return;
+
+ size = sizeof(struct fw_cmd_most_used) + 2;
+ break;
+
+ case FW_CMD_W0W1_WORD:
+ /* struct fw_cmd_ocp_w0w1_word {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * __le16 w0;
+ * __le16 w1;
+ * };
+ */
+
+ if ((j + sizeof(struct fw_cmd_most_used)) > total)
+ goto result_return;
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+
+ if ((type & 0xff) || (addr & 1))
+ goto result_return;
+
+ size = sizeof(struct fw_cmd_most_used) +
+ sizeof(le16_data) * 2;
+ break;
+
+ case FW_CMD_W0W1_DWORD:
+ /* struct fw_cmd_ocp_w0w1_dword {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * __le32 w0;
+ * __le32 w1;
+ * };
+ */
+
+ if ((j + sizeof(struct fw_cmd_most_used)) > total)
+ goto result_return;
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+
+ if ((type & 0xff) || (addr & 3))
+ goto result_return;
+
+ size = sizeof(struct fw_cmd_most_used) +
+ sizeof(le32_data) * 2;
+ break;
+
+ case FW_CMD_CMP:
+ /* struct fw_cmd_compare {
+ * struct fw_cmd_generic op;
+ * __le32 data;
+ * };
+ */
+
+ size = sizeof(le32_data);
+ break;
+
+ case FW_CMD_JMP:
+ case FW_CMD_JE:
+ case FW_CMD_JNE:
+ case FW_CMD_JA:
+ case FW_CMD_JAE:
+ case FW_CMD_JB:
+ case FW_CMD_JBE:
+ case FW_CMD_LOOP:
+ case FW_CMD_LOOPE:
+ case FW_CMD_LOOPNE:
+ /* struct fw_cmd_jump {
+ * struct fw_cmd_generic op;
+ * __le16 offset;
+ * };
+ */
+
+ if (j + sizeof(le16_data) > total)
+ goto result_return;
+
+ memcpy(&le16_data, &d[j], sizeof(le16_data));
+ j = (short)__le16_to_cpu(le16_data);
+
+ j += sizeof(op) + len + i;
+ if (j < 0 || j > total)
+ goto result_return;
+
+ size = sizeof(le16_data);
+ break;
+
+ case FW_CMD_CX:
+ case FW_CMD_USLEEP:
+ /* struct fw_cmd_cx {
+ * struct fw_cmd_generic op;
+ * __le16 cx;
+ * };
+ */
+
+ if (j + sizeof(le16_data) > total)
+ goto result_return;
+
+ memcpy(&le16_data, &d[j], sizeof(le16_data));
+ cx = __le16_to_cpu(le16_data);
+ if (cx == 0)
+ goto result_return;
+
+ size = sizeof(le16_data);
+ break;
+
+ case FW_CMD_W0W1_CURRENT:
+ /* struct fw_cmd_ocp_w0w1_current {
+ * struct fw_cmd_generic op;
+ * __le32 w0;
+ * __le32 w1;
+ * };
+ */
+ size = sizeof(le32_data) * 2;
+ break;
+
+ case FW_CMD_END:
+ size = 0;
+ goto result_return;
+
+ default:
+ goto result_return;
+ }
+
+ if (len != size)
+ goto result_return;
+
+ i += sizeof(op) + len;
+ }
+
+ if (i <= total)
+ result = true;
+
+result_return:
+ return result;
+}
+
+static bool rtl_check_firmware(struct r8152 *tp, struct rtl_fw *fw)
+{
+ bool fw_ok = false;
+
+ if (!rtl_fw_format_ok(&tp->rtl_fw))
+ goto out;
+
+ if (rtl_fw_data_ok(tp->rtl_fw.code, tp->rtl_fw.code_size))
+ fw_ok = true;
+
+out:
+ return fw_ok;
+}
+
+static void rtl_fw_write(struct r8152 *tp, u8 *d, size_t total)
+{
+ u16 cmd, len, type, addr, byteen, size, cx = 0, us;
+ u32 ocp_data = 0, compare = 0;
+ struct fw_cmd_generic op;
+ __le16 le16_data;
+ __le32 le32_data;
+ size_t i = 0, j;
+
+ while (i < total) {
+ memcpy(&op, &d[i], sizeof(op));
+ cmd = __le16_to_cpu(op.cmd);
+ len = __le16_to_cpu(op.length);
+ j = i + sizeof(op);
+
+ switch (cmd) {
+ case FW_CMD_GENERIC_WRITE:
+ /* struct fw_cmd_generic_write {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * __le16 data_length;
+ * };
+ */
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+ j += sizeof(struct fw_cmd_most_used);
+
+ byteen = type & 0xff;
+ type = type & ~0xff;
+
+ memcpy(&le16_data, &d[j], sizeof(le16_data));
+ j += sizeof(le16_data);
+ size = __le16_to_cpu(le16_data);
+
+ generic_ocp_write(tp, addr, byteen, size, &d[j], type);
+ break;
+
+ case FW_CMD_WRITE_BYTE:
+ /* struct fw_cmd_generic_write {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * u8 data;
+ * };
+ */
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+ j += sizeof(struct fw_cmd_most_used);
+
+ ocp_data = d[j];
+ ocp_write_byte(tp, type, addr, ocp_data);
+ break;
+
+ case FW_CMD_WRITE_WORD:
+ /* struct fw_cmd_ocp_write_word {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * __le16 data;
+ * };
+ */
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+ j += sizeof(struct fw_cmd_most_used);
+
+ memcpy(&le16_data, &d[j], sizeof(le16_data));
+ ocp_data = __le16_to_cpu(le16_data);
+
+ ocp_write_word(tp, type, addr, ocp_data);
+ break;
+
+ case FW_CMD_WRITE_DWORD:
+ /* struct fw_cmd_ocp_write_dword {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * __le32 data;
+ * };
+ */
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+ j += sizeof(struct fw_cmd_most_used);
+
+ memcpy(&le32_data, &d[j], sizeof(le32_data));
+ ocp_data = __le32_to_cpu(le32_data);
+
+ ocp_write_dword(tp, type, addr, ocp_data);
+ break;
+
+ case FW_CMD_READ_BYTE:
+ /* struct fw_cmd_ocp_read {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * };
+ */
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+ ocp_data = ocp_read_byte(tp, type, addr);
+ break;
+
+ case FW_CMD_READ_WORD:
+ /* struct fw_cmd_ocp_read {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * };
+ */
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+ ocp_data = ocp_read_word(tp, type, addr);
+ break;
+
+ case FW_CMD_READ_DWORD:
+ /* struct fw_cmd_ocp_read {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * };
+ */
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+ ocp_data = ocp_read_dword(tp, type, addr);
+ break;
+
+ case FW_CMD_W0W1_BYTE:
+ /* struct fw_cmd_ocp_w0w1_byte {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * u8 w0;
+ * u8 w1;
+ * };
+ */
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+ j += sizeof(struct fw_cmd_most_used);
+
+ ocp_data = ocp_read_byte(tp, type, addr);
+ ocp_data &= ~d[j++];
+ ocp_data |= d[j++];
+ ocp_write_byte(tp, type, addr, ocp_data);
+ break;
+
+ case FW_CMD_W0W1_WORD:
+ /* struct fw_cmd_ocp_w0w1_word {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * __le16 w0;
+ * __le16 w1;
+ * };
+ */
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+ j += sizeof(struct fw_cmd_most_used);
+
+ ocp_data = ocp_read_word(tp, type, addr);
+
+ memcpy(&le16_data, &d[j], sizeof(le16_data));
+ j += sizeof(le16_data);
+ ocp_data &= ~__le16_to_cpu(le16_data);
+
+ memcpy(&le16_data, &d[j], sizeof(le16_data));
+ j += sizeof(le16_data);
+ ocp_data |= __le16_to_cpu(le16_data);
+
+ ocp_write_word(tp, type, addr, ocp_data);
+ break;
+
+ case FW_CMD_W0W1_DWORD:
+ /* struct fw_cmd_ocp_w0w1_dword {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * __le32 w0;
+ * __le32 w1;
+ * };
+ */
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+ j += sizeof(struct fw_cmd_most_used);
+
+ ocp_data = ocp_read_dword(tp, type, addr);
+
+ memcpy(&le32_data, &d[j], sizeof(le32_data));
+ j += sizeof(le32_data);
+ ocp_data &= ~__le32_to_cpu(le32_data);
+
+ memcpy(&le32_data, &d[j], sizeof(le32_data));
+ j += sizeof(le32_data);
+ ocp_data |= __le32_to_cpu(le32_data);
+
+ ocp_write_dword(tp, type, addr, ocp_data);
+ break;
+
+ case FW_CMD_W0W1_CURRENT:
+ /* struct fw_cmd_ocp_w0w1_current {
+ * struct fw_cmd_generic op;
+ * __le32 w0;
+ * __le32 w1;
+ * };
+ */
+
+ memcpy(&le32_data, &d[j], sizeof(le32_data));
+ j += sizeof(le32_data);
+ ocp_data &= ~__le32_to_cpu(le32_data);
+
+ memcpy(&le32_data, &d[j], sizeof(le32_data));
+ j += sizeof(le32_data);
+ ocp_data |= __le32_to_cpu(le32_data);
+ break;
+
+ case FW_CMD_WRITE_CURRENT_BYTE:
+ /* struct fw_cmd_write_current {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * };
+ */
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+ ocp_write_byte(tp, type, addr, ocp_data);
+ break;
+
+ case FW_CMD_WRITE_CURRENT_WORD:
+ /* struct fw_cmd_write_current {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * };
+ */
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+ ocp_write_word(tp, type, addr, ocp_data);
+ break;
+
+ case FW_CMD_WRITE_CURRENT_DWORD:
+ /* struct fw_cmd_write_current {
+ * struct fw_cmd_generic op;
+ * struct fw_cmd_most_used info2;
+ * };
+ */
+
+ rtl_fw_get_info2(&d[j], &type, &addr);
+ ocp_write_dword(tp, type, addr, ocp_data);
+ break;
+
+ case FW_CMD_CMP:
+ /* struct fw_cmd_compare {
+ * struct fw_cmd_generic op;
+ * __le32 data;
+ * };
+ */
+
+ memcpy(&le32_data, &d[j], sizeof(le32_data));
+ compare = __le32_to_cpu(le32_data);
+ break;
+
+ case FW_CMD_JMP:
+ /* struct fw_cmd_jump {
+ * struct fw_cmd_generic op;
+ * __le16 offset;
+ * };
+ */
+do_jump:
+ memcpy(&le16_data, &d[j], sizeof(le16_data));
+ j = (short)__le16_to_cpu(le16_data);
+
+ i += sizeof(op) + len + j;
+ continue;
+
+ case FW_CMD_JE:
+ if (ocp_data == compare)
+ goto do_jump;
+
+ break;
+
+ case FW_CMD_JNE:
+ if (ocp_data != compare)
+ goto do_jump;
+
+ break;
+
+ case FW_CMD_JA:
+ if (ocp_data > compare)
+ goto do_jump;
+
+ break;
+
+ case FW_CMD_JAE:
+ if (ocp_data >= compare)
+ goto do_jump;
+
+ break;
+
+ case FW_CMD_JB:
+ if (ocp_data < compare)
+ goto do_jump;
+
+ break;
+
+ case FW_CMD_JBE:
+ if (ocp_data <= compare)
+ goto do_jump;
+
+ break;
+
+ case FW_CMD_CX:
+ /* struct fw_cmd_cx {
+ * struct fw_cmd_generic op;
+ * __le16 cx;
+ * };
+ */
+
+ memcpy(&le16_data, &d[j], sizeof(le16_data));
+ cx = __le16_to_cpu(le16_data);
+ break;
+
+ case FW_CMD_LOOP:
+ if (--cx > 0)
+ goto do_jump;
+
+ break;
+
+ case FW_CMD_LOOPE:
+ if (--cx > 0 && ocp_data == compare)
+ goto do_jump;
+
+ break;
+
+ case FW_CMD_LOOPNE:
+ if (--cx > 0 && ocp_data != compare)
+ goto do_jump;
+
+ break;
+
+ case FW_CMD_USLEEP:
+ /* struct fw_cmd_usleep {
+ * struct fw_cmd_generic op;
+ * __le16 us;
+ * };
+ */
+
+ memcpy(&le16_data, &d[j], sizeof(le16_data));
+ us = __le16_to_cpu(le16_data);
+ usleep_range(us , us * 4);
+ break;
+
+ case FW_CMD_END:
+ default:
+ return;
+ }
+
+ i += sizeof(op) + len;
+ }
+}
+
+static void rtl_release_firmware(struct r8152 *tp)
+{
+ if (!IS_ERR_OR_NULL(tp->rtl_fw.fw)) {
+ release_firmware(tp->rtl_fw.fw);
+ tp->rtl_fw.fw = NULL;
+ }
+}
+
+static void rtl_request_firmware(struct r8152 *tp)
+{
+ char *fw_name = NULL;
+
+ if (tp->rtl_fw.fw)
+ goto out_request;
+
+ switch (tp->version) {
+ case RTL_VER_01:
+ fw_name = "rtl_nic/rtl8152-1.fw";
+ break;
+ case RTL_VER_02:
+ fw_name = "rtl_nic/rtl8152-2.fw";
+ break;
+ case RTL_VER_03:
+ fw_name = "rtl_nic/rtl8153-1.fw";
+ break;
+ case RTL_VER_04:
+ fw_name = "rtl_nic/rtl8153-2.fw";
+ break;
+ case RTL_VER_05:
+ fw_name = "rtl_nic/rtl8153-3.fw";
+ break;
+ default:
+ goto out_request;
+ }
+
+ if (request_firmware(&tp->rtl_fw.fw, fw_name, &tp->netdev->dev) < 0)
+ goto err_warn;
+
+ if (!rtl_check_firmware(tp, &tp->rtl_fw)) {
+ netif_err(tp, ifup, tp->netdev, "invalid firwmare\n");
+ goto err_release_firmware;
+ }
+
+out_request:
+ return;
+
+err_release_firmware:
+ release_firmware(tp->rtl_fw.fw);
+err_warn:
+ netif_warn(tp, ifup, tp->netdev, "unable to load firmware patch %s\n",
+ fw_name);
+ tp->rtl_fw.fw = ERR_PTR(-ENOENT);
+ goto out_request;
+}
+
+static void rtl_apply_firmware(struct r8152 *tp)
+{
+ if (!IS_ERR_OR_NULL(tp->rtl_fw.fw)) {
+ rtl_fw_write(tp, tp->rtl_fw.code, tp->rtl_fw.code_size);
+ tp->ocp_base = 0;
+ }
+}
+
static struct tx_agg *r8152_get_tx_agg(struct r8152 *tp)
{
struct tx_agg *agg = NULL;
@@ -2226,6 +3083,7 @@ static void r8152b_hw_phy_cfg(struct r8152 *tp)

r8152b_disable_aldps(tp);

+ rtl_apply_firmware(tp);

r8152b_enable_aldps(tp);
set_bit(PHY_RESET, &tp->flags);
@@ -2381,6 +3239,7 @@ static void r8153_hw_phy_cfg(struct r8152 *tp)
r8152_mdio_write(tp, MII_BMCR, data);
}

+ rtl_apply_firmware(tp);

if (tp->version == RTL_VER_03) {
data = ocp_reg_read(tp, OCP_EEE_CFG);
@@ -2802,6 +3661,7 @@ static int rtl8152_open(struct net_device *netdev)
tp->rtl_ops.disable(tp);
}

+ rtl_request_firmware(tp);
tp->rtl_ops.up(tp);

rtl8152_set_speed(tp, AUTONEG_ENABLE,
@@ -3130,6 +3990,10 @@ static void rtl8152_get_drvinfo(struct net_device *netdev,
strlcpy(info->driver, MODULENAME, sizeof(info->driver));
strlcpy(info->version, DRIVER_VERSION, sizeof(info->version));
usb_make_path(tp->udev, info->bus_info, sizeof(info->bus_info));
+ BUILD_BUG_ON(sizeof(info->fw_version) < sizeof(tp->rtl_fw.version));
+ if (!IS_ERR_OR_NULL(tp->rtl_fw.fw))
+ strlcpy(info->fw_version, tp->rtl_fw.version,
+ sizeof(info->fw_version));
}

static
@@ -3514,6 +4378,7 @@ static void rtl8152_disconnect(struct usb_interface *intf)
tasklet_kill(&tp->tl);
unregister_netdev(tp->netdev);
tp->rtl_ops.unload(tp);
+ rtl_release_firmware(tp);
free_netdev(tp->netdev);
}
}
--
1.9.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/