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