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