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