| 1 | /* from src/prism2/download/prism2dl.c |
| 2 | * |
| 3 | * utility for downloading prism2 images moved into kernelspace |
| 4 | * |
| 5 | * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. |
| 6 | * -------------------------------------------------------------------- |
| 7 | * |
| 8 | * linux-wlan |
| 9 | * |
| 10 | * The contents of this file are subject to the Mozilla Public |
| 11 | * License Version 1.1 (the "License"); you may not use this file |
| 12 | * except in compliance with the License. You may obtain a copy of |
| 13 | * the License at http://www.mozilla.org/MPL/ |
| 14 | * |
| 15 | * Software distributed under the License is distributed on an "AS |
| 16 | * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or |
| 17 | * implied. See the License for the specific language governing |
| 18 | * rights and limitations under the License. |
| 19 | * |
| 20 | * Alternatively, the contents of this file may be used under the |
| 21 | * terms of the GNU Public License version 2 (the "GPL"), in which |
| 22 | * case the provisions of the GPL are applicable instead of the |
| 23 | * above. If you wish to allow the use of your version of this file |
| 24 | * only under the terms of the GPL and not to allow others to use |
| 25 | * your version of this file under the MPL, indicate your decision |
| 26 | * by deleting the provisions above and replace them with the notice |
| 27 | * and other provisions required by the GPL. If you do not delete |
| 28 | * the provisions above, a recipient may use your version of this |
| 29 | * file under either the MPL or the GPL. |
| 30 | * |
| 31 | * -------------------------------------------------------------------- |
| 32 | * |
| 33 | * Inquiries regarding the linux-wlan Open Source project can be |
| 34 | * made directly to: |
| 35 | * |
| 36 | * AbsoluteValue Systems Inc. |
| 37 | * info@linux-wlan.com |
| 38 | * http://www.linux-wlan.com |
| 39 | * |
| 40 | * -------------------------------------------------------------------- |
| 41 | * |
| 42 | * Portions of the development of this software were funded by |
| 43 | * Intersil Corporation as part of PRISM(R) chipset product development. |
| 44 | * |
| 45 | * -------------------------------------------------------------------- |
| 46 | */ |
| 47 | |
| 48 | /*================================================================*/ |
| 49 | /* System Includes */ |
| 50 | #include <linux/ihex.h> |
| 51 | #include <linux/slab.h> |
| 52 | |
| 53 | /*================================================================*/ |
| 54 | /* Local Constants */ |
| 55 | |
| 56 | #define PRISM2_USB_FWFILE "prism2_ru.fw" |
| 57 | MODULE_FIRMWARE(PRISM2_USB_FWFILE); |
| 58 | |
| 59 | #define S3DATA_MAX 5000 |
| 60 | #define S3PLUG_MAX 200 |
| 61 | #define S3CRC_MAX 200 |
| 62 | #define S3INFO_MAX 50 |
| 63 | |
| 64 | #define S3ADDR_PLUG (0xff000000UL) |
| 65 | #define S3ADDR_CRC (0xff100000UL) |
| 66 | #define S3ADDR_INFO (0xff200000UL) |
| 67 | #define S3ADDR_START (0xff400000UL) |
| 68 | |
| 69 | #define CHUNKS_MAX 100 |
| 70 | |
| 71 | #define WRITESIZE_MAX 4096 |
| 72 | |
| 73 | /*================================================================*/ |
| 74 | /* Local Types */ |
| 75 | |
| 76 | typedef struct s3datarec { |
| 77 | u32 len; |
| 78 | u32 addr; |
| 79 | u8 checksum; |
| 80 | u8 *data; |
| 81 | } s3datarec_t; |
| 82 | |
| 83 | typedef struct s3plugrec { |
| 84 | u32 itemcode; |
| 85 | u32 addr; |
| 86 | u32 len; |
| 87 | } s3plugrec_t; |
| 88 | |
| 89 | typedef struct s3crcrec { |
| 90 | u32 addr; |
| 91 | u32 len; |
| 92 | unsigned int dowrite; |
| 93 | } s3crcrec_t; |
| 94 | |
| 95 | typedef struct s3inforec { |
| 96 | u16 len; |
| 97 | u16 type; |
| 98 | union { |
| 99 | hfa384x_compident_t version; |
| 100 | hfa384x_caplevel_t compat; |
| 101 | u16 buildseq; |
| 102 | hfa384x_compident_t platform; |
| 103 | } info; |
| 104 | } s3inforec_t; |
| 105 | |
| 106 | typedef struct pda { |
| 107 | u8 buf[HFA384x_PDA_LEN_MAX]; |
| 108 | hfa384x_pdrec_t *rec[HFA384x_PDA_RECS_MAX]; |
| 109 | unsigned int nrec; |
| 110 | } pda_t; |
| 111 | |
| 112 | typedef struct imgchunk { |
| 113 | u32 addr; /* start address */ |
| 114 | u32 len; /* in bytes */ |
| 115 | u16 crc; /* CRC value (if it falls at a chunk boundary) */ |
| 116 | u8 *data; |
| 117 | } imgchunk_t; |
| 118 | |
| 119 | /*================================================================*/ |
| 120 | /* Local Static Definitions */ |
| 121 | |
| 122 | /*----------------------------------------------------------------*/ |
| 123 | /* s-record image processing */ |
| 124 | |
| 125 | /* Data records */ |
| 126 | unsigned int ns3data; |
| 127 | s3datarec_t s3data[S3DATA_MAX]; |
| 128 | |
| 129 | /* Plug records */ |
| 130 | unsigned int ns3plug; |
| 131 | s3plugrec_t s3plug[S3PLUG_MAX]; |
| 132 | |
| 133 | /* CRC records */ |
| 134 | unsigned int ns3crc; |
| 135 | s3crcrec_t s3crc[S3CRC_MAX]; |
| 136 | |
| 137 | /* Info records */ |
| 138 | unsigned int ns3info; |
| 139 | s3inforec_t s3info[S3INFO_MAX]; |
| 140 | |
| 141 | /* S7 record (there _better_ be only one) */ |
| 142 | u32 startaddr; |
| 143 | |
| 144 | /* Load image chunks */ |
| 145 | unsigned int nfchunks; |
| 146 | imgchunk_t fchunk[CHUNKS_MAX]; |
| 147 | |
| 148 | /* Note that for the following pdrec_t arrays, the len and code */ |
| 149 | /* fields are stored in HOST byte order. The mkpdrlist() function */ |
| 150 | /* does the conversion. */ |
| 151 | /*----------------------------------------------------------------*/ |
| 152 | /* PDA, built from [card|newfile]+[addfile1+addfile2...] */ |
| 153 | |
| 154 | pda_t pda; |
| 155 | hfa384x_compident_t nicid; |
| 156 | hfa384x_caplevel_t rfid; |
| 157 | hfa384x_caplevel_t macid; |
| 158 | hfa384x_caplevel_t priid; |
| 159 | |
| 160 | /*================================================================*/ |
| 161 | /* Local Function Declarations */ |
| 162 | |
| 163 | int prism2_fwapply(const struct ihex_binrec *rfptr, wlandevice_t *wlandev); |
| 164 | int read_fwfile(const struct ihex_binrec *rfptr); |
| 165 | int mkimage(imgchunk_t *clist, unsigned int *ccnt); |
| 166 | int read_cardpda(pda_t *pda, wlandevice_t *wlandev); |
| 167 | int mkpdrlist(pda_t *pda); |
| 168 | int plugimage(imgchunk_t *fchunk, unsigned int nfchunks, |
| 169 | s3plugrec_t *s3plug, unsigned int ns3plug, pda_t * pda); |
| 170 | int crcimage(imgchunk_t *fchunk, unsigned int nfchunks, |
| 171 | s3crcrec_t *s3crc, unsigned int ns3crc); |
| 172 | int writeimage(wlandevice_t *wlandev, imgchunk_t *fchunk, |
| 173 | unsigned int nfchunks); |
| 174 | void free_chunks(imgchunk_t *fchunk, unsigned int *nfchunks); |
| 175 | void free_srecs(void); |
| 176 | |
| 177 | int validate_identity(void); |
| 178 | |
| 179 | /*================================================================*/ |
| 180 | /* Function Definitions */ |
| 181 | |
| 182 | /*---------------------------------------------------------------- |
| 183 | * prism2_fwtry |
| 184 | * |
| 185 | * Try and get firmware into memory |
| 186 | * |
| 187 | * Arguments: |
| 188 | * udev usb device structure |
| 189 | * wlandev wlan device structure |
| 190 | * |
| 191 | * Returns: |
| 192 | * 0 - success |
| 193 | * ~0 - failure |
| 194 | ----------------------------------------------------------------*/ |
| 195 | int prism2_fwtry(struct usb_device *udev, wlandevice_t *wlandev) |
| 196 | { |
| 197 | const struct firmware *fw_entry = NULL; |
| 198 | |
| 199 | printk(KERN_INFO "prism2_usb: Checking for firmware %s\n", |
| 200 | PRISM2_USB_FWFILE); |
| 201 | if (request_ihex_firmware(&fw_entry, PRISM2_USB_FWFILE, &udev->dev) != 0) { |
| 202 | printk(KERN_INFO |
| 203 | "prism2_usb: Firmware not available, but not essential\n"); |
| 204 | printk(KERN_INFO |
| 205 | "prism2_usb: can continue to use card anyway.\n"); |
| 206 | return 1; |
| 207 | } |
| 208 | |
| 209 | printk(KERN_INFO "prism2_usb: %s will be processed, size %zu\n", |
| 210 | PRISM2_USB_FWFILE, fw_entry->size); |
| 211 | prism2_fwapply((const struct ihex_binrec *)fw_entry->data, wlandev); |
| 212 | |
| 213 | release_firmware(fw_entry); |
| 214 | return 0; |
| 215 | } |
| 216 | |
| 217 | /*---------------------------------------------------------------- |
| 218 | * prism2_fwapply |
| 219 | * |
| 220 | * Apply the firmware loaded into memory |
| 221 | * |
| 222 | * Arguments: |
| 223 | * rfptr firmware image in kernel memory |
| 224 | * wlandev device |
| 225 | * |
| 226 | * Returns: |
| 227 | * 0 - success |
| 228 | * ~0 - failure |
| 229 | ----------------------------------------------------------------*/ |
| 230 | int prism2_fwapply(const struct ihex_binrec *rfptr, wlandevice_t *wlandev) |
| 231 | { |
| 232 | signed int result = 0; |
| 233 | p80211msg_dot11req_mibget_t getmsg; |
| 234 | p80211itemd_t *item; |
| 235 | u32 *data; |
| 236 | |
| 237 | /* Initialize the data structures */ |
| 238 | ns3data = 0; |
| 239 | memset(s3data, 0, sizeof(s3data)); |
| 240 | ns3plug = 0; |
| 241 | memset(s3plug, 0, sizeof(s3plug)); |
| 242 | ns3crc = 0; |
| 243 | memset(s3crc, 0, sizeof(s3crc)); |
| 244 | ns3info = 0; |
| 245 | memset(s3info, 0, sizeof(s3info)); |
| 246 | startaddr = 0; |
| 247 | |
| 248 | nfchunks = 0; |
| 249 | memset(fchunk, 0, sizeof(fchunk)); |
| 250 | memset(&nicid, 0, sizeof(nicid)); |
| 251 | memset(&rfid, 0, sizeof(rfid)); |
| 252 | memset(&macid, 0, sizeof(macid)); |
| 253 | memset(&priid, 0, sizeof(priid)); |
| 254 | |
| 255 | /* clear the pda and add an initial END record */ |
| 256 | memset(&pda, 0, sizeof(pda)); |
| 257 | pda.rec[0] = (hfa384x_pdrec_t *) pda.buf; |
| 258 | pda.rec[0]->len = cpu_to_le16(2); /* len in words *//* len in words */ |
| 259 | pda.rec[0]->code = cpu_to_le16(HFA384x_PDR_END_OF_PDA); |
| 260 | pda.nrec = 1; |
| 261 | |
| 262 | /*-----------------------------------------------------*/ |
| 263 | /* Put card into fwload state */ |
| 264 | prism2sta_ifstate(wlandev, P80211ENUM_ifstate_fwload); |
| 265 | |
| 266 | /* Build the PDA we're going to use. */ |
| 267 | if (read_cardpda(&pda, wlandev)) { |
| 268 | printk(KERN_ERR "load_cardpda failed, exiting.\n"); |
| 269 | return 1; |
| 270 | } |
| 271 | |
| 272 | /* read the card's PRI-SUP */ |
| 273 | memset(&getmsg, 0, sizeof(getmsg)); |
| 274 | getmsg.msgcode = DIDmsg_dot11req_mibget; |
| 275 | getmsg.msglen = sizeof(getmsg); |
| 276 | strcpy(getmsg.devname, wlandev->name); |
| 277 | |
| 278 | getmsg.mibattribute.did = DIDmsg_dot11req_mibget_mibattribute; |
| 279 | getmsg.mibattribute.status = P80211ENUM_msgitem_status_data_ok; |
| 280 | getmsg.resultcode.did = DIDmsg_dot11req_mibget_resultcode; |
| 281 | getmsg.resultcode.status = P80211ENUM_msgitem_status_no_value; |
| 282 | |
| 283 | item = (p80211itemd_t *) getmsg.mibattribute.data; |
| 284 | item->did = DIDmib_p2_p2NIC_p2PRISupRange; |
| 285 | item->status = P80211ENUM_msgitem_status_no_value; |
| 286 | |
| 287 | data = (u32 *) item->data; |
| 288 | |
| 289 | /* DIDmsg_dot11req_mibget */ |
| 290 | prism2mgmt_mibset_mibget(wlandev, &getmsg); |
| 291 | if (getmsg.resultcode.data != P80211ENUM_resultcode_success) |
| 292 | printk(KERN_ERR "Couldn't fetch PRI-SUP info\n"); |
| 293 | |
| 294 | /* Already in host order */ |
| 295 | priid.role = *data++; |
| 296 | priid.id = *data++; |
| 297 | priid.variant = *data++; |
| 298 | priid.bottom = *data++; |
| 299 | priid.top = *data++; |
| 300 | |
| 301 | /* Read the S3 file */ |
| 302 | result = read_fwfile(rfptr); |
| 303 | if (result) { |
| 304 | printk(KERN_ERR "Failed to read the data exiting.\n"); |
| 305 | return 1; |
| 306 | } |
| 307 | |
| 308 | result = validate_identity(); |
| 309 | |
| 310 | if (result) { |
| 311 | printk(KERN_ERR "Incompatible firmware image.\n"); |
| 312 | return 1; |
| 313 | } |
| 314 | |
| 315 | if (startaddr == 0x00000000) { |
| 316 | printk(KERN_ERR "Can't RAM download a Flash image!\n"); |
| 317 | return 1; |
| 318 | } |
| 319 | |
| 320 | /* Make the image chunks */ |
| 321 | result = mkimage(fchunk, &nfchunks); |
| 322 | |
| 323 | /* Do any plugging */ |
| 324 | result = plugimage(fchunk, nfchunks, s3plug, ns3plug, &pda); |
| 325 | if (result) { |
| 326 | printk(KERN_ERR "Failed to plug data.\n"); |
| 327 | return 1; |
| 328 | } |
| 329 | |
| 330 | /* Insert any CRCs */ |
| 331 | if (crcimage(fchunk, nfchunks, s3crc, ns3crc)) { |
| 332 | printk(KERN_ERR "Failed to insert all CRCs\n"); |
| 333 | return 1; |
| 334 | } |
| 335 | |
| 336 | /* Write the image */ |
| 337 | result = writeimage(wlandev, fchunk, nfchunks); |
| 338 | if (result) { |
| 339 | printk(KERN_ERR "Failed to ramwrite image data.\n"); |
| 340 | return 1; |
| 341 | } |
| 342 | |
| 343 | /* clear any allocated memory */ |
| 344 | free_chunks(fchunk, &nfchunks); |
| 345 | free_srecs(); |
| 346 | |
| 347 | printk(KERN_INFO "prism2_usb: firmware loading finished.\n"); |
| 348 | |
| 349 | return result; |
| 350 | } |
| 351 | |
| 352 | /*---------------------------------------------------------------- |
| 353 | * crcimage |
| 354 | * |
| 355 | * Adds a CRC16 in the two bytes prior to each block identified by |
| 356 | * an S3 CRC record. Currently, we don't actually do a CRC we just |
| 357 | * insert the value 0xC0DE in hfa384x order. |
| 358 | * |
| 359 | * Arguments: |
| 360 | * fchunk Array of image chunks |
| 361 | * nfchunks Number of image chunks |
| 362 | * s3crc Array of crc records |
| 363 | * ns3crc Number of crc records |
| 364 | * |
| 365 | * Returns: |
| 366 | * 0 success |
| 367 | * ~0 failure |
| 368 | ----------------------------------------------------------------*/ |
| 369 | int crcimage(imgchunk_t *fchunk, unsigned int nfchunks, s3crcrec_t *s3crc, |
| 370 | unsigned int ns3crc) |
| 371 | { |
| 372 | int result = 0; |
| 373 | int i; |
| 374 | int c; |
| 375 | u32 crcstart; |
| 376 | u32 crcend; |
| 377 | u32 cstart = 0; |
| 378 | u32 cend; |
| 379 | u8 *dest; |
| 380 | u32 chunkoff; |
| 381 | |
| 382 | for (i = 0; i < ns3crc; i++) { |
| 383 | if (!s3crc[i].dowrite) |
| 384 | continue; |
| 385 | crcstart = s3crc[i].addr; |
| 386 | crcend = s3crc[i].addr + s3crc[i].len; |
| 387 | /* Find chunk */ |
| 388 | for (c = 0; c < nfchunks; c++) { |
| 389 | cstart = fchunk[c].addr; |
| 390 | cend = fchunk[c].addr + fchunk[c].len; |
| 391 | /* the line below does an address & len match search */ |
| 392 | /* unfortunately, I've found that the len fields of */ |
| 393 | /* some crc records don't match with the length of */ |
| 394 | /* the actual data, so we're not checking right */ |
| 395 | /* now */ |
| 396 | /* if ( crcstart-2 >= cstart && crcend <= cend ) break; */ |
| 397 | |
| 398 | /* note the -2 below, it's to make sure the chunk has */ |
| 399 | /* space for the CRC value */ |
| 400 | if (crcstart - 2 >= cstart && crcstart < cend) |
| 401 | break; |
| 402 | } |
| 403 | if (c >= nfchunks) { |
| 404 | printk(KERN_ERR |
| 405 | "Failed to find chunk for " |
| 406 | "crcrec[%d], addr=0x%06x len=%d , " |
| 407 | "aborting crc.\n", |
| 408 | i, s3crc[i].addr, s3crc[i].len); |
| 409 | return 1; |
| 410 | } |
| 411 | |
| 412 | /* Insert crc */ |
| 413 | pr_debug("Adding crc @ 0x%06x\n", s3crc[i].addr - 2); |
| 414 | chunkoff = crcstart - cstart - 2; |
| 415 | dest = fchunk[c].data + chunkoff; |
| 416 | *dest = 0xde; |
| 417 | *(dest + 1) = 0xc0; |
| 418 | |
| 419 | } |
| 420 | return result; |
| 421 | } |
| 422 | |
| 423 | /*---------------------------------------------------------------- |
| 424 | * free_chunks |
| 425 | * |
| 426 | * Clears the chunklist data structures in preparation for a new file. |
| 427 | * |
| 428 | * Arguments: |
| 429 | * none |
| 430 | * |
| 431 | * Returns: |
| 432 | * nothing |
| 433 | ----------------------------------------------------------------*/ |
| 434 | void free_chunks(imgchunk_t *fchunk, unsigned int *nfchunks) |
| 435 | { |
| 436 | int i; |
| 437 | for (i = 0; i < *nfchunks; i++) { |
| 438 | if (fchunk[i].data != NULL) |
| 439 | kfree(fchunk[i].data); |
| 440 | } |
| 441 | *nfchunks = 0; |
| 442 | memset(fchunk, 0, sizeof(*fchunk)); |
| 443 | |
| 444 | } |
| 445 | |
| 446 | /*---------------------------------------------------------------- |
| 447 | * free_srecs |
| 448 | * |
| 449 | * Clears the srec data structures in preparation for a new file. |
| 450 | * |
| 451 | * Arguments: |
| 452 | * none |
| 453 | * |
| 454 | * Returns: |
| 455 | * nothing |
| 456 | ----------------------------------------------------------------*/ |
| 457 | void free_srecs(void) |
| 458 | { |
| 459 | ns3data = 0; |
| 460 | memset(s3data, 0, sizeof(s3data)); |
| 461 | ns3plug = 0; |
| 462 | memset(s3plug, 0, sizeof(s3plug)); |
| 463 | ns3crc = 0; |
| 464 | memset(s3crc, 0, sizeof(s3crc)); |
| 465 | ns3info = 0; |
| 466 | memset(s3info, 0, sizeof(s3info)); |
| 467 | startaddr = 0; |
| 468 | } |
| 469 | |
| 470 | /*---------------------------------------------------------------- |
| 471 | * mkimage |
| 472 | * |
| 473 | * Scans the currently loaded set of S records for data residing |
| 474 | * in contiguous memory regions. Each contiguous region is then |
| 475 | * made into a 'chunk'. This function assumes that we're building |
| 476 | * a new chunk list. Assumes the s3data items are in sorted order. |
| 477 | * |
| 478 | * Arguments: none |
| 479 | * |
| 480 | * Returns: |
| 481 | * 0 - success |
| 482 | * ~0 - failure (probably an errno) |
| 483 | ----------------------------------------------------------------*/ |
| 484 | int mkimage(imgchunk_t *clist, unsigned int *ccnt) |
| 485 | { |
| 486 | int result = 0; |
| 487 | int i; |
| 488 | int j; |
| 489 | int currchunk = 0; |
| 490 | u32 nextaddr = 0; |
| 491 | u32 s3start; |
| 492 | u32 s3end; |
| 493 | u32 cstart = 0; |
| 494 | u32 cend; |
| 495 | u32 coffset; |
| 496 | |
| 497 | /* There may already be data in the chunklist */ |
| 498 | *ccnt = 0; |
| 499 | |
| 500 | /* Establish the location and size of each chunk */ |
| 501 | for (i = 0; i < ns3data; i++) { |
| 502 | if (s3data[i].addr == nextaddr) { |
| 503 | /* existing chunk, grow it */ |
| 504 | clist[currchunk].len += s3data[i].len; |
| 505 | nextaddr += s3data[i].len; |
| 506 | } else { |
| 507 | /* New chunk */ |
| 508 | (*ccnt)++; |
| 509 | currchunk = *ccnt - 1; |
| 510 | clist[currchunk].addr = s3data[i].addr; |
| 511 | clist[currchunk].len = s3data[i].len; |
| 512 | nextaddr = s3data[i].addr + s3data[i].len; |
| 513 | /* Expand the chunk if there is a CRC record at */ |
| 514 | /* their beginning bound */ |
| 515 | for (j = 0; j < ns3crc; j++) { |
| 516 | if (s3crc[j].dowrite && |
| 517 | s3crc[j].addr == clist[currchunk].addr) { |
| 518 | clist[currchunk].addr -= 2; |
| 519 | clist[currchunk].len += 2; |
| 520 | } |
| 521 | } |
| 522 | } |
| 523 | } |
| 524 | |
| 525 | /* We're currently assuming there aren't any overlapping chunks */ |
| 526 | /* if this proves false, we'll need to add code to coalesce. */ |
| 527 | |
| 528 | /* Allocate buffer space for chunks */ |
| 529 | for (i = 0; i < *ccnt; i++) { |
| 530 | clist[i].data = kmalloc(clist[i].len, GFP_KERNEL); |
| 531 | if (clist[i].data == NULL) { |
| 532 | printk(KERN_ERR |
| 533 | "failed to allocate image space, exitting.\n"); |
| 534 | return 1; |
| 535 | } |
| 536 | memset(clist[i].data, 0, clist[i].len); |
| 537 | pr_debug("chunk[%d]: addr=0x%06x len=%d\n", |
| 538 | i, clist[i].addr, clist[i].len); |
| 539 | } |
| 540 | |
| 541 | /* Copy srec data to chunks */ |
| 542 | for (i = 0; i < ns3data; i++) { |
| 543 | s3start = s3data[i].addr; |
| 544 | s3end = s3start + s3data[i].len - 1; |
| 545 | for (j = 0; j < *ccnt; j++) { |
| 546 | cstart = clist[j].addr; |
| 547 | cend = cstart + clist[j].len - 1; |
| 548 | if (s3start >= cstart && s3end <= cend) |
| 549 | break; |
| 550 | } |
| 551 | if (((unsigned int)j) >= (*ccnt)) { |
| 552 | printk(KERN_ERR |
| 553 | "s3rec(a=0x%06x,l=%d), no chunk match, exiting.\n", |
| 554 | s3start, s3data[i].len); |
| 555 | return 1; |
| 556 | } |
| 557 | coffset = s3start - cstart; |
| 558 | memcpy(clist[j].data + coffset, s3data[i].data, s3data[i].len); |
| 559 | } |
| 560 | |
| 561 | return result; |
| 562 | } |
| 563 | |
| 564 | /*---------------------------------------------------------------- |
| 565 | * mkpdrlist |
| 566 | * |
| 567 | * Reads a raw PDA and builds an array of pdrec_t structures. |
| 568 | * |
| 569 | * Arguments: |
| 570 | * pda buffer containing raw PDA bytes |
| 571 | * pdrec ptr to an array of pdrec_t's. Will be filled on exit. |
| 572 | * nrec ptr to a variable that will contain the count of PDRs |
| 573 | * |
| 574 | * Returns: |
| 575 | * 0 - success |
| 576 | * ~0 - failure (probably an errno) |
| 577 | ----------------------------------------------------------------*/ |
| 578 | int mkpdrlist(pda_t *pda) |
| 579 | { |
| 580 | int result = 0; |
| 581 | u16 *pda16 = (u16 *) pda->buf; |
| 582 | int curroff; /* in 'words' */ |
| 583 | |
| 584 | pda->nrec = 0; |
| 585 | curroff = 0; |
| 586 | while (curroff < (HFA384x_PDA_LEN_MAX / 2) && |
| 587 | le16_to_cpu(pda16[curroff + 1]) != HFA384x_PDR_END_OF_PDA) { |
| 588 | pda->rec[pda->nrec] = (hfa384x_pdrec_t *) &(pda16[curroff]); |
| 589 | |
| 590 | if (le16_to_cpu(pda->rec[pda->nrec]->code) == HFA384x_PDR_NICID) { |
| 591 | memcpy(&nicid, &pda->rec[pda->nrec]->data.nicid, |
| 592 | sizeof(nicid)); |
| 593 | nicid.id = le16_to_cpu(nicid.id); |
| 594 | nicid.variant = le16_to_cpu(nicid.variant); |
| 595 | nicid.major = le16_to_cpu(nicid.major); |
| 596 | nicid.minor = le16_to_cpu(nicid.minor); |
| 597 | } |
| 598 | if (le16_to_cpu(pda->rec[pda->nrec]->code) == |
| 599 | HFA384x_PDR_MFISUPRANGE) { |
| 600 | memcpy(&rfid, &pda->rec[pda->nrec]->data.mfisuprange, |
| 601 | sizeof(rfid)); |
| 602 | rfid.id = le16_to_cpu(rfid.id); |
| 603 | rfid.variant = le16_to_cpu(rfid.variant); |
| 604 | rfid.bottom = le16_to_cpu(rfid.bottom); |
| 605 | rfid.top = le16_to_cpu(rfid.top); |
| 606 | } |
| 607 | if (le16_to_cpu(pda->rec[pda->nrec]->code) == |
| 608 | HFA384x_PDR_CFISUPRANGE) { |
| 609 | memcpy(&macid, &pda->rec[pda->nrec]->data.cfisuprange, |
| 610 | sizeof(macid)); |
| 611 | macid.id = le16_to_cpu(macid.id); |
| 612 | macid.variant = le16_to_cpu(macid.variant); |
| 613 | macid.bottom = le16_to_cpu(macid.bottom); |
| 614 | macid.top = le16_to_cpu(macid.top); |
| 615 | } |
| 616 | |
| 617 | (pda->nrec)++; |
| 618 | curroff += le16_to_cpu(pda16[curroff]) + 1; |
| 619 | |
| 620 | } |
| 621 | if (curroff >= (HFA384x_PDA_LEN_MAX / 2)) { |
| 622 | printk(KERN_ERR |
| 623 | "no end record found or invalid lengths in " |
| 624 | "PDR data, exiting. %x %d\n", curroff, pda->nrec); |
| 625 | return 1; |
| 626 | } |
| 627 | if (le16_to_cpu(pda16[curroff + 1]) == HFA384x_PDR_END_OF_PDA) { |
| 628 | pda->rec[pda->nrec] = (hfa384x_pdrec_t *) &(pda16[curroff]); |
| 629 | (pda->nrec)++; |
| 630 | } |
| 631 | return result; |
| 632 | } |
| 633 | |
| 634 | /*---------------------------------------------------------------- |
| 635 | * plugimage |
| 636 | * |
| 637 | * Plugs the given image using the given plug records from the given |
| 638 | * PDA and filename. |
| 639 | * |
| 640 | * Arguments: |
| 641 | * fchunk Array of image chunks |
| 642 | * nfchunks Number of image chunks |
| 643 | * s3plug Array of plug records |
| 644 | * ns3plug Number of plug records |
| 645 | * pda Current pda data |
| 646 | * |
| 647 | * Returns: |
| 648 | * 0 success |
| 649 | * ~0 failure |
| 650 | ----------------------------------------------------------------*/ |
| 651 | int plugimage(imgchunk_t *fchunk, unsigned int nfchunks, |
| 652 | s3plugrec_t *s3plug, unsigned int ns3plug, pda_t * pda) |
| 653 | { |
| 654 | int result = 0; |
| 655 | int i; /* plug index */ |
| 656 | int j; /* index of PDR or -1 if fname plug */ |
| 657 | int c; /* chunk index */ |
| 658 | u32 pstart; |
| 659 | u32 pend; |
| 660 | u32 cstart = 0; |
| 661 | u32 cend; |
| 662 | u32 chunkoff; |
| 663 | u8 *dest; |
| 664 | |
| 665 | /* for each plug record */ |
| 666 | for (i = 0; i < ns3plug; i++) { |
| 667 | pstart = s3plug[i].addr; |
| 668 | pend = s3plug[i].addr + s3plug[i].len; |
| 669 | /* find the matching PDR (or filename) */ |
| 670 | if (s3plug[i].itemcode != 0xffffffffUL) { /* not filename */ |
| 671 | for (j = 0; j < pda->nrec; j++) { |
| 672 | if (s3plug[i].itemcode == |
| 673 | le16_to_cpu(pda->rec[j]->code)) |
| 674 | break; |
| 675 | } |
| 676 | } else { |
| 677 | j = -1; |
| 678 | } |
| 679 | if (j >= pda->nrec && j != -1) { /* if no matching PDR, fail */ |
| 680 | printk(KERN_WARNING |
| 681 | "warning: Failed to find PDR for " |
| 682 | "plugrec 0x%04x.\n", s3plug[i].itemcode); |
| 683 | continue; /* and move on to the next PDR */ |
| 684 | #if 0 |
| 685 | /* MSM: They swear that unless it's the MAC address, |
| 686 | * the serial number, or the TX calibration records, |
| 687 | * then there's reasonable defaults in the f/w |
| 688 | * image. Therefore, missing PDRs in the card |
| 689 | * should only be a warning, not fatal. |
| 690 | * TODO: add fatals for the PDRs mentioned above. |
| 691 | */ |
| 692 | result = 1; |
| 693 | continue; |
| 694 | #endif |
| 695 | } |
| 696 | |
| 697 | /* Validate plug len against PDR len */ |
| 698 | if (j != -1 && s3plug[i].len < le16_to_cpu(pda->rec[j]->len)) { |
| 699 | printk(KERN_ERR |
| 700 | "error: Plug vs. PDR len mismatch for " |
| 701 | "plugrec 0x%04x, abort plugging.\n", |
| 702 | s3plug[i].itemcode); |
| 703 | result = 1; |
| 704 | continue; |
| 705 | } |
| 706 | |
| 707 | /* Validate plug address against chunk data and identify chunk */ |
| 708 | for (c = 0; c < nfchunks; c++) { |
| 709 | cstart = fchunk[c].addr; |
| 710 | cend = fchunk[c].addr + fchunk[c].len; |
| 711 | if (pstart >= cstart && pend <= cend) |
| 712 | break; |
| 713 | } |
| 714 | if (c >= nfchunks) { |
| 715 | printk(KERN_ERR |
| 716 | "error: Failed to find image chunk for " |
| 717 | "plugrec 0x%04x.\n", s3plug[i].itemcode); |
| 718 | result = 1; |
| 719 | continue; |
| 720 | } |
| 721 | |
| 722 | /* Plug data */ |
| 723 | chunkoff = pstart - cstart; |
| 724 | dest = fchunk[c].data + chunkoff; |
| 725 | pr_debug("Plugging item 0x%04x @ 0x%06x, len=%d, " |
| 726 | "cnum=%d coff=0x%06x\n", |
| 727 | s3plug[i].itemcode, pstart, s3plug[i].len, |
| 728 | c, chunkoff); |
| 729 | |
| 730 | if (j == -1) { /* plug the filename */ |
| 731 | memset(dest, 0, s3plug[i].len); |
| 732 | strncpy(dest, PRISM2_USB_FWFILE, s3plug[i].len - 1); |
| 733 | } else { /* plug a PDR */ |
| 734 | memcpy(dest, &(pda->rec[j]->data), s3plug[i].len); |
| 735 | } |
| 736 | } |
| 737 | return result; |
| 738 | |
| 739 | } |
| 740 | |
| 741 | /*---------------------------------------------------------------- |
| 742 | * read_cardpda |
| 743 | * |
| 744 | * Sends the command for the driver to read the pda from the card |
| 745 | * named in the device variable. Upon success, the card pda is |
| 746 | * stored in the "cardpda" variables. Note that the pda structure |
| 747 | * is considered 'well formed' after this function. That means |
| 748 | * that the nrecs is valid, the rec array has been set up, and there's |
| 749 | * a valid PDAEND record in the raw PDA data. |
| 750 | * |
| 751 | * Arguments: |
| 752 | * pda pda structure |
| 753 | * wlandev device |
| 754 | * |
| 755 | * Returns: |
| 756 | * 0 - success |
| 757 | * ~0 - failure (probably an errno) |
| 758 | ----------------------------------------------------------------*/ |
| 759 | int read_cardpda(pda_t *pda, wlandevice_t *wlandev) |
| 760 | { |
| 761 | int result = 0; |
| 762 | p80211msg_p2req_readpda_t msg; |
| 763 | |
| 764 | /* set up the msg */ |
| 765 | msg.msgcode = DIDmsg_p2req_readpda; |
| 766 | msg.msglen = sizeof(msg); |
| 767 | strcpy(msg.devname, wlandev->name); |
| 768 | msg.pda.did = DIDmsg_p2req_readpda_pda; |
| 769 | msg.pda.len = HFA384x_PDA_LEN_MAX; |
| 770 | msg.pda.status = P80211ENUM_msgitem_status_no_value; |
| 771 | msg.resultcode.did = DIDmsg_p2req_readpda_resultcode; |
| 772 | msg.resultcode.len = sizeof(u32); |
| 773 | msg.resultcode.status = P80211ENUM_msgitem_status_no_value; |
| 774 | |
| 775 | if (prism2mgmt_readpda(wlandev, &msg) != 0) { |
| 776 | /* prism2mgmt_readpda prints an errno if appropriate */ |
| 777 | result = -1; |
| 778 | } else if (msg.resultcode.data == P80211ENUM_resultcode_success) { |
| 779 | memcpy(pda->buf, msg.pda.data, HFA384x_PDA_LEN_MAX); |
| 780 | result = mkpdrlist(pda); |
| 781 | } else { |
| 782 | /* resultcode must've been something other than success */ |
| 783 | result = -1; |
| 784 | } |
| 785 | |
| 786 | return result; |
| 787 | } |
| 788 | |
| 789 | /*---------------------------------------------------------------- |
| 790 | * read_fwfile |
| 791 | * |
| 792 | * Reads the given fw file which should have been compiled from an srec |
| 793 | * file. Each record in the fw file will either be a plain data record, |
| 794 | * a start address record, or other records used for plugging. |
| 795 | * |
| 796 | * Note that data records are expected to be sorted into |
| 797 | * ascending address order in the fw file. |
| 798 | * |
| 799 | * Note also that the start address record, originally an S7 record in |
| 800 | * the srec file, is expected in the fw file to be like a data record but |
| 801 | * with a certain address to make it identiable. |
| 802 | * |
| 803 | * Here's the SREC format that the fw should have come from: |
| 804 | * S[37]nnaaaaaaaaddd...dddcc |
| 805 | * |
| 806 | * nn - number of bytes starting with the address field |
| 807 | * aaaaaaaa - address in readable (or big endian) format |
| 808 | * dd....dd - 0-245 data bytes (two chars per byte) |
| 809 | * cc - checksum |
| 810 | * |
| 811 | * The S7 record's (there should be only one) address value gets |
| 812 | * converted to an S3 record with address of 0xff400000, with the |
| 813 | * start address being stored as a 4 byte data word. That address is |
| 814 | * the start execution address used for RAM downloads. |
| 815 | * |
| 816 | * The S3 records have a collection of subformats indicated by the |
| 817 | * value of aaaaaaaa: |
| 818 | * 0xff000000 - Plug record, data field format: |
| 819 | * xxxxxxxxaaaaaaaassssssss |
| 820 | * x - PDR code number (little endian) |
| 821 | * a - Address in load image to plug (little endian) |
| 822 | * s - Length of plug data area (little endian) |
| 823 | * |
| 824 | * 0xff100000 - CRC16 generation record, data field format: |
| 825 | * aaaaaaaassssssssbbbbbbbb |
| 826 | * a - Start address for CRC calculation (little endian) |
| 827 | * s - Length of data to calculate over (little endian) |
| 828 | * b - Boolean, true=write crc, false=don't write |
| 829 | * |
| 830 | * 0xff200000 - Info record, data field format: |
| 831 | * ssssttttdd..dd |
| 832 | * s - Size in words (little endian) |
| 833 | * t - Info type (little endian), see #defines and |
| 834 | * s3inforec_t for details about types. |
| 835 | * d - (s - 1) little endian words giving the contents of |
| 836 | * the given info type. |
| 837 | * |
| 838 | * 0xff400000 - Start address record, data field format: |
| 839 | * aaaaaaaa |
| 840 | * a - Address in load image to plug (little endian) |
| 841 | * |
| 842 | * Arguments: |
| 843 | * record firmware image (ihex record structure) in kernel memory |
| 844 | * |
| 845 | * Returns: |
| 846 | * 0 - success |
| 847 | * ~0 - failure (probably an errno) |
| 848 | ----------------------------------------------------------------*/ |
| 849 | int read_fwfile(const struct ihex_binrec *record) |
| 850 | { |
| 851 | int i; |
| 852 | int rcnt = 0; |
| 853 | u16 *tmpinfo; |
| 854 | u16 *ptr16; |
| 855 | u32 *ptr32, len, addr; |
| 856 | |
| 857 | pr_debug("Reading fw file ...\n"); |
| 858 | |
| 859 | while (record) { |
| 860 | |
| 861 | rcnt++; |
| 862 | |
| 863 | len = be16_to_cpu(record->len); |
| 864 | addr = be32_to_cpu(record->addr); |
| 865 | |
| 866 | /* Point into data for different word lengths */ |
| 867 | ptr32 = (u32 *) record->data; |
| 868 | ptr16 = (u16 *) record->data; |
| 869 | |
| 870 | /* parse what was an S3 srec and put it in the right array */ |
| 871 | switch (addr) { |
| 872 | case S3ADDR_START: |
| 873 | startaddr = *ptr32; |
| 874 | pr_debug(" S7 start addr, record=%d " |
| 875 | " addr=0x%08x\n", |
| 876 | rcnt, |
| 877 | startaddr); |
| 878 | break; |
| 879 | case S3ADDR_PLUG: |
| 880 | s3plug[ns3plug].itemcode = *ptr32; |
| 881 | s3plug[ns3plug].addr = *(ptr32 + 1); |
| 882 | s3plug[ns3plug].len = *(ptr32 + 2); |
| 883 | |
| 884 | pr_debug(" S3 plugrec, record=%d " |
| 885 | "itemcode=0x%08x addr=0x%08x len=%d\n", |
| 886 | rcnt, |
| 887 | s3plug[ns3plug].itemcode, |
| 888 | s3plug[ns3plug].addr, |
| 889 | s3plug[ns3plug].len); |
| 890 | |
| 891 | ns3plug++; |
| 892 | if (ns3plug == S3PLUG_MAX) { |
| 893 | printk(KERN_ERR "S3 plugrec limit reached - aborting\n"); |
| 894 | return 1; |
| 895 | } |
| 896 | break; |
| 897 | case S3ADDR_CRC: |
| 898 | s3crc[ns3crc].addr = *ptr32; |
| 899 | s3crc[ns3crc].len = *(ptr32 + 1); |
| 900 | s3crc[ns3crc].dowrite = *(ptr32 + 2); |
| 901 | |
| 902 | pr_debug(" S3 crcrec, record=%d " |
| 903 | "addr=0x%08x len=%d write=0x%08x\n", |
| 904 | rcnt, |
| 905 | s3crc[ns3crc].addr, |
| 906 | s3crc[ns3crc].len, |
| 907 | s3crc[ns3crc].dowrite); |
| 908 | ns3crc++; |
| 909 | if (ns3crc == S3CRC_MAX) { |
| 910 | printk(KERN_ERR "S3 crcrec limit reached - aborting\n"); |
| 911 | return 1; |
| 912 | } |
| 913 | break; |
| 914 | case S3ADDR_INFO: |
| 915 | s3info[ns3info].len = *ptr16; |
| 916 | s3info[ns3info].type = *(ptr16 + 1); |
| 917 | |
| 918 | pr_debug(" S3 inforec, record=%d " |
| 919 | "len=0x%04x type=0x%04x\n", |
| 920 | rcnt, |
| 921 | s3info[ns3info].len, |
| 922 | s3info[ns3info].type); |
| 923 | if (((s3info[ns3info].len - 1) * sizeof(u16)) > sizeof(s3info[ns3info].info)) { |
| 924 | printk(KERN_ERR " S3 inforec length too long - aborting\n"); |
| 925 | return 1; |
| 926 | } |
| 927 | |
| 928 | tmpinfo = (u16 *)&(s3info[ns3info].info.version); |
| 929 | pr_debug(" info="); |
| 930 | for (i = 0; i < s3info[ns3info].len - 1; i++) { |
| 931 | tmpinfo[i] = *(ptr16 + 2 + i); |
| 932 | pr_debug("%04x ", tmpinfo[i]); |
| 933 | } |
| 934 | pr_debug("\n"); |
| 935 | |
| 936 | ns3info++; |
| 937 | if (ns3info == S3INFO_MAX) { |
| 938 | printk(KERN_ERR "S3 inforec limit reached - aborting\n"); |
| 939 | return 1; |
| 940 | } |
| 941 | break; |
| 942 | default: /* Data record */ |
| 943 | s3data[ns3data].addr = addr; |
| 944 | s3data[ns3data].len = len; |
| 945 | s3data[ns3data].data = (uint8_t *) record->data; |
| 946 | ns3data++; |
| 947 | if (ns3data == S3DATA_MAX) { |
| 948 | printk(KERN_ERR "S3 datarec limit reached - aborting\n"); |
| 949 | return 1; |
| 950 | } |
| 951 | break; |
| 952 | } |
| 953 | record = ihex_next_binrec(record); |
| 954 | } |
| 955 | return 0; |
| 956 | } |
| 957 | |
| 958 | /*---------------------------------------------------------------- |
| 959 | * writeimage |
| 960 | * |
| 961 | * Takes the chunks, builds p80211 messages and sends them down |
| 962 | * to the driver for writing to the card. |
| 963 | * |
| 964 | * Arguments: |
| 965 | * wlandev device |
| 966 | * fchunk Array of image chunks |
| 967 | * nfchunks Number of image chunks |
| 968 | * |
| 969 | * Returns: |
| 970 | * 0 success |
| 971 | * ~0 failure |
| 972 | ----------------------------------------------------------------*/ |
| 973 | int writeimage(wlandevice_t *wlandev, imgchunk_t *fchunk, |
| 974 | unsigned int nfchunks) |
| 975 | { |
| 976 | int result = 0; |
| 977 | p80211msg_p2req_ramdl_state_t rstatemsg; |
| 978 | p80211msg_p2req_ramdl_write_t rwritemsg; |
| 979 | p80211msg_t *msgp; |
| 980 | u32 resultcode; |
| 981 | int i; |
| 982 | int j; |
| 983 | unsigned int nwrites; |
| 984 | u32 curroff; |
| 985 | u32 currlen; |
| 986 | u32 currdaddr; |
| 987 | |
| 988 | /* Initialize the messages */ |
| 989 | memset(&rstatemsg, 0, sizeof(rstatemsg)); |
| 990 | strcpy(rstatemsg.devname, wlandev->name); |
| 991 | rstatemsg.msgcode = DIDmsg_p2req_ramdl_state; |
| 992 | rstatemsg.msglen = sizeof(rstatemsg); |
| 993 | rstatemsg.enable.did = DIDmsg_p2req_ramdl_state_enable; |
| 994 | rstatemsg.exeaddr.did = DIDmsg_p2req_ramdl_state_exeaddr; |
| 995 | rstatemsg.resultcode.did = DIDmsg_p2req_ramdl_state_resultcode; |
| 996 | rstatemsg.enable.status = P80211ENUM_msgitem_status_data_ok; |
| 997 | rstatemsg.exeaddr.status = P80211ENUM_msgitem_status_data_ok; |
| 998 | rstatemsg.resultcode.status = P80211ENUM_msgitem_status_no_value; |
| 999 | rstatemsg.enable.len = sizeof(u32); |
| 1000 | rstatemsg.exeaddr.len = sizeof(u32); |
| 1001 | rstatemsg.resultcode.len = sizeof(u32); |
| 1002 | |
| 1003 | memset(&rwritemsg, 0, sizeof(rwritemsg)); |
| 1004 | strcpy(rwritemsg.devname, wlandev->name); |
| 1005 | rwritemsg.msgcode = DIDmsg_p2req_ramdl_write; |
| 1006 | rwritemsg.msglen = sizeof(rwritemsg); |
| 1007 | rwritemsg.addr.did = DIDmsg_p2req_ramdl_write_addr; |
| 1008 | rwritemsg.len.did = DIDmsg_p2req_ramdl_write_len; |
| 1009 | rwritemsg.data.did = DIDmsg_p2req_ramdl_write_data; |
| 1010 | rwritemsg.resultcode.did = DIDmsg_p2req_ramdl_write_resultcode; |
| 1011 | rwritemsg.addr.status = P80211ENUM_msgitem_status_data_ok; |
| 1012 | rwritemsg.len.status = P80211ENUM_msgitem_status_data_ok; |
| 1013 | rwritemsg.data.status = P80211ENUM_msgitem_status_data_ok; |
| 1014 | rwritemsg.resultcode.status = P80211ENUM_msgitem_status_no_value; |
| 1015 | rwritemsg.addr.len = sizeof(u32); |
| 1016 | rwritemsg.len.len = sizeof(u32); |
| 1017 | rwritemsg.data.len = WRITESIZE_MAX; |
| 1018 | rwritemsg.resultcode.len = sizeof(u32); |
| 1019 | |
| 1020 | /* Send xxx_state(enable) */ |
| 1021 | pr_debug("Sending dl_state(enable) message.\n"); |
| 1022 | rstatemsg.enable.data = P80211ENUM_truth_true; |
| 1023 | rstatemsg.exeaddr.data = startaddr; |
| 1024 | |
| 1025 | msgp = (p80211msg_t *) &rstatemsg; |
| 1026 | result = prism2mgmt_ramdl_state(wlandev, msgp); |
| 1027 | if (result) { |
| 1028 | printk(KERN_ERR |
| 1029 | "writeimage state enable failed w/ result=%d, " |
| 1030 | "aborting download\n", result); |
| 1031 | return result; |
| 1032 | } |
| 1033 | resultcode = rstatemsg.resultcode.data; |
| 1034 | if (resultcode != P80211ENUM_resultcode_success) { |
| 1035 | printk(KERN_ERR |
| 1036 | "writeimage()->xxxdl_state msg indicates failure, " |
| 1037 | "w/ resultcode=%d, aborting download.\n", resultcode); |
| 1038 | return 1; |
| 1039 | } |
| 1040 | |
| 1041 | /* Now, loop through the data chunks and send WRITESIZE_MAX data */ |
| 1042 | for (i = 0; i < nfchunks; i++) { |
| 1043 | nwrites = fchunk[i].len / WRITESIZE_MAX; |
| 1044 | nwrites += (fchunk[i].len % WRITESIZE_MAX) ? 1 : 0; |
| 1045 | curroff = 0; |
| 1046 | for (j = 0; j < nwrites; j++) { |
| 1047 | currlen = |
| 1048 | (fchunk[i].len - (WRITESIZE_MAX * j)) > |
| 1049 | WRITESIZE_MAX ? WRITESIZE_MAX : (fchunk[i].len - |
| 1050 | (WRITESIZE_MAX * |
| 1051 | j)); |
| 1052 | curroff = j * WRITESIZE_MAX; |
| 1053 | currdaddr = fchunk[i].addr + curroff; |
| 1054 | /* Setup the message */ |
| 1055 | rwritemsg.addr.data = currdaddr; |
| 1056 | rwritemsg.len.data = currlen; |
| 1057 | memcpy(rwritemsg.data.data, |
| 1058 | fchunk[i].data + curroff, currlen); |
| 1059 | |
| 1060 | /* Send flashdl_write(pda) */ |
| 1061 | pr_debug |
| 1062 | ("Sending xxxdl_write message addr=%06x len=%d.\n", |
| 1063 | currdaddr, currlen); |
| 1064 | |
| 1065 | msgp = (p80211msg_t *) &rwritemsg; |
| 1066 | result = prism2mgmt_ramdl_write(wlandev, msgp); |
| 1067 | |
| 1068 | /* Check the results */ |
| 1069 | if (result) { |
| 1070 | printk(KERN_ERR |
| 1071 | "writeimage chunk write failed w/ result=%d, " |
| 1072 | "aborting download\n", result); |
| 1073 | return result; |
| 1074 | } |
| 1075 | resultcode = rstatemsg.resultcode.data; |
| 1076 | if (resultcode != P80211ENUM_resultcode_success) { |
| 1077 | printk(KERN_ERR |
| 1078 | "writeimage()->xxxdl_write msg indicates failure, " |
| 1079 | "w/ resultcode=%d, aborting download.\n", |
| 1080 | resultcode); |
| 1081 | return 1; |
| 1082 | } |
| 1083 | |
| 1084 | } |
| 1085 | } |
| 1086 | |
| 1087 | /* Send xxx_state(disable) */ |
| 1088 | pr_debug("Sending dl_state(disable) message.\n"); |
| 1089 | rstatemsg.enable.data = P80211ENUM_truth_false; |
| 1090 | rstatemsg.exeaddr.data = 0; |
| 1091 | |
| 1092 | msgp = (p80211msg_t *) &rstatemsg; |
| 1093 | result = prism2mgmt_ramdl_state(wlandev, msgp); |
| 1094 | if (result) { |
| 1095 | printk(KERN_ERR |
| 1096 | "writeimage state disable failed w/ result=%d, " |
| 1097 | "aborting download\n", result); |
| 1098 | return result; |
| 1099 | } |
| 1100 | resultcode = rstatemsg.resultcode.data; |
| 1101 | if (resultcode != P80211ENUM_resultcode_success) { |
| 1102 | printk(KERN_ERR |
| 1103 | "writeimage()->xxxdl_state msg indicates failure, " |
| 1104 | "w/ resultcode=%d, aborting download.\n", resultcode); |
| 1105 | return 1; |
| 1106 | } |
| 1107 | return result; |
| 1108 | } |
| 1109 | |
| 1110 | int validate_identity(void) |
| 1111 | { |
| 1112 | int i; |
| 1113 | int result = 1; |
| 1114 | int trump = 0; |
| 1115 | |
| 1116 | pr_debug("NIC ID: %#x v%d.%d.%d\n", |
| 1117 | nicid.id, nicid.major, nicid.minor, nicid.variant); |
| 1118 | pr_debug("MFI ID: %#x v%d %d->%d\n", |
| 1119 | rfid.id, rfid.variant, rfid.bottom, rfid.top); |
| 1120 | pr_debug("CFI ID: %#x v%d %d->%d\n", |
| 1121 | macid.id, macid.variant, macid.bottom, macid.top); |
| 1122 | pr_debug("PRI ID: %#x v%d %d->%d\n", |
| 1123 | priid.id, priid.variant, priid.bottom, priid.top); |
| 1124 | |
| 1125 | for (i = 0; i < ns3info; i++) { |
| 1126 | switch (s3info[i].type) { |
| 1127 | case 1: |
| 1128 | pr_debug("Version: ID %#x %d.%d.%d\n", |
| 1129 | s3info[i].info.version.id, |
| 1130 | s3info[i].info.version.major, |
| 1131 | s3info[i].info.version.minor, |
| 1132 | s3info[i].info.version.variant); |
| 1133 | break; |
| 1134 | case 2: |
| 1135 | pr_debug("Compat: Role %#x Id %#x v%d %d->%d\n", |
| 1136 | s3info[i].info.compat.role, |
| 1137 | s3info[i].info.compat.id, |
| 1138 | s3info[i].info.compat.variant, |
| 1139 | s3info[i].info.compat.bottom, |
| 1140 | s3info[i].info.compat.top); |
| 1141 | |
| 1142 | /* MAC compat range */ |
| 1143 | if ((s3info[i].info.compat.role == 1) && |
| 1144 | (s3info[i].info.compat.id == 2)) { |
| 1145 | if (s3info[i].info.compat.variant != |
| 1146 | macid.variant) { |
| 1147 | result = 2; |
| 1148 | } |
| 1149 | } |
| 1150 | |
| 1151 | /* PRI compat range */ |
| 1152 | if ((s3info[i].info.compat.role == 1) && |
| 1153 | (s3info[i].info.compat.id == 3)) { |
| 1154 | if ((s3info[i].info.compat.bottom > priid.top) |
| 1155 | || (s3info[i].info.compat.top < |
| 1156 | priid.bottom)) { |
| 1157 | result = 3; |
| 1158 | } |
| 1159 | } |
| 1160 | /* SEC compat range */ |
| 1161 | if ((s3info[i].info.compat.role == 1) && |
| 1162 | (s3info[i].info.compat.id == 4)) { |
| 1163 | /* FIXME: isn't something missing here? */ |
| 1164 | } |
| 1165 | |
| 1166 | break; |
| 1167 | case 3: |
| 1168 | pr_debug("Seq: %#x\n", s3info[i].info.buildseq); |
| 1169 | |
| 1170 | break; |
| 1171 | case 4: |
| 1172 | pr_debug("Platform: ID %#x %d.%d.%d\n", |
| 1173 | s3info[i].info.version.id, |
| 1174 | s3info[i].info.version.major, |
| 1175 | s3info[i].info.version.minor, |
| 1176 | s3info[i].info.version.variant); |
| 1177 | |
| 1178 | if (nicid.id != s3info[i].info.version.id) |
| 1179 | continue; |
| 1180 | if (nicid.major != s3info[i].info.version.major) |
| 1181 | continue; |
| 1182 | if (nicid.minor != s3info[i].info.version.minor) |
| 1183 | continue; |
| 1184 | if ((nicid.variant != s3info[i].info.version.variant) && |
| 1185 | (nicid.id != 0x8008)) |
| 1186 | continue; |
| 1187 | |
| 1188 | trump = 1; |
| 1189 | break; |
| 1190 | case 0x8001: |
| 1191 | pr_debug("name inforec len %d\n", s3info[i].len); |
| 1192 | |
| 1193 | break; |
| 1194 | default: |
| 1195 | pr_debug("Unknown inforec type %d\n", s3info[i].type); |
| 1196 | } |
| 1197 | } |
| 1198 | /* walk through */ |
| 1199 | |
| 1200 | if (trump && (result != 2)) |
| 1201 | result = 0; |
| 1202 | return result; |
| 1203 | } |