1/* 2 * Abilis Systems Single DVB-T Receiver 3 * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> 4 * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2, or (at your option) 9 * any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 */ 16#include <linux/kernel.h> 17#include <linux/errno.h> 18#include <linux/ctype.h> 19#include <linux/delay.h> 20#include <linux/firmware.h> 21 22#include "as102_drv.h" 23#include "as102_fw.h" 24 25static const char as102_st_fw1[] = "as102_data1_st.hex"; 26static const char as102_st_fw2[] = "as102_data2_st.hex"; 27static const char as102_dt_fw1[] = "as102_data1_dt.hex"; 28static const char as102_dt_fw2[] = "as102_data2_dt.hex"; 29 30static unsigned char atohx(unsigned char *dst, char *src) 31{ 32 unsigned char value = 0; 33 34 char msb = tolower(*src) - '0'; 35 char lsb = tolower(*(src + 1)) - '0'; 36 37 if (msb > 9) 38 msb -= 7; 39 if (lsb > 9) 40 lsb -= 7; 41 42 *dst = value = ((msb & 0xF) << 4) | (lsb & 0xF); 43 return value; 44} 45 46/* 47 * Parse INTEL HEX firmware file to extract address and data. 48 */ 49static int parse_hex_line(unsigned char *fw_data, unsigned char *addr, 50 unsigned char *data, int *dataLength, 51 unsigned char *addr_has_changed) { 52 53 int count = 0; 54 unsigned char *src, dst; 55 56 if (*fw_data++ != ':') { 57 pr_err("invalid firmware file\n"); 58 return -EFAULT; 59 } 60 61 /* locate end of line */ 62 for (src = fw_data; *src != '\n'; src += 2) { 63 atohx(&dst, src); 64 /* parse line to split addr / data */ 65 switch (count) { 66 case 0: 67 *dataLength = dst; 68 break; 69 case 1: 70 addr[2] = dst; 71 break; 72 case 2: 73 addr[3] = dst; 74 break; 75 case 3: 76 /* check if data is an address */ 77 if (dst == 0x04) 78 *addr_has_changed = 1; 79 else 80 *addr_has_changed = 0; 81 break; 82 case 4: 83 case 5: 84 if (*addr_has_changed) 85 addr[(count - 4)] = dst; 86 else 87 data[(count - 4)] = dst; 88 break; 89 default: 90 data[(count - 4)] = dst; 91 break; 92 } 93 count++; 94 } 95 96 /* return read value + ':' + '\n' */ 97 return (count * 2) + 2; 98} 99 100static int as102_firmware_upload(struct as10x_bus_adapter_t *bus_adap, 101 unsigned char *cmd, 102 const struct firmware *firmware) { 103 104 struct as10x_fw_pkt_t fw_pkt; 105 int total_read_bytes = 0, errno = 0; 106 unsigned char addr_has_changed = 0; 107 108 for (total_read_bytes = 0; total_read_bytes < firmware->size; ) { 109 int read_bytes = 0, data_len = 0; 110 111 /* parse intel hex line */ 112 read_bytes = parse_hex_line( 113 (u8 *) (firmware->data + total_read_bytes), 114 fw_pkt.raw.address, 115 fw_pkt.raw.data, 116 &data_len, 117 &addr_has_changed); 118 119 if (read_bytes <= 0) 120 goto error; 121 122 /* detect the end of file */ 123 total_read_bytes += read_bytes; 124 if (total_read_bytes == firmware->size) { 125 fw_pkt.u.request[0] = 0x00; 126 fw_pkt.u.request[1] = 0x03; 127 128 /* send EOF command */ 129 errno = bus_adap->ops->upload_fw_pkt(bus_adap, 130 (uint8_t *) 131 &fw_pkt, 2, 0); 132 if (errno < 0) 133 goto error; 134 } else { 135 if (!addr_has_changed) { 136 /* prepare command to send */ 137 fw_pkt.u.request[0] = 0x00; 138 fw_pkt.u.request[1] = 0x01; 139 140 data_len += sizeof(fw_pkt.u.request); 141 data_len += sizeof(fw_pkt.raw.address); 142 143 /* send cmd to device */ 144 errno = bus_adap->ops->upload_fw_pkt(bus_adap, 145 (uint8_t *) 146 &fw_pkt, 147 data_len, 148 0); 149 if (errno < 0) 150 goto error; 151 } 152 } 153 } 154error: 155 return (errno == 0) ? total_read_bytes : errno; 156} 157 158int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap) 159{ 160 int errno = -EFAULT; 161 const struct firmware *firmware = NULL; 162 unsigned char *cmd_buf = NULL; 163 const char *fw1, *fw2; 164 struct usb_device *dev = bus_adap->usb_dev; 165 166 /* select fw file to upload */ 167 if (dual_tuner) { 168 fw1 = as102_dt_fw1; 169 fw2 = as102_dt_fw2; 170 } else { 171 fw1 = as102_st_fw1; 172 fw2 = as102_st_fw2; 173 } 174 175 /* allocate buffer to store firmware upload command and data */ 176 cmd_buf = kzalloc(MAX_FW_PKT_SIZE, GFP_KERNEL); 177 if (cmd_buf == NULL) { 178 errno = -ENOMEM; 179 goto error; 180 } 181 182 /* request kernel to locate firmware file: part1 */ 183 errno = request_firmware(&firmware, fw1, &dev->dev); 184 if (errno < 0) { 185 pr_err("%s: unable to locate firmware file: %s\n", 186 DRIVER_NAME, fw1); 187 goto error; 188 } 189 190 /* initiate firmware upload */ 191 errno = as102_firmware_upload(bus_adap, cmd_buf, firmware); 192 if (errno < 0) { 193 pr_err("%s: error during firmware upload part1\n", 194 DRIVER_NAME); 195 goto error; 196 } 197 198 pr_info("%s: firmware: %s loaded with success\n", 199 DRIVER_NAME, fw1); 200 release_firmware(firmware); 201 202 /* wait for boot to complete */ 203 mdelay(100); 204 205 /* request kernel to locate firmware file: part2 */ 206 errno = request_firmware(&firmware, fw2, &dev->dev); 207 if (errno < 0) { 208 pr_err("%s: unable to locate firmware file: %s\n", 209 DRIVER_NAME, fw2); 210 goto error; 211 } 212 213 /* initiate firmware upload */ 214 errno = as102_firmware_upload(bus_adap, cmd_buf, firmware); 215 if (errno < 0) { 216 pr_err("%s: error during firmware upload part2\n", 217 DRIVER_NAME); 218 goto error; 219 } 220 221 pr_info("%s: firmware: %s loaded with success\n", 222 DRIVER_NAME, fw2); 223error: 224 kfree(cmd_buf); 225 release_firmware(firmware); 226 227 return errno; 228} 229