From 77c5ff2d8992e9c4b8ed722f391e92aece5c14cc Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 1 Apr 2012 01:32:23 -0300 Subject: [PATCH] [media] af9035: reimplement firmware downloader MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Big thanks to Daniel Glöckner for revealing firmware structure on Linux Media mailing list. Signed-off-by: Antti Palosaari Cc: Daniel Glöckner Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9035.c | 136 ++++++++++++++--------------- 1 file changed, 65 insertions(+), 71 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c index b8cd27ae161b..01dee02ff280 100644 --- a/drivers/media/dvb/dvb-usb/af9035.c +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -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 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, -- 2.30.2