[media] af9035: reimplement firmware downloader
authorAntti Palosaari <crope@iki.fi>
Sun, 1 Apr 2012 04:32:23 +0000 (01:32 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 9 Apr 2012 17:43:40 +0000 (14:43 -0300)
Big thanks to Daniel Glöckner <daniel-gl@gmx.net> for
revealing firmware structure on Linux Media mailing
list.

Signed-off-by: Antti Palosaari <crope@iki.fi>
Cc: Daniel Glöckner <daniel-gl@gmx.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/dvb/dvb-usb/af9035.c

index b8cd27a..01dee02 100644 (file)
@@ -355,80 +355,74 @@ err:
 static int af9035_download_firmware(struct usb_device *udev,
                const struct firmware *fw)
 {
-       u8 *fw_data_ptr = (u8 *) fw->data;
-       int i, j, len, packets, remainder, ret;
+       int ret, i, j, len;
        u8 wbuf[1];
        u8 rbuf[4];
-       struct fw_header fw_hdr;
        struct usb_req req = { 0, 0, 0, NULL, 0, NULL };
        struct usb_req req_fw_dl = { CMD_FW_DL, 0, 0, wbuf, 0, NULL };
        struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ;
+       u8 hdr_core;
+       u16 hdr_addr, hdr_data_len, hdr_checksum;
+       #define MAX_DATA 57
+       #define HDR_SIZE 7
+
+       /*
+        * Thanks to Daniel Glöckner <daniel-gl@gmx.net> about that info!
+        *
+        * byte 0: MCS 51 core
+        *  There are two inside the AF9035 (1=Link and 2=OFDM) with separate
+        *  address spaces
+        * byte 1-2: Big endian destination address
+        * byte 3-4: Big endian number of data bytes following the header
+        * byte 5-6: Big endian header checksum, apparently ignored by the chip
+        *  Calculated as ~(h[0]*256+h[1]+h[2]*256+h[3]+h[4]*256)
+        */
+
+       for (i = fw->size; i > HDR_SIZE;) {
+               hdr_core = fw->data[fw->size - i + 0];
+               hdr_addr = fw->data[fw->size - i + 1] << 8;
+               hdr_addr |= fw->data[fw->size - i + 2] << 0;
+               hdr_data_len = fw->data[fw->size - i + 3] << 8;
+               hdr_data_len |= fw->data[fw->size - i + 4] << 0;
+               hdr_checksum = fw->data[fw->size - i + 5] << 8;
+               hdr_checksum |= fw->data[fw->size - i + 6] << 0;
+
+               pr_debug("%s: core=%d addr=%04x data_len=%d checksum=%04x\n",
+                               __func__, hdr_core, hdr_addr, hdr_data_len,
+                               hdr_checksum);
+
+               if (((hdr_core != 1) && (hdr_core != 2)) ||
+                               (hdr_data_len > i)) {
+                       pr_debug("%s: bad firmware\n", __func__);
+                       break;
+               }
 
-       /* read firmware segment info from beginning of the firmware file */
-       fw_hdr.segment_count = *fw_data_ptr++;
-       pr_debug("%s: fw segment count=%d\n", __func__, fw_hdr.segment_count);
-       if (fw_hdr.segment_count > SEGMENT_MAX_COUNT) {
-               pr_debug("%s: too big fw segmen count=%d\n", __func__,
-                               fw_hdr.segment_count);
-               fw_hdr.segment_count = SEGMENT_MAX_COUNT;
-       }
-       for (i = 0; i < fw_hdr.segment_count; i++) {
-               fw_hdr.segment[i].type = (*fw_data_ptr++);
-               fw_hdr.segment[i].len  = (*fw_data_ptr++) << 24;
-               fw_hdr.segment[i].len += (*fw_data_ptr++) << 16;
-               fw_hdr.segment[i].len += (*fw_data_ptr++) <<  8;
-               fw_hdr.segment[i].len += (*fw_data_ptr++) <<  0;
-               pr_debug("%s: fw segment type=%d len=%d\n", __func__,
-                       fw_hdr.segment[i].type, fw_hdr.segment[i].len);
-       }
-
-       #define FW_PACKET_MAX_DATA 57 /* 63-4-2, packet_size-header-checksum */
-
-       /* download all segments */
-       for (i = 0; i < fw_hdr.segment_count; i++) {
-               pr_debug("%s: segment type=%d\n", __func__,
-                       fw_hdr.segment[i].type);
-               if (fw_hdr.segment[i].type == SEGMENT_FW_DL) {
-                       /* download begin packet */
-                       req.cmd = CMD_FW_DL_BEGIN;
-                       ret = af9035_ctrl_msg(udev, &req);
-                       if (ret < 0) {
-                               pr_debug("%s: fw dl failed=%d\n", __func__,
-                                       ret);
-                               goto err;
-                       }
-
-                       packets = fw_hdr.segment[i].len / FW_PACKET_MAX_DATA;
-                       remainder = fw_hdr.segment[i].len % FW_PACKET_MAX_DATA;
-                       len = FW_PACKET_MAX_DATA;
-                       for (j = 0; j <= packets; j++) {
-                               if (j == packets)  /* size of the last packet */
-                                       len = remainder;
-
-                               req_fw_dl.wlen = len;
-                               req_fw_dl.wbuf = fw_data_ptr;
-                               ret = af9035_ctrl_msg(udev, &req_fw_dl);
-                               if (ret < 0) {
-                                       pr_debug("%s: fw dl failed=%d " \
-                                                       "segment=%d " \
-                                                       "packet=%d\n",
-                                                       __func__, ret, i, j);
-                                       goto err;
-                               }
-                               fw_data_ptr += len;
-                       }
-                       /* download end packet */
-                       req.cmd = CMD_FW_DL_END;
-                       ret = af9035_ctrl_msg(udev, &req);
-                       if (ret < 0) {
-                               pr_debug("%s: fw dl failed=%d\n", __func__,
-                                       ret);
+               /* download begin packet */
+               req.cmd = CMD_FW_DL_BEGIN;
+               ret = af9035_ctrl_msg(udev, &req);
+
+               /* download firmware packet(s) */
+               for (j = HDR_SIZE + hdr_data_len; j > 0; j -= MAX_DATA) {
+                       len = j;
+                       if (len > MAX_DATA)
+                               len = MAX_DATA;
+                       req_fw_dl.wlen = len;
+                       req_fw_dl.wbuf = (u8 *) &fw->data[fw->size - i +
+                                       HDR_SIZE + hdr_data_len - j];
+                       ret = af9035_ctrl_msg(udev, &req_fw_dl);
+                       if (ret < 0)
                                goto err;
-                       }
-               } else {
-                       pr_debug("%s: segment type=%d not implemented\n",
-                               __func__, fw_hdr.segment[i].type);
                }
+
+               /* download end packet */
+               req.cmd = CMD_FW_DL_END;
+               ret = af9035_ctrl_msg(udev, &req);
+               if (ret < 0)
+                       goto err;
+
+               i -= hdr_data_len + HDR_SIZE;
+
+               pr_debug("%s: data uploaded=%lu\n", __func__, fw->size - i);
        }
 
        /* firmware loaded, request boot */
@@ -443,15 +437,15 @@ static int af9035_download_firmware(struct usb_device *udev,
        if (ret < 0)
                goto err;
 
-       pr_debug("%s: reply=%02x %02x %02x %02x\n", __func__,
-               rbuf[0], rbuf[1], rbuf[2], rbuf[3]);
-
        if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) {
-               pr_debug("%s: fw did not run\n", __func__);
+               info("firmware did not run");
                ret = -ENODEV;
                goto err;
        }
 
+       info("firmware version=%d.%d.%d.%d", rbuf[0], rbuf[1], rbuf[2],
+                       rbuf[3]);
+
        return 0;
 
 err:
@@ -654,7 +648,7 @@ static struct dvb_usb_device_properties af9035_properties[] = {
 
                .usb_ctrl = DEVICE_SPECIFIC,
                .download_firmware = af9035_download_firmware,
-               .firmware = "dvb-usb-af9035-01.fw",
+               .firmware = "dvb-usb-af9035-02.fw",
                .no_reconnect = 1,
 
                .num_adapters = 1,