| // SPDX-License-Identifier: GPL-2.0-or-later | 
| /* | 
|  * Abilis Systems Single DVB-T Receiver | 
|  * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> | 
|  * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com> | 
|  */ | 
| #include <linux/kernel.h> | 
| #include <linux/errno.h> | 
| #include <linux/ctype.h> | 
| #include <linux/delay.h> | 
| #include <linux/firmware.h> | 
|   | 
| #include "as102_drv.h" | 
| #include "as102_fw.h" | 
|   | 
| static const char as102_st_fw1[] = "as102_data1_st.hex"; | 
| static const char as102_st_fw2[] = "as102_data2_st.hex"; | 
| static const char as102_dt_fw1[] = "as102_data1_dt.hex"; | 
| static const char as102_dt_fw2[] = "as102_data2_dt.hex"; | 
|   | 
| static unsigned char atohx(unsigned char *dst, char *src) | 
| { | 
|     unsigned char value = 0; | 
|   | 
|     char msb = tolower(*src) - '0'; | 
|     char lsb = tolower(*(src + 1)) - '0'; | 
|   | 
|     if (msb > 9) | 
|         msb -= 7; | 
|     if (lsb > 9) | 
|         lsb -= 7; | 
|   | 
|     *dst = value = ((msb & 0xF) << 4) | (lsb & 0xF); | 
|     return value; | 
| } | 
|   | 
| /* | 
|  * Parse INTEL HEX firmware file to extract address and data. | 
|  */ | 
| static int parse_hex_line(unsigned char *fw_data, unsigned char *addr, | 
|               unsigned char *data, int *dataLength, | 
|               unsigned char *addr_has_changed) { | 
|   | 
|     int count = 0; | 
|     unsigned char *src, dst; | 
|   | 
|     if (*fw_data++ != ':') { | 
|         pr_err("invalid firmware file\n"); | 
|         return -EFAULT; | 
|     } | 
|   | 
|     /* locate end of line */ | 
|     for (src = fw_data; *src != '\n'; src += 2) { | 
|         atohx(&dst, src); | 
|         /* parse line to split addr / data */ | 
|         switch (count) { | 
|         case 0: | 
|             *dataLength = dst; | 
|             break; | 
|         case 1: | 
|             addr[2] = dst; | 
|             break; | 
|         case 2: | 
|             addr[3] = dst; | 
|             break; | 
|         case 3: | 
|             /* check if data is an address */ | 
|             if (dst == 0x04) | 
|                 *addr_has_changed = 1; | 
|             else | 
|                 *addr_has_changed = 0; | 
|             break; | 
|         case  4: | 
|         case  5: | 
|             if (*addr_has_changed) | 
|                 addr[(count - 4)] = dst; | 
|             else | 
|                 data[(count - 4)] = dst; | 
|             break; | 
|         default: | 
|             data[(count - 4)] = dst; | 
|             break; | 
|         } | 
|         count++; | 
|     } | 
|   | 
|     /* return read value + ':' + '\n' */ | 
|     return (count * 2) + 2; | 
| } | 
|   | 
| static int as102_firmware_upload(struct as10x_bus_adapter_t *bus_adap, | 
|                  unsigned char *cmd, | 
|                  const struct firmware *firmware) { | 
|   | 
|     struct as10x_fw_pkt_t *fw_pkt; | 
|     int total_read_bytes = 0, errno = 0; | 
|     unsigned char addr_has_changed = 0; | 
|   | 
|     fw_pkt = kmalloc(sizeof(*fw_pkt), GFP_KERNEL); | 
|     if (!fw_pkt) | 
|         return -ENOMEM; | 
|   | 
|   | 
|     for (total_read_bytes = 0; total_read_bytes < firmware->size; ) { | 
|         int read_bytes = 0, data_len = 0; | 
|   | 
|         /* parse intel hex line */ | 
|         read_bytes = parse_hex_line( | 
|                 (u8 *) (firmware->data + total_read_bytes), | 
|                 fw_pkt->raw.address, | 
|                 fw_pkt->raw.data, | 
|                 &data_len, | 
|                 &addr_has_changed); | 
|   | 
|         if (read_bytes <= 0) | 
|             goto error; | 
|   | 
|         /* detect the end of file */ | 
|         total_read_bytes += read_bytes; | 
|         if (total_read_bytes == firmware->size) { | 
|             fw_pkt->u.request[0] = 0x00; | 
|             fw_pkt->u.request[1] = 0x03; | 
|   | 
|             /* send EOF command */ | 
|             errno = bus_adap->ops->upload_fw_pkt(bus_adap, | 
|                                  (uint8_t *) | 
|                                  fw_pkt, 2, 0); | 
|             if (errno < 0) | 
|                 goto error; | 
|         } else { | 
|             if (!addr_has_changed) { | 
|                 /* prepare command to send */ | 
|                 fw_pkt->u.request[0] = 0x00; | 
|                 fw_pkt->u.request[1] = 0x01; | 
|   | 
|                 data_len += sizeof(fw_pkt->u.request); | 
|                 data_len += sizeof(fw_pkt->raw.address); | 
|   | 
|                 /* send cmd to device */ | 
|                 errno = bus_adap->ops->upload_fw_pkt(bus_adap, | 
|                                      (uint8_t *) | 
|                                      fw_pkt, | 
|                                      data_len, | 
|                                      0); | 
|                 if (errno < 0) | 
|                     goto error; | 
|             } | 
|         } | 
|     } | 
| error: | 
|     kfree(fw_pkt); | 
|     return (errno == 0) ? total_read_bytes : errno; | 
| } | 
|   | 
| int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap) | 
| { | 
|     int errno = -EFAULT; | 
|     const struct firmware *firmware = NULL; | 
|     unsigned char *cmd_buf = NULL; | 
|     const char *fw1, *fw2; | 
|     struct usb_device *dev = bus_adap->usb_dev; | 
|   | 
|     /* select fw file to upload */ | 
|     if (dual_tuner) { | 
|         fw1 = as102_dt_fw1; | 
|         fw2 = as102_dt_fw2; | 
|     } else { | 
|         fw1 = as102_st_fw1; | 
|         fw2 = as102_st_fw2; | 
|     } | 
|   | 
|     /* allocate buffer to store firmware upload command and data */ | 
|     cmd_buf = kzalloc(MAX_FW_PKT_SIZE, GFP_KERNEL); | 
|     if (cmd_buf == NULL) { | 
|         errno = -ENOMEM; | 
|         goto error; | 
|     } | 
|   | 
|     /* request kernel to locate firmware file: part1 */ | 
|     errno = request_firmware(&firmware, fw1, &dev->dev); | 
|     if (errno < 0) { | 
|         pr_err("%s: unable to locate firmware file: %s\n", | 
|                DRIVER_NAME, fw1); | 
|         goto error; | 
|     } | 
|   | 
|     /* initiate firmware upload */ | 
|     errno = as102_firmware_upload(bus_adap, cmd_buf, firmware); | 
|     if (errno < 0) { | 
|         pr_err("%s: error during firmware upload part1\n", | 
|                DRIVER_NAME); | 
|         goto error; | 
|     } | 
|   | 
|     pr_info("%s: firmware: %s loaded with success\n", | 
|         DRIVER_NAME, fw1); | 
|     release_firmware(firmware); | 
|     firmware = NULL; | 
|   | 
|     /* wait for boot to complete */ | 
|     mdelay(100); | 
|   | 
|     /* request kernel to locate firmware file: part2 */ | 
|     errno = request_firmware(&firmware, fw2, &dev->dev); | 
|     if (errno < 0) { | 
|         pr_err("%s: unable to locate firmware file: %s\n", | 
|                DRIVER_NAME, fw2); | 
|         goto error; | 
|     } | 
|   | 
|     /* initiate firmware upload */ | 
|     errno = as102_firmware_upload(bus_adap, cmd_buf, firmware); | 
|     if (errno < 0) { | 
|         pr_err("%s: error during firmware upload part2\n", | 
|                DRIVER_NAME); | 
|         goto error; | 
|     } | 
|   | 
|     pr_info("%s: firmware: %s loaded with success\n", | 
|         DRIVER_NAME, fw2); | 
| error: | 
|     kfree(cmd_buf); | 
|     release_firmware(firmware); | 
|   | 
|     return errno; | 
| } |