1 /* 2 * Ucode download related utility functions 3 * 4 * Copyright (C) 1999-2013, Broadcom Corporation 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 * $Id: ucode_download.c 297277 2011-11-18 14:10:09Z $ 19 */ 20 21 #include <unistd.h> 22 #include <errno.h> 23 #include <trxhdr.h> 24 #include <bcmendian.h> 25 #include <wlu_common.h> 26 27 #define DEVPRESENT_DELAY 10000 /* in microsecs */ 28 #define DEVPRESENT_RETRIES 100 29 30 extern int wl_validatedev(void *dev_handle); 31 32 int 33 dload_generic_data(void *wl, uint16 dload_type, unsigned char *dload_buf, int len) 34 { 35 struct wl_dload_data *dload_ptr = (struct wl_dload_data *)dload_buf; 36 int err = 0; 37 int actual_data_offset; 38 char *buf; 39 40 actual_data_offset = OFFSETOF(struct wl_dload_data, data); 41 dload_ptr->flag = (DLOAD_HANDLER_VER << DLOAD_FLAG_VER_SHIFT); 42 dload_ptr->flag |= DL_CRC_NOT_INUSE; 43 dload_ptr->dload_type = dload_type; 44 dload_ptr->len = htod32(len - actual_data_offset); 45 dload_ptr->crc = 0; 46 47 len = len + 8 - (len%8); 48 49 buf = malloc(WLC_IOCTL_MEDLEN); 50 if (buf) { 51 bzero(buf, WLC_IOCTL_MEDLEN); 52 err = wlu_iovar_setbuf(wl, "generic_dload", dload_buf, len, buf, 53 WLC_IOCTL_MEDLEN); 54 } 55 free(buf); 56 return err; 57 } 58 59 int 60 dload_ucode_part(void *wl, uint8 ucode_type, uint32 datalen, unsigned char *org_buf) 61 { 62 int num_chunks, chunk_len, cumulative_len = 0; 63 int size2alloc, ucode_chunk_len = 0; 64 unsigned char *new_buf; 65 struct wl_ucode_info *ucode_ptr; 66 int err = 0, ucode_offset, chunk_offset; 67 68 ucode_offset = OFFSETOF(wl_dload_data_t, data); 69 chunk_offset = OFFSETOF(wl_ucode_info_t, data_chunk); 70 71 err = wlu_iovar_getint(wl, "ucdload_chunk_len", 72 &ucode_chunk_len); 73 if (err) { 74 printf("err in getting ucode chunk len, exiting\n"); 75 return err; 76 } 77 78 num_chunks = datalen/ucode_chunk_len; 79 if (datalen % ucode_chunk_len != 0) 80 num_chunks++; 81 size2alloc = ucode_offset + chunk_offset + ucode_chunk_len; 82 83 /* single chunk buffer */ 84 new_buf = (unsigned char *)malloc(size2alloc); 85 memset(new_buf, 0, size2alloc); 86 ucode_ptr = (struct wl_ucode_info *)((uint8 *)new_buf+ucode_offset); 87 ucode_ptr->ucode_type = ucode_type; 88 ucode_ptr->num_chunks = num_chunks; 89 do { 90 if (datalen >= ucode_chunk_len) 91 chunk_len = ucode_chunk_len; 92 else 93 chunk_len = datalen; 94 memset(new_buf+ucode_offset+chunk_offset, 0, size2alloc-ucode_offset-chunk_offset); 95 ucode_ptr->chunk_len = htod32(chunk_len); 96 ucode_ptr->chunk_num++; 97 memcpy(&ucode_ptr->data_chunk[0], org_buf + cumulative_len, chunk_len); 98 cumulative_len += chunk_len; 99 err = dload_generic_data(wl, DL_TYPE_UCODE, new_buf, size2alloc); 100 if (err) { 101 printf("error while writing %s to the memory\n", 102 (ucode_type == UCODE_FW)? "ucode" : "initvals"); 103 break; 104 } 105 datalen = datalen - chunk_len; 106 } while (datalen > 0); 107 free(new_buf); 108 109 return err; 110 } 111 112 static int 113 check_ucode_file(unsigned char *headers) 114 { 115 struct trx_header *trx; 116 int actual_data_len = -1; 117 118 /* Extract trx header */ 119 trx = (struct trx_header *)headers; 120 if (trx->magic != TRX_MAGIC) { 121 printf("Error: trx bad hdr\n"); 122 goto err; 123 } 124 actual_data_len = ROUNDUP(trx->offsets[0], 4) + ROUNDUP(trx->offsets[1], 4); 125 err: 126 return actual_data_len; 127 } 128 129 int 130 proc_ucode_download(char* fw_filename, void *dev_handle) 131 { 132 FILE *fp = NULL; 133 int ret = 0, loopcnt = 0; 134 struct trx_header main_trx_hdr, *ucode_trx_hdr; 135 uint32 maintrx_hdr_len, tmp_len; 136 uint32 fw_size, second_offset, ucode_trx_offset; 137 long ucode_pos; 138 unsigned long ucode_info_len = 0, status; 139 unsigned char *ucodetrx_buf, *initvals_ptr; 140 int ucode_len, initvals_len; 141 int ucdload_status = 0; 142 int is_devpresent; 143 144 /* read the file and push blocks down to memory */ 145 if ((fp = fopen(fw_filename, "rb")) == NULL) { 146 fprintf(stderr, "%s: unable to open %s: %s\n", 147 __FUNCTION__, fw_filename, strerror(errno)); 148 ret = -1; 149 goto exit; 150 } 151 152 maintrx_hdr_len = sizeof(struct trx_header); 153 tmp_len = fread(&main_trx_hdr, sizeof(uint8), maintrx_hdr_len, fp); 154 155 if (tmp_len == maintrx_hdr_len) { 156 if (main_trx_hdr.magic == TRX_MAGIC) { 157 fw_size = main_trx_hdr.offsets[0]; 158 second_offset = main_trx_hdr.offsets[2]; 159 160 if (second_offset == maintrx_hdr_len) { 161 second_offset = 0; 162 } 163 ucode_trx_offset = maintrx_hdr_len + 164 ROUNDUP(fw_size, 4) + ROUNDUP(second_offset, 4); 165 ucode_pos = fseek(fp, ucode_trx_offset, SEEK_SET); 166 BCM_REFERENCE(ucode_pos); 167 168 if ((ucode_trx_hdr = malloc(sizeof(struct trx_header))) 169 == NULL) { 170 printf("Unable to allocate %d bytes!\n", maintrx_hdr_len); 171 ret = -ENOMEM; 172 goto exit; 173 } 174 175 /* Read ONLY the firmware-file-header into the new_buffer */ 176 status = fread(ucode_trx_hdr, sizeof(uint8), 177 maintrx_hdr_len, fp); 178 if (status < sizeof(struct trx_header)) { 179 printf("Short read in hdr read for %s!\n", fw_filename); 180 ret = -EINVAL; 181 goto exit; 182 } 183 184 if ((ucode_info_len = check_ucode_file( 185 (unsigned char *)ucode_trx_hdr)) <= 0) { 186 printf("not a valid ucode.trx\n"); 187 ret = -1; 188 goto exit; 189 } 190 191 ucodetrx_buf = (unsigned char *)malloc(ucode_info_len * 192 sizeof(char)); 193 tmp_len = fread(ucodetrx_buf, sizeof(uint8), 194 ucode_info_len, fp); 195 if (ucode_info_len > 0) { 196 ucode_len = ucode_trx_hdr->offsets[0]; 197 initvals_ptr = ucodetrx_buf + 198 ROUNDUP(ucode_trx_hdr->offsets[0], 4); 199 initvals_len = ucode_trx_hdr->offsets[1]; 200 } 201 free(ucode_trx_hdr); 202 203 init_cmd_batchingmode(); 204 do { 205 is_devpresent = wl_validatedev(dev_handle); 206 loopcnt++; 207 /* in USB after dongle fw starts running wl interface 208 might not appear in the list of interfaces immediately, hence try 209 after some delay of 10ms 210 */ 211 if (!is_devpresent) 212 usleep(DEVPRESENT_DELAY); 213 else { 214 /* below iovar to verify if the for foundout 215 interface has already ucode been downloaded 216 */ 217 ret = wlu_iovar_getint(dev_handle, "ucdload_status", 218 &ucdload_status); 219 if (ret) { 220 printf("err in ucdload_status, exiting\n"); 221 goto exit; 222 } 223 if (ucdload_status) { 224 /* Number of 'wl' interfaces to skip 225 in the next round of going thru wl_find 226 */ 227 printf("ucode is already downloaded\n"); 228 } 229 } 230 /* usb seems to take some time to come up, hence the 231 loop value of 100 232 */ 233 } while (loopcnt < DEVPRESENT_RETRIES && !is_devpresent); 234 235 if (loopcnt < DEVPRESENT_RETRIES) { 236 /* download the ucode fw */ 237 ret = dload_ucode_part(dev_handle, UCODE_FW, ucode_len, 238 ucodetrx_buf); 239 if (ret) { 240 printf("error while downloading ucode, exiting\n"); 241 goto exit; 242 } 243 /* download the initvals to the dongle */ 244 ret = dload_ucode_part(dev_handle, INIT_VALS, 245 initvals_len, initvals_ptr); 246 247 if (ret) { 248 printf("error while downloading initvals, exiting\n"); 249 goto exit; 250 } 251 } 252 else { 253 printf("wl device is not present\n"); 254 } 255 free(ucodetrx_buf); 256 } 257 } 258 259 exit: 260 if (fp) 261 fclose(fp); 262 return ret; 263 } 264