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