root/drivers/net/wireless/intersil/orinoco/hermes_dld.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dblock_addr
  2. dblock_len
  3. pdr_id
  4. pdr_addr
  5. pdr_len
  6. pdi_id
  7. pdi_len
  8. hermes_find_pdr
  9. hermes_find_pdi
  10. hermes_plug_pdi
  11. hermes_apply_pda
  12. hermes_blocks_length
  13. hermes_program
  14. hermes_apply_pda_with_defaults

   1 /*
   2  * Hermes download helper.
   3  *
   4  * This helper:
   5  *  - is capable of writing to the volatile area of the hermes device
   6  *  - is currently not capable of writing to non-volatile areas
   7  *  - provide helpers to identify and update plugin data
   8  *  - is not capable of interpreting a fw image directly. That is up to
   9  *    the main card driver.
  10  *  - deals with Hermes I devices. It can probably be modified to deal
  11  *    with Hermes II devices
  12  *
  13  * Copyright (C) 2007, David Kilroy
  14  *
  15  * Plug data code slightly modified from spectrum_cs driver
  16  *    Copyright (C) 2002-2005 Pavel Roskin <proski@gnu.org>
  17  * Portions based on information in wl_lkm_718 Agere driver
  18  *    COPYRIGHT (C) 2001-2004 by Agere Systems Inc. All Rights Reserved
  19  *
  20  * The contents of this file are subject to the Mozilla Public License
  21  * Version 1.1 (the "License"); you may not use this file except in
  22  * compliance with the License. You may obtain a copy of the License
  23  * at http://www.mozilla.org/MPL/
  24  *
  25  * Software distributed under the License is distributed on an "AS IS"
  26  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  27  * the License for the specific language governing rights and
  28  * limitations under the License.
  29  *
  30  * Alternatively, the contents of this file may be used under the
  31  * terms of the GNU General Public License version 2 (the "GPL"), in
  32  * which case the provisions of the GPL are applicable instead of the
  33  * above.  If you wish to allow the use of your version of this file
  34  * only under the terms of the GPL and not to allow others to use your
  35  * version of this file under the MPL, indicate your decision by
  36  * deleting the provisions above and replace them with the notice and
  37  * other provisions required by the GPL.  If you do not delete the
  38  * provisions above, a recipient may use your version of this file
  39  * under either the MPL or the GPL.
  40  */
  41 
  42 #include <linux/module.h>
  43 #include <linux/delay.h>
  44 #include "hermes.h"
  45 #include "hermes_dld.h"
  46 
  47 #define PFX "hermes_dld: "
  48 
  49 /* End markers used in dblocks */
  50 #define PDI_END         0x00000000      /* End of PDA */
  51 #define BLOCK_END       0xFFFFFFFF      /* Last image block */
  52 #define TEXT_END        0x1A            /* End of text header */
  53 
  54 /*
  55  * The following structures have little-endian fields denoted by
  56  * the leading underscore.  Don't access them directly - use inline
  57  * functions defined below.
  58  */
  59 
  60 /*
  61  * The binary image to be downloaded consists of series of data blocks.
  62  * Each block has the following structure.
  63  */
  64 struct dblock {
  65         __le32 addr;            /* adapter address where to write the block */
  66         __le16 len;             /* length of the data only, in bytes */
  67         char data[0];           /* data to be written */
  68 } __packed;
  69 
  70 /*
  71  * Plug Data References are located in the image after the last data
  72  * block.  They refer to areas in the adapter memory where the plug data
  73  * items with matching ID should be written.
  74  */
  75 struct pdr {
  76         __le32 id;              /* record ID */
  77         __le32 addr;            /* adapter address where to write the data */
  78         __le32 len;             /* expected length of the data, in bytes */
  79         char next[0];           /* next PDR starts here */
  80 } __packed;
  81 
  82 /*
  83  * Plug Data Items are located in the EEPROM read from the adapter by
  84  * primary firmware.  They refer to the device-specific data that should
  85  * be plugged into the secondary firmware.
  86  */
  87 struct pdi {
  88         __le16 len;             /* length of ID and data, in words */
  89         __le16 id;              /* record ID */
  90         char data[0];           /* plug data */
  91 } __packed;
  92 
  93 /*** FW data block access functions ***/
  94 
  95 static inline u32
  96 dblock_addr(const struct dblock *blk)
  97 {
  98         return le32_to_cpu(blk->addr);
  99 }
 100 
 101 static inline u32
 102 dblock_len(const struct dblock *blk)
 103 {
 104         return le16_to_cpu(blk->len);
 105 }
 106 
 107 /*** PDR Access functions ***/
 108 
 109 static inline u32
 110 pdr_id(const struct pdr *pdr)
 111 {
 112         return le32_to_cpu(pdr->id);
 113 }
 114 
 115 static inline u32
 116 pdr_addr(const struct pdr *pdr)
 117 {
 118         return le32_to_cpu(pdr->addr);
 119 }
 120 
 121 static inline u32
 122 pdr_len(const struct pdr *pdr)
 123 {
 124         return le32_to_cpu(pdr->len);
 125 }
 126 
 127 /*** PDI Access functions ***/
 128 
 129 static inline u32
 130 pdi_id(const struct pdi *pdi)
 131 {
 132         return le16_to_cpu(pdi->id);
 133 }
 134 
 135 /* Return length of the data only, in bytes */
 136 static inline u32
 137 pdi_len(const struct pdi *pdi)
 138 {
 139         return 2 * (le16_to_cpu(pdi->len) - 1);
 140 }
 141 
 142 /*** Plug Data Functions ***/
 143 
 144 /*
 145  * Scan PDR for the record with the specified RECORD_ID.
 146  * If it's not found, return NULL.
 147  */
 148 static const struct pdr *
 149 hermes_find_pdr(const struct pdr *first_pdr, u32 record_id, const void *end)
 150 {
 151         const struct pdr *pdr = first_pdr;
 152 
 153         end -= sizeof(struct pdr);
 154 
 155         while (((void *) pdr <= end) &&
 156                (pdr_id(pdr) != PDI_END)) {
 157                 /*
 158                  * PDR area is currently not terminated by PDI_END.
 159                  * It's followed by CRC records, which have the type
 160                  * field where PDR has length.  The type can be 0 or 1.
 161                  */
 162                 if (pdr_len(pdr) < 2)
 163                         return NULL;
 164 
 165                 /* If the record ID matches, we are done */
 166                 if (pdr_id(pdr) == record_id)
 167                         return pdr;
 168 
 169                 pdr = (struct pdr *) pdr->next;
 170         }
 171         return NULL;
 172 }
 173 
 174 /* Scan production data items for a particular entry */
 175 static const struct pdi *
 176 hermes_find_pdi(const struct pdi *first_pdi, u32 record_id, const void *end)
 177 {
 178         const struct pdi *pdi = first_pdi;
 179 
 180         end -= sizeof(struct pdi);
 181 
 182         while (((void *) pdi <= end) &&
 183                (pdi_id(pdi) != PDI_END)) {
 184 
 185                 /* If the record ID matches, we are done */
 186                 if (pdi_id(pdi) == record_id)
 187                         return pdi;
 188 
 189                 pdi = (struct pdi *) &pdi->data[pdi_len(pdi)];
 190         }
 191         return NULL;
 192 }
 193 
 194 /* Process one Plug Data Item - find corresponding PDR and plug it */
 195 static int
 196 hermes_plug_pdi(struct hermes *hw, const struct pdr *first_pdr,
 197                 const struct pdi *pdi, const void *pdr_end)
 198 {
 199         const struct pdr *pdr;
 200 
 201         /* Find the PDR corresponding to this PDI */
 202         pdr = hermes_find_pdr(first_pdr, pdi_id(pdi), pdr_end);
 203 
 204         /* No match is found, safe to ignore */
 205         if (!pdr)
 206                 return 0;
 207 
 208         /* Lengths of the data in PDI and PDR must match */
 209         if (pdi_len(pdi) != pdr_len(pdr))
 210                 return -EINVAL;
 211 
 212         /* do the actual plugging */
 213         hw->ops->program(hw, pdi->data, pdr_addr(pdr), pdi_len(pdi));
 214 
 215         return 0;
 216 }
 217 
 218 /* Parse PDA and write the records into the adapter
 219  *
 220  * Attempt to write every records that is in the specified pda
 221  * which also has a valid production data record for the firmware.
 222  */
 223 int hermes_apply_pda(struct hermes *hw,
 224                      const char *first_pdr,
 225                      const void *pdr_end,
 226                      const __le16 *pda,
 227                      const void *pda_end)
 228 {
 229         int ret;
 230         const struct pdi *pdi;
 231         const struct pdr *pdr;
 232 
 233         pdr = (const struct pdr *) first_pdr;
 234         pda_end -= sizeof(struct pdi);
 235 
 236         /* Go through every PDI and plug them into the adapter */
 237         pdi = (const struct pdi *) (pda + 2);
 238         while (((void *) pdi <= pda_end) &&
 239                (pdi_id(pdi) != PDI_END)) {
 240                 ret = hermes_plug_pdi(hw, pdr, pdi, pdr_end);
 241                 if (ret)
 242                         return ret;
 243 
 244                 /* Increment to the next PDI */
 245                 pdi = (const struct pdi *) &pdi->data[pdi_len(pdi)];
 246         }
 247         return 0;
 248 }
 249 
 250 /* Identify the total number of bytes in all blocks
 251  * including the header data.
 252  */
 253 size_t
 254 hermes_blocks_length(const char *first_block, const void *end)
 255 {
 256         const struct dblock *blk = (const struct dblock *) first_block;
 257         int total_len = 0;
 258         int len;
 259 
 260         end -= sizeof(*blk);
 261 
 262         /* Skip all blocks to locate Plug Data References
 263          * (Spectrum CS) */
 264         while (((void *) blk <= end) &&
 265                (dblock_addr(blk) != BLOCK_END)) {
 266                 len = dblock_len(blk);
 267                 total_len += sizeof(*blk) + len;
 268                 blk = (struct dblock *) &blk->data[len];
 269         }
 270 
 271         return total_len;
 272 }
 273 
 274 /*** Hermes programming ***/
 275 
 276 /* Program the data blocks */
 277 int hermes_program(struct hermes *hw, const char *first_block, const void *end)
 278 {
 279         const struct dblock *blk;
 280         u32 blkaddr;
 281         u32 blklen;
 282         int err = 0;
 283 
 284         blk = (const struct dblock *) first_block;
 285 
 286         if ((void *) blk > (end - sizeof(*blk)))
 287                 return -EIO;
 288 
 289         blkaddr = dblock_addr(blk);
 290         blklen = dblock_len(blk);
 291 
 292         while ((blkaddr != BLOCK_END) &&
 293                (((void *) blk + blklen) <= end)) {
 294                 pr_debug(PFX "Programming block of length %d "
 295                          "to address 0x%08x\n", blklen, blkaddr);
 296 
 297                 err = hw->ops->program(hw, blk->data, blkaddr, blklen);
 298                 if (err)
 299                         break;
 300 
 301                 blk = (const struct dblock *) &blk->data[blklen];
 302 
 303                 if ((void *) blk > (end - sizeof(*blk)))
 304                         return -EIO;
 305 
 306                 blkaddr = dblock_addr(blk);
 307                 blklen = dblock_len(blk);
 308         }
 309         return err;
 310 }
 311 
 312 /*** Default plugging data for Hermes I ***/
 313 /* Values from wl_lkm_718/hcf/dhf.c */
 314 
 315 #define DEFINE_DEFAULT_PDR(pid, length, data)                           \
 316 static const struct {                                                   \
 317         __le16 len;                                                     \
 318         __le16 id;                                                      \
 319         u8 val[length];                                                 \
 320 } __packed default_pdr_data_##pid = {                   \
 321         cpu_to_le16((sizeof(default_pdr_data_##pid)/                    \
 322                                 sizeof(__le16)) - 1),                   \
 323         cpu_to_le16(pid),                                               \
 324         data                                                            \
 325 }
 326 
 327 #define DEFAULT_PDR(pid) default_pdr_data_##pid
 328 
 329 /*  HWIF Compatibility */
 330 DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00");
 331 
 332 /* PPPPSign */
 333 DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00");
 334 
 335 /* PPPPProf */
 336 DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00");
 337 
 338 /* Antenna diversity */
 339 DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F");
 340 
 341 /* Modem VCO band Set-up */
 342 DEFINE_DEFAULT_PDR(0x0160, 28,
 343                    "\x00\x00\x00\x00\x00\x00\x00\x00"
 344                    "\x00\x00\x00\x00\x00\x00\x00\x00"
 345                    "\x00\x00\x00\x00\x00\x00\x00\x00"
 346                    "\x00\x00\x00\x00");
 347 
 348 /* Modem Rx Gain Table Values */
 349 DEFINE_DEFAULT_PDR(0x0161, 256,
 350                    "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
 351                    "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
 352                    "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
 353                    "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
 354                    "\x3F\x01\x3E\01\x3E\x01\x3D\x01"
 355                    "\x3D\x01\x3C\01\x3C\x01\x3B\x01"
 356                    "\x3B\x01\x3A\01\x3A\x01\x39\x01"
 357                    "\x39\x01\x38\01\x38\x01\x37\x01"
 358                    "\x37\x01\x36\01\x36\x01\x35\x01"
 359                    "\x35\x01\x34\01\x34\x01\x33\x01"
 360                    "\x33\x01\x32\x01\x32\x01\x31\x01"
 361                    "\x31\x01\x30\x01\x30\x01\x7B\x01"
 362                    "\x7B\x01\x7A\x01\x7A\x01\x79\x01"
 363                    "\x79\x01\x78\x01\x78\x01\x77\x01"
 364                    "\x77\x01\x76\x01\x76\x01\x75\x01"
 365                    "\x75\x01\x74\x01\x74\x01\x73\x01"
 366                    "\x73\x01\x72\x01\x72\x01\x71\x01"
 367                    "\x71\x01\x70\x01\x70\x01\x68\x01"
 368                    "\x68\x01\x67\x01\x67\x01\x66\x01"
 369                    "\x66\x01\x65\x01\x65\x01\x57\x01"
 370                    "\x57\x01\x56\x01\x56\x01\x55\x01"
 371                    "\x55\x01\x54\x01\x54\x01\x53\x01"
 372                    "\x53\x01\x52\x01\x52\x01\x51\x01"
 373                    "\x51\x01\x50\x01\x50\x01\x48\x01"
 374                    "\x48\x01\x47\x01\x47\x01\x46\x01"
 375                    "\x46\x01\x45\x01\x45\x01\x44\x01"
 376                    "\x44\x01\x43\x01\x43\x01\x42\x01"
 377                    "\x42\x01\x41\x01\x41\x01\x40\x01"
 378                    "\x40\x01\x40\x01\x40\x01\x40\x01"
 379                    "\x40\x01\x40\x01\x40\x01\x40\x01"
 380                    "\x40\x01\x40\x01\x40\x01\x40\x01"
 381                    "\x40\x01\x40\x01\x40\x01\x40\x01");
 382 
 383 /* Write PDA according to certain rules.
 384  *
 385  * For every production data record, look for a previous setting in
 386  * the pda, and use that.
 387  *
 388  * For certain records, use defaults if they are not found in pda.
 389  */
 390 int hermes_apply_pda_with_defaults(struct hermes *hw,
 391                                    const char *first_pdr,
 392                                    const void *pdr_end,
 393                                    const __le16 *pda,
 394                                    const void *pda_end)
 395 {
 396         const struct pdr *pdr = (const struct pdr *) first_pdr;
 397         const struct pdi *first_pdi = (const struct pdi *) &pda[2];
 398         const struct pdi *pdi;
 399         const struct pdi *default_pdi = NULL;
 400         const struct pdi *outdoor_pdi;
 401         int record_id;
 402 
 403         pdr_end -= sizeof(struct pdr);
 404 
 405         while (((void *) pdr <= pdr_end) &&
 406                (pdr_id(pdr) != PDI_END)) {
 407                 /*
 408                  * For spectrum_cs firmwares,
 409                  * PDR area is currently not terminated by PDI_END.
 410                  * It's followed by CRC records, which have the type
 411                  * field where PDR has length.  The type can be 0 or 1.
 412                  */
 413                 if (pdr_len(pdr) < 2)
 414                         break;
 415                 record_id = pdr_id(pdr);
 416 
 417                 pdi = hermes_find_pdi(first_pdi, record_id, pda_end);
 418                 if (pdi)
 419                         pr_debug(PFX "Found record 0x%04x at %p\n",
 420                                  record_id, pdi);
 421 
 422                 switch (record_id) {
 423                 case 0x110: /* Modem REFDAC values */
 424                 case 0x120: /* Modem VGDAC values */
 425                         outdoor_pdi = hermes_find_pdi(first_pdi, record_id + 1,
 426                                                       pda_end);
 427                         default_pdi = NULL;
 428                         if (outdoor_pdi) {
 429                                 pdi = outdoor_pdi;
 430                                 pr_debug(PFX
 431                                          "Using outdoor record 0x%04x at %p\n",
 432                                          record_id + 1, pdi);
 433                         }
 434                         break;
 435                 case 0x5: /*  HWIF Compatibility */
 436                         default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005);
 437                         break;
 438                 case 0x108: /* PPPPSign */
 439                         default_pdi = (struct pdi *) &DEFAULT_PDR(0x0108);
 440                         break;
 441                 case 0x109: /* PPPPProf */
 442                         default_pdi = (struct pdi *) &DEFAULT_PDR(0x0109);
 443                         break;
 444                 case 0x150: /* Antenna diversity */
 445                         default_pdi = (struct pdi *) &DEFAULT_PDR(0x0150);
 446                         break;
 447                 case 0x160: /* Modem VCO band Set-up */
 448                         default_pdi = (struct pdi *) &DEFAULT_PDR(0x0160);
 449                         break;
 450                 case 0x161: /* Modem Rx Gain Table Values */
 451                         default_pdi = (struct pdi *) &DEFAULT_PDR(0x0161);
 452                         break;
 453                 default:
 454                         default_pdi = NULL;
 455                         break;
 456                 }
 457                 if (!pdi && default_pdi) {
 458                         /* Use default */
 459                         pdi = default_pdi;
 460                         pr_debug(PFX "Using default record 0x%04x at %p\n",
 461                                  record_id, pdi);
 462                 }
 463 
 464                 if (pdi) {
 465                         /* Lengths of the data in PDI and PDR must match */
 466                         if ((pdi_len(pdi) == pdr_len(pdr)) &&
 467                             ((void *) pdi->data + pdi_len(pdi) < pda_end)) {
 468                                 /* do the actual plugging */
 469                                 hw->ops->program(hw, pdi->data, pdr_addr(pdr),
 470                                                  pdi_len(pdi));
 471                         }
 472                 }
 473 
 474                 pdr++;
 475         }
 476         return 0;
 477 }

/* [<][>][^][v][top][bottom][index][help] */