Merge branch 'merge' of ssh://master.kernel.org/pub/scm/linux/kernel/git/jwboyer...
[linux-2.6] / drivers / net / wireless / hermes_dld.c
1 /*
2  * Hermes download helper driver.
3  *
4  * This could be entirely merged into hermes.c.
5  *
6  * I'm keeping it separate to minimise the amount of merging between
7  * kernel upgrades. It also means the memory overhead for drivers that
8  * don't need firmware download low.
9  *
10  * This driver:
11  *  - is capable of writing to the volatile area of the hermes device
12  *  - is currently not capable of writing to non-volatile areas
13  *  - provide helpers to identify and update plugin data
14  *  - is not capable of interpreting a fw image directly. That is up to
15  *    the main card driver.
16  *  - deals with Hermes I devices. It can probably be modified to deal
17  *    with Hermes II devices
18  *
19  * Copyright (C) 2007, David Kilroy
20  *
21  * Plug data code slightly modified from spectrum_cs driver
22  *    Copyright (C) 2002-2005 Pavel Roskin <proski@gnu.org>
23  * Portions based on information in wl_lkm_718 Agere driver
24  *    COPYRIGHT (C) 2001-2004 by Agere Systems Inc. All Rights Reserved
25  *
26  * The contents of this file are subject to the Mozilla Public License
27  * Version 1.1 (the "License"); you may not use this file except in
28  * compliance with the License. You may obtain a copy of the License
29  * at http://www.mozilla.org/MPL/
30  *
31  * Software distributed under the License is distributed on an "AS IS"
32  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
33  * the License for the specific language governing rights and
34  * limitations under the License.
35  *
36  * Alternatively, the contents of this file may be used under the
37  * terms of the GNU General Public License version 2 (the "GPL"), in
38  * which case the provisions of the GPL are applicable instead of the
39  * above.  If you wish to allow the use of your version of this file
40  * only under the terms of the GPL and not to allow others to use your
41  * version of this file under the MPL, indicate your decision by
42  * deleting the provisions above and replace them with the notice and
43  * other provisions required by the GPL.  If you do not delete the
44  * provisions above, a recipient may use your version of this file
45  * under either the MPL or the GPL.
46  */
47
48 #include <linux/module.h>
49 #include <linux/delay.h>
50 #include "hermes.h"
51 #include "hermes_dld.h"
52
53 MODULE_DESCRIPTION("Download helper for Lucent Hermes chipset");
54 MODULE_AUTHOR("David Kilroy <kilroyd@gmail.com>");
55 MODULE_LICENSE("Dual MPL/GPL");
56
57 #define PFX "hermes_dld: "
58
59 /*
60  * AUX port access.  To unlock the AUX port write the access keys to the
61  * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL
62  * register.  Then read it and make sure it's HERMES_AUX_ENABLED.
63  */
64 #define HERMES_AUX_ENABLE       0x8000  /* Enable auxiliary port access */
65 #define HERMES_AUX_DISABLE      0x4000  /* Disable to auxiliary port access */
66 #define HERMES_AUX_ENABLED      0xC000  /* Auxiliary port is open */
67 #define HERMES_AUX_DISABLED     0x0000  /* Auxiliary port is closed */
68
69 #define HERMES_AUX_PW0  0xFE01
70 #define HERMES_AUX_PW1  0xDC23
71 #define HERMES_AUX_PW2  0xBA45
72
73 /* HERMES_CMD_DOWNLD */
74 #define HERMES_PROGRAM_DISABLE             (0x0000 | HERMES_CMD_DOWNLD)
75 #define HERMES_PROGRAM_ENABLE_VOLATILE     (0x0100 | HERMES_CMD_DOWNLD)
76 #define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD)
77 #define HERMES_PROGRAM_NON_VOLATILE        (0x0300 | HERMES_CMD_DOWNLD)
78
79 /* End markers used in dblocks */
80 #define PDI_END         0x00000000      /* End of PDA */
81 #define BLOCK_END       0xFFFFFFFF      /* Last image block */
82 #define TEXT_END        0x1A            /* End of text header */
83
84 /*
85  * PDA == Production Data Area
86  *
87  * In principle, the max. size of the PDA is is 4096 words. Currently,
88  * however, only about 500 bytes of this area are used.
89  *
90  * Some USB implementations can't handle sizes in excess of 1016. Note
91  * that PDA is not actually used in those USB environments, but may be
92  * retrieved by common code.
93  */
94 #define MAX_PDA_SIZE    1000
95
96 /* Limit the amout we try to download in a single shot.
97  * Size is in bytes.
98  */
99 #define MAX_DL_SIZE 1024
100 #define LIMIT_PROGRAM_SIZE 0
101
102 /*
103  * The following structures have little-endian fields denoted by
104  * the leading underscore.  Don't access them directly - use inline
105  * functions defined below.
106  */
107
108 /*
109  * The binary image to be downloaded consists of series of data blocks.
110  * Each block has the following structure.
111  */
112 struct dblock {
113         __le32 addr;            /* adapter address where to write the block */
114         __le16 len;             /* length of the data only, in bytes */
115         char data[0];           /* data to be written */
116 } __attribute__ ((packed));
117
118 /*
119  * Plug Data References are located in in the image after the last data
120  * block.  They refer to areas in the adapter memory where the plug data
121  * items with matching ID should be written.
122  */
123 struct pdr {
124         __le32 id;              /* record ID */
125         __le32 addr;            /* adapter address where to write the data */
126         __le32 len;             /* expected length of the data, in bytes */
127         char next[0];           /* next PDR starts here */
128 } __attribute__ ((packed));
129
130 /*
131  * Plug Data Items are located in the EEPROM read from the adapter by
132  * primary firmware.  They refer to the device-specific data that should
133  * be plugged into the secondary firmware.
134  */
135 struct pdi {
136         __le16 len;             /* length of ID and data, in words */
137         __le16 id;              /* record ID */
138         char data[0];           /* plug data */
139 } __attribute__ ((packed));
140
141 /*** FW data block access functions ***/
142
143 static inline u32
144 dblock_addr(const struct dblock *blk)
145 {
146         return le32_to_cpu(blk->addr);
147 }
148
149 static inline u32
150 dblock_len(const struct dblock *blk)
151 {
152         return le16_to_cpu(blk->len);
153 }
154
155 /*** PDR Access functions ***/
156
157 static inline u32
158 pdr_id(const struct pdr *pdr)
159 {
160         return le32_to_cpu(pdr->id);
161 }
162
163 static inline u32
164 pdr_addr(const struct pdr *pdr)
165 {
166         return le32_to_cpu(pdr->addr);
167 }
168
169 static inline u32
170 pdr_len(const struct pdr *pdr)
171 {
172         return le32_to_cpu(pdr->len);
173 }
174
175 /*** PDI Access functions ***/
176
177 static inline u32
178 pdi_id(const struct pdi *pdi)
179 {
180         return le16_to_cpu(pdi->id);
181 }
182
183 /* Return length of the data only, in bytes */
184 static inline u32
185 pdi_len(const struct pdi *pdi)
186 {
187         return 2 * (le16_to_cpu(pdi->len) - 1);
188 }
189
190 /*** Hermes AUX control ***/
191
192 static inline void
193 hermes_aux_setaddr(hermes_t *hw, u32 addr)
194 {
195         hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7));
196         hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F));
197 }
198
199 static inline int
200 hermes_aux_control(hermes_t *hw, int enabled)
201 {
202         int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED;
203         int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE;
204         int i;
205
206         /* Already open? */
207         if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state)
208                 return 0;
209
210         hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0);
211         hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1);
212         hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2);
213         hermes_write_reg(hw, HERMES_CONTROL, action);
214
215         for (i = 0; i < 20; i++) {
216                 udelay(10);
217                 if (hermes_read_reg(hw, HERMES_CONTROL) ==
218                     desired_state)
219                         return 0;
220         }
221
222         return -EBUSY;
223 }
224
225 /*** Plug Data Functions ***/
226
227 /*
228  * Scan PDR for the record with the specified RECORD_ID.
229  * If it's not found, return NULL.
230  */
231 static struct pdr *
232 hermes_find_pdr(struct pdr *first_pdr, u32 record_id)
233 {
234         struct pdr *pdr = first_pdr;
235         void *end = (void *)first_pdr + MAX_PDA_SIZE;
236
237         while (((void *)pdr < end) &&
238                (pdr_id(pdr) != PDI_END)) {
239                 /*
240                  * PDR area is currently not terminated by PDI_END.
241                  * It's followed by CRC records, which have the type
242                  * field where PDR has length.  The type can be 0 or 1.
243                  */
244                 if (pdr_len(pdr) < 2)
245                         return NULL;
246
247                 /* If the record ID matches, we are done */
248                 if (pdr_id(pdr) == record_id)
249                         return pdr;
250
251                 pdr = (struct pdr *) pdr->next;
252         }
253         return NULL;
254 }
255
256 /* Scan production data items for a particular entry */
257 static struct pdi *
258 hermes_find_pdi(struct pdi *first_pdi, u32 record_id)
259 {
260         struct pdi *pdi = first_pdi;
261
262         while (pdi_id(pdi) != PDI_END) {
263
264                 /* If the record ID matches, we are done */
265                 if (pdi_id(pdi) == record_id)
266                         return pdi;
267
268                 pdi = (struct pdi *) &pdi->data[pdi_len(pdi)];
269         }
270         return NULL;
271 }
272
273 /* Process one Plug Data Item - find corresponding PDR and plug it */
274 static int
275 hermes_plug_pdi(hermes_t *hw, struct pdr *first_pdr, const struct pdi *pdi)
276 {
277         struct pdr *pdr;
278
279         /* Find the PDR corresponding to this PDI */
280         pdr = hermes_find_pdr(first_pdr, pdi_id(pdi));
281
282         /* No match is found, safe to ignore */
283         if (!pdr)
284                 return 0;
285
286         /* Lengths of the data in PDI and PDR must match */
287         if (pdi_len(pdi) != pdr_len(pdr))
288                 return -EINVAL;
289
290         /* do the actual plugging */
291         hermes_aux_setaddr(hw, pdr_addr(pdr));
292         hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi));
293
294         return 0;
295 }
296
297 /* Read PDA from the adapter */
298 int hermes_read_pda(hermes_t *hw,
299                     __le16 *pda,
300                     u32 pda_addr,
301                     u16 pda_len,
302                     int use_eeprom) /* can we get this into hw? */
303 {
304         int ret;
305         u16 pda_size;
306         u16 data_len = pda_len;
307         __le16 *data = pda;
308
309         if (use_eeprom) {
310                 /* PDA of spectrum symbol is in eeprom */
311
312                 /* Issue command to read EEPROM */
313                 ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
314                 if (ret)
315                         return ret;
316         } else {
317                 /* wl_lkm does not include PDA size in the PDA area.
318                  * We will pad the information into pda, so other routines
319                  * don't have to be modified */
320                 pda[0] = cpu_to_le16(pda_len - 2);
321                         /* Includes CFG_PROD_DATA but not itself */
322                 pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */
323                 data_len = pda_len - 4;
324                 data = pda + 2;
325         }
326
327         /* Open auxiliary port */
328         ret = hermes_aux_control(hw, 1);
329         printk(KERN_DEBUG PFX "AUX enable returned %d\n", ret);
330         if (ret)
331                 return ret;
332
333         /* read PDA from EEPROM */
334         hermes_aux_setaddr(hw, pda_addr);
335         hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2);
336
337         /* Close aux port */
338         ret = hermes_aux_control(hw, 0);
339         printk(KERN_DEBUG PFX "AUX disable returned %d\n", ret);
340
341         /* Check PDA length */
342         pda_size = le16_to_cpu(pda[0]);
343         printk(KERN_DEBUG PFX "Actual PDA length %d, Max allowed %d\n",
344                pda_size, pda_len);
345         if (pda_size > pda_len)
346                 return -EINVAL;
347
348         return 0;
349 }
350 EXPORT_SYMBOL(hermes_read_pda);
351
352 /* Parse PDA and write the records into the adapter
353  *
354  * Attempt to write every records that is in the specified pda
355  * which also has a valid production data record for the firmware.
356  */
357 int hermes_apply_pda(hermes_t *hw,
358                      const char *first_pdr,
359                      const __le16 *pda)
360 {
361         int ret;
362         const struct pdi *pdi;
363         struct pdr *pdr;
364
365         pdr = (struct pdr *) first_pdr;
366
367         /* Go through every PDI and plug them into the adapter */
368         pdi = (const struct pdi *) (pda + 2);
369         while (pdi_id(pdi) != PDI_END) {
370                 ret = hermes_plug_pdi(hw, pdr, pdi);
371                 if (ret)
372                         return ret;
373
374                 /* Increment to the next PDI */
375                 pdi = (const struct pdi *) &pdi->data[pdi_len(pdi)];
376         }
377         return 0;
378 }
379 EXPORT_SYMBOL(hermes_apply_pda);
380
381 /* Identify the total number of bytes in all blocks
382  * including the header data.
383  */
384 size_t
385 hermes_blocks_length(const char *first_block)
386 {
387         const struct dblock *blk = (const struct dblock *) first_block;
388         int total_len = 0;
389         int len;
390
391         /* Skip all blocks to locate Plug Data References
392          * (Spectrum CS) */
393         while (dblock_addr(blk) != BLOCK_END) {
394                 len = dblock_len(blk);
395                 total_len += sizeof(*blk) + len;
396                 blk = (struct dblock *) &blk->data[len];
397         }
398
399         return total_len;
400 }
401 EXPORT_SYMBOL(hermes_blocks_length);
402
403 /*** Hermes programming ***/
404
405 /* About to start programming data (Hermes I)
406  * offset is the entry point
407  *
408  * Spectrum_cs' Symbol fw does not require this
409  * wl_lkm Agere fw does
410  * Don't know about intersil
411  */
412 int hermesi_program_init(hermes_t *hw, u32 offset)
413 {
414         int err;
415
416         /* Disable interrupts?*/
417         /*hw->inten = 0x0;*/
418         /*hermes_write_regn(hw, INTEN, 0);*/
419         /*hermes_set_irqmask(hw, 0);*/
420
421         /* Acknowledge any outstanding command */
422         hermes_write_regn(hw, EVACK, 0xFFFF);
423
424         /* Using doicmd_wait rather than docmd_wait */
425         err = hermes_doicmd_wait(hw,
426                                  0x0100 | HERMES_CMD_INIT,
427                                  0, 0, 0, NULL);
428         if (err)
429                 return err;
430
431         err = hermes_doicmd_wait(hw,
432                                  0x0000 | HERMES_CMD_INIT,
433                                  0, 0, 0, NULL);
434         if (err)
435                 return err;
436
437         err = hermes_aux_control(hw, 1);
438         printk(KERN_DEBUG PFX "AUX enable returned %d\n", err);
439
440         if (err)
441                 return err;
442
443         printk(KERN_DEBUG PFX "Enabling volatile, EP 0x%08x\n", offset);
444         err = hermes_doicmd_wait(hw,
445                                  HERMES_PROGRAM_ENABLE_VOLATILE,
446                                  offset & 0xFFFFu,
447                                  offset >> 16,
448                                  0,
449                                  NULL);
450         printk(KERN_DEBUG PFX "PROGRAM_ENABLE returned %d\n",
451                err);
452
453         return err;
454 }
455 EXPORT_SYMBOL(hermesi_program_init);
456
457 /* Done programming data (Hermes I)
458  *
459  * Spectrum_cs' Symbol fw does not require this
460  * wl_lkm Agere fw does
461  * Don't know about intersil
462  */
463 int hermesi_program_end(hermes_t *hw)
464 {
465         struct hermes_response resp;
466         int rc = 0;
467         int err;
468
469         rc = hermes_docmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp);
470
471         printk(KERN_DEBUG PFX "PROGRAM_DISABLE returned %d, "
472                "r0 0x%04x, r1 0x%04x, r2 0x%04x\n",
473                rc, resp.resp0, resp.resp1, resp.resp2);
474
475         if ((rc == 0) &&
476             ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD))
477                 rc = -EIO;
478
479         err = hermes_aux_control(hw, 0);
480         printk(KERN_DEBUG PFX "AUX disable returned %d\n", err);
481
482         /* Acknowledge any outstanding command */
483         hermes_write_regn(hw, EVACK, 0xFFFF);
484
485         /* Reinitialise, ignoring return */
486         (void) hermes_doicmd_wait(hw, 0x0000 | HERMES_CMD_INIT,
487                                   0, 0, 0, NULL);
488
489         return rc ? rc : err;
490 }
491 EXPORT_SYMBOL(hermesi_program_end);
492
493 /* Program the data blocks */
494 int hermes_program(hermes_t *hw, const char *first_block, const char *end)
495 {
496         const struct dblock *blk;
497         u32 blkaddr;
498         u32 blklen;
499 #if LIMIT_PROGRAM_SIZE
500         u32 addr;
501         u32 len;
502 #endif
503
504         blk = (const struct dblock *) first_block;
505
506         if ((const char *) blk > (end - sizeof(*blk)))
507                 return -EIO;
508
509         blkaddr = dblock_addr(blk);
510         blklen = dblock_len(blk);
511
512         while ((blkaddr != BLOCK_END) &&
513                (((const char *) blk + blklen) <= end)) {
514                 printk(KERN_DEBUG PFX
515                        "Programming block of length %d to address 0x%08x\n",
516                        blklen, blkaddr);
517
518 #if !LIMIT_PROGRAM_SIZE
519                 /* wl_lkm driver splits this into writes of 2000 bytes */
520                 hermes_aux_setaddr(hw, blkaddr);
521                 hermes_write_bytes(hw, HERMES_AUXDATA, blk->data,
522                                    blklen);
523 #else
524                 len = (blklen < MAX_DL_SIZE) ? blklen : MAX_DL_SIZE;
525                 addr = blkaddr;
526
527                 while (addr < (blkaddr + blklen)) {
528                         printk(KERN_DEBUG PFX
529                                "Programming subblock of length %d "
530                                "to address 0x%08x. Data @ %p\n",
531                                len, addr, &blk->data[addr - blkaddr]);
532
533                         hermes_aux_setaddr(hw, addr);
534                         hermes_write_bytes(hw, HERMES_AUXDATA,
535                                            &blk->data[addr - blkaddr],
536                                            len);
537
538                         addr += len;
539                         len = ((blkaddr + blklen - addr) < MAX_DL_SIZE) ?
540                                 (blkaddr + blklen - addr) : MAX_DL_SIZE;
541                 }
542 #endif
543                 blk = (const struct dblock *) &blk->data[blklen];
544
545                 if ((const char *) blk > (end - sizeof(*blk)))
546                         return -EIO;
547
548                 blkaddr = dblock_addr(blk);
549                 blklen = dblock_len(blk);
550         }
551         return 0;
552 }
553 EXPORT_SYMBOL(hermes_program);
554
555 static int __init init_hermes_dld(void)
556 {
557         return 0;
558 }
559
560 static void __exit exit_hermes_dld(void)
561 {
562 }
563
564 module_init(init_hermes_dld);
565 module_exit(exit_hermes_dld);
566
567 /*** Default plugging data for Hermes I ***/
568 /* Values from wl_lkm_718/hcf/dhf.c */
569
570 #define DEFINE_DEFAULT_PDR(pid, length, data)                           \
571 static const struct {                                                   \
572         __le16 len;                                                     \
573         __le16 id;                                                      \
574         u8 val[length];                                                 \
575 } __attribute__ ((packed)) default_pdr_data_##pid = {                   \
576         __constant_cpu_to_le16((sizeof(default_pdr_data_##pid)/         \
577                                 sizeof(__le16)) - 1),                   \
578         __constant_cpu_to_le16(pid),                                    \
579         data                                                            \
580 }
581
582 #define DEFAULT_PDR(pid) default_pdr_data_##pid
583
584 /*  HWIF Compatiblity */
585 DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00");
586
587 /* PPPPSign */
588 DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00");
589
590 /* PPPPProf */
591 DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00");
592
593 /* Antenna diversity */
594 DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F");
595
596 /* Modem VCO band Set-up */
597 DEFINE_DEFAULT_PDR(0x0160, 28,
598                    "\x00\x00\x00\x00\x00\x00\x00\x00"
599                    "\x00\x00\x00\x00\x00\x00\x00\x00"
600                    "\x00\x00\x00\x00\x00\x00\x00\x00"
601                    "\x00\x00\x00\x00");
602
603 /* Modem Rx Gain Table Values */
604 DEFINE_DEFAULT_PDR(0x0161, 256,
605                    "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
606                    "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
607                    "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
608                    "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
609                    "\x3F\x01\x3E\01\x3E\x01\x3D\x01"
610                    "\x3D\x01\x3C\01\x3C\x01\x3B\x01"
611                    "\x3B\x01\x3A\01\x3A\x01\x39\x01"
612                    "\x39\x01\x38\01\x38\x01\x37\x01"
613                    "\x37\x01\x36\01\x36\x01\x35\x01"
614                    "\x35\x01\x34\01\x34\x01\x33\x01"
615                    "\x33\x01\x32\x01\x32\x01\x31\x01"
616                    "\x31\x01\x30\x01\x30\x01\x7B\x01"
617                    "\x7B\x01\x7A\x01\x7A\x01\x79\x01"
618                    "\x79\x01\x78\x01\x78\x01\x77\x01"
619                    "\x77\x01\x76\x01\x76\x01\x75\x01"
620                    "\x75\x01\x74\x01\x74\x01\x73\x01"
621                    "\x73\x01\x72\x01\x72\x01\x71\x01"
622                    "\x71\x01\x70\x01\x70\x01\x68\x01"
623                    "\x68\x01\x67\x01\x67\x01\x66\x01"
624                    "\x66\x01\x65\x01\x65\x01\x57\x01"
625                    "\x57\x01\x56\x01\x56\x01\x55\x01"
626                    "\x55\x01\x54\x01\x54\x01\x53\x01"
627                    "\x53\x01\x52\x01\x52\x01\x51\x01"
628                    "\x51\x01\x50\x01\x50\x01\x48\x01"
629                    "\x48\x01\x47\x01\x47\x01\x46\x01"
630                    "\x46\x01\x45\x01\x45\x01\x44\x01"
631                    "\x44\x01\x43\x01\x43\x01\x42\x01"
632                    "\x42\x01\x41\x01\x41\x01\x40\x01"
633                    "\x40\x01\x40\x01\x40\x01\x40\x01"
634                    "\x40\x01\x40\x01\x40\x01\x40\x01"
635                    "\x40\x01\x40\x01\x40\x01\x40\x01"
636                    "\x40\x01\x40\x01\x40\x01\x40\x01");
637
638 /* Write PDA according to certain rules.
639  *
640  * For every production data record, look for a previous setting in
641  * the pda, and use that.
642  *
643  * For certain records, use defaults if they are not found in pda.
644  */
645 int hermes_apply_pda_with_defaults(hermes_t *hw,
646                                    const char *first_pdr,
647                                    const __le16 *pda)
648 {
649         const struct pdr *pdr = (const struct pdr *) first_pdr;
650         struct pdi *first_pdi = (struct pdi *) &pda[2];
651         struct pdi *pdi;
652         struct pdi *default_pdi = NULL;
653         struct pdi *outdoor_pdi;
654         void *end = (void *)first_pdr + MAX_PDA_SIZE;
655         int record_id;
656
657         while (((void *)pdr < end) &&
658                (pdr_id(pdr) != PDI_END)) {
659                 /*
660                  * For spectrum_cs firmwares,
661                  * PDR area is currently not terminated by PDI_END.
662                  * It's followed by CRC records, which have the type
663                  * field where PDR has length.  The type can be 0 or 1.
664                  */
665                 if (pdr_len(pdr) < 2)
666                         break;
667                 record_id = pdr_id(pdr);
668
669                 pdi = hermes_find_pdi(first_pdi, record_id);
670                 if (pdi)
671                         printk(KERN_DEBUG PFX "Found record 0x%04x at %p\n",
672                                record_id, pdi);
673
674                 switch (record_id) {
675                 case 0x110: /* Modem REFDAC values */
676                 case 0x120: /* Modem VGDAC values */
677                         outdoor_pdi = hermes_find_pdi(first_pdi, record_id + 1);
678                         default_pdi = NULL;
679                         if (outdoor_pdi) {
680                                 pdi = outdoor_pdi;
681                                 printk(KERN_DEBUG PFX
682                                        "Using outdoor record 0x%04x at %p\n",
683                                        record_id + 1, pdi);
684                         }
685                         break;
686                 case 0x5: /*  HWIF Compatiblity */
687                         default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005);
688                         break;
689                 case 0x108: /* PPPPSign */
690                         default_pdi = (struct pdi *) &DEFAULT_PDR(0x0108);
691                         break;
692                 case 0x109: /* PPPPProf */
693                         default_pdi = (struct pdi *) &DEFAULT_PDR(0x0109);
694                         break;
695                 case 0x150: /* Antenna diversity */
696                         default_pdi = (struct pdi *) &DEFAULT_PDR(0x0150);
697                         break;
698                 case 0x160: /* Modem VCO band Set-up */
699                         default_pdi = (struct pdi *) &DEFAULT_PDR(0x0160);
700                         break;
701                 case 0x161: /* Modem Rx Gain Table Values */
702                         default_pdi = (struct pdi *) &DEFAULT_PDR(0x0161);
703                         break;
704                 default:
705                         default_pdi = NULL;
706                         break;
707                 }
708                 if (!pdi && default_pdi) {
709                         /* Use default */
710                         pdi = default_pdi;
711                         printk(KERN_DEBUG PFX
712                                "Using default record 0x%04x at %p\n",
713                                record_id, pdi);
714                 }
715
716                 if (pdi) {
717                         /* Lengths of the data in PDI and PDR must match */
718                         if (pdi_len(pdi) == pdr_len(pdr)) {
719                                 /* do the actual plugging */
720                                 hermes_aux_setaddr(hw, pdr_addr(pdr));
721                                 hermes_write_bytes(hw, HERMES_AUXDATA,
722                                                    pdi->data, pdi_len(pdi));
723                         }
724                 }
725
726                 pdr++;
727         }
728         return 0;
729 }
730 EXPORT_SYMBOL(hermes_apply_pda_with_defaults);