Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

orinoco: Make firmware download logic more generic

Ensure PDA read is terminated.
Prevent invalid programming blocks from causing reads outside the
firmware image
Turn off aux stuff when finished.
Option to program in limited block sizes (controlled by macro).
Option to read PDA from EEPROM.

Signed-off-by: David Kilroy <kilroyd@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>

authored by

David Kilroy and committed by
John W. Linville
e2334180 f482eb79

+182 -72
+158 -51
drivers/net/wireless/hermes_dld.c
··· 64 64 #define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */ 65 65 #define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */ 66 66 #define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */ 67 + #define HERMES_AUX_DISABLED 0x0000 /* Auxiliary port is closed */ 67 68 68 69 #define HERMES_AUX_PW0 0xFE01 69 70 #define HERMES_AUX_PW1 0xDC23 70 71 #define HERMES_AUX_PW2 0xBA45 71 72 72 - /* End markers */ 73 + /* End markers used in dblocks */ 73 74 #define PDI_END 0x00000000 /* End of PDA */ 74 75 #define BLOCK_END 0xFFFFFFFF /* Last image block */ 76 + #define TEXT_END 0x1A /* End of text header */ 77 + 78 + /* 79 + * PDA == Production Data Area 80 + * 81 + * In principle, the max. size of the PDA is is 4096 words. Currently, 82 + * however, only about 500 bytes of this area are used. 83 + * 84 + * Some USB implementations can't handle sizes in excess of 1016. Note 85 + * that PDA is not actually used in those USB environments, but may be 86 + * retrieved by common code. 87 + */ 88 + #define MAX_PDA_SIZE 1000 89 + 90 + /* Limit the amout we try to download in a single shot. 91 + * Size is in bytes. 92 + */ 93 + #define MAX_DL_SIZE 1024 94 + #define LIMIT_PROGRAM_SIZE 0 75 95 76 96 /* 77 97 * The following structures have little-endian fields denoted by ··· 132 112 char data[0]; /* plug data */ 133 113 } __attribute__ ((packed)); 134 114 135 - /* Functions for access to little-endian data */ 115 + /*** FW data block access functions ***/ 116 + 136 117 static inline u32 137 118 dblock_addr(const struct dblock *blk) 138 119 { ··· 145 124 { 146 125 return le16_to_cpu(blk->len); 147 126 } 127 + 128 + /*** PDR Access functions ***/ 148 129 149 130 static inline u32 150 131 pdr_id(const struct pdr *pdr) ··· 166 143 return le32_to_cpu(pdr->len); 167 144 } 168 145 146 + /*** PDI Access functions ***/ 147 + 169 148 static inline u32 170 149 pdi_id(const struct pdi *pdi) 171 150 { ··· 181 156 return 2 * (le16_to_cpu(pdi->len) - 1); 182 157 } 183 158 184 - /* Set address of the auxiliary port */ 159 + /*** Hermes AUX control ***/ 160 + 185 161 static inline void 186 - spectrum_aux_setaddr(hermes_t *hw, u32 addr) 162 + hermes_aux_setaddr(hermes_t *hw, u32 addr) 187 163 { 188 164 hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7)); 189 165 hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F)); 190 166 } 191 167 192 - /* Open access to the auxiliary port */ 193 - static int 194 - spectrum_aux_open(hermes_t *hw) 168 + static inline int 169 + hermes_aux_control(hermes_t *hw, int enabled) 195 170 { 171 + int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED; 172 + int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE; 196 173 int i; 197 174 198 175 /* Already open? */ 199 - if (hermes_read_reg(hw, HERMES_CONTROL) == HERMES_AUX_ENABLED) 176 + if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state) 200 177 return 0; 201 178 202 179 hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0); 203 180 hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1); 204 181 hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2); 205 - hermes_write_reg(hw, HERMES_CONTROL, HERMES_AUX_ENABLE); 182 + hermes_write_reg(hw, HERMES_CONTROL, action); 206 183 207 184 for (i = 0; i < 20; i++) { 208 185 udelay(10); 209 186 if (hermes_read_reg(hw, HERMES_CONTROL) == 210 - HERMES_AUX_ENABLED) 187 + desired_state) 211 188 return 0; 212 189 } 213 190 214 191 return -EBUSY; 215 192 } 216 193 194 + /*** Plug Data Functions ***/ 195 + 217 196 /* 218 197 * Scan PDR for the record with the specified RECORD_ID. 219 198 * If it's not found, return NULL. 220 199 */ 221 200 static struct pdr * 222 - spectrum_find_pdr(struct pdr *first_pdr, u32 record_id) 201 + hermes_find_pdr(struct pdr *first_pdr, u32 record_id) 223 202 { 224 203 struct pdr *pdr = first_pdr; 204 + void *end = (void *)first_pdr + MAX_PDA_SIZE; 225 205 226 - while (pdr_id(pdr) != PDI_END) { 206 + while (((void *)pdr < end) && 207 + (pdr_id(pdr) != PDI_END)) { 227 208 /* 228 209 * PDR area is currently not terminated by PDI_END. 229 210 * It's followed by CRC records, which have the type ··· 249 218 250 219 /* Process one Plug Data Item - find corresponding PDR and plug it */ 251 220 static int 252 - spectrum_plug_pdi(hermes_t *hw, struct pdr *first_pdr, struct pdi *pdi) 221 + hermes_plug_pdi(hermes_t *hw, struct pdr *first_pdr, const struct pdi *pdi) 253 222 { 254 223 struct pdr *pdr; 255 224 256 - /* Find the PDI corresponding to this PDR */ 257 - pdr = spectrum_find_pdr(first_pdr, pdi_id(pdi)); 225 + /* Find the PDR corresponding to this PDI */ 226 + pdr = hermes_find_pdr(first_pdr, pdi_id(pdi)); 258 227 259 228 /* No match is found, safe to ignore */ 260 229 if (!pdr) ··· 265 234 return -EINVAL; 266 235 267 236 /* do the actual plugging */ 268 - spectrum_aux_setaddr(hw, pdr_addr(pdr)); 237 + hermes_aux_setaddr(hw, pdr_addr(pdr)); 269 238 hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi)); 270 239 271 240 return 0; 272 241 } 273 242 274 243 /* Read PDA from the adapter */ 275 - int 276 - spectrum_read_pda(hermes_t *hw, __le16 *pda, int pda_len) 244 + int hermes_read_pda(hermes_t *hw, 245 + __le16 *pda, 246 + u32 pda_addr, 247 + u16 pda_len, 248 + int use_eeprom) /* can we get this into hw? */ 277 249 { 278 250 int ret; 279 - int pda_size; 251 + u16 pda_size; 252 + u16 data_len = pda_len; 253 + __le16 *data = pda; 280 254 281 - /* Issue command to read EEPROM */ 282 - ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); 283 - if (ret) 284 - return ret; 255 + if (use_eeprom) { 256 + /* PDA of spectrum symbol is in eeprom */ 257 + 258 + /* Issue command to read EEPROM */ 259 + ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); 260 + if (ret) 261 + return ret; 262 + } 285 263 286 264 /* Open auxiliary port */ 287 - ret = spectrum_aux_open(hw); 265 + ret = hermes_aux_control(hw, 1); 266 + printk(KERN_DEBUG PFX "AUX enable returned %d\n", ret); 288 267 if (ret) 289 268 return ret; 290 269 291 270 /* read PDA from EEPROM */ 292 - spectrum_aux_setaddr(hw, PDA_ADDR); 293 - hermes_read_words(hw, HERMES_AUXDATA, pda, pda_len / 2); 271 + hermes_aux_setaddr(hw, pda_addr); 272 + hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2); 273 + 274 + /* Close aux port */ 275 + ret = hermes_aux_control(hw, 0); 276 + printk(KERN_DEBUG PFX "AUX disable returned %d\n", ret); 294 277 295 278 /* Check PDA length */ 296 279 pda_size = le16_to_cpu(pda[0]); 280 + printk(KERN_DEBUG PFX "Actual PDA length %d, Max allowed %d\n", 281 + pda_size, pda_len); 297 282 if (pda_size > pda_len) 298 283 return -EINVAL; 299 284 300 285 return 0; 301 286 } 302 - EXPORT_SYMBOL(spectrum_read_pda); 287 + EXPORT_SYMBOL(hermes_read_pda); 303 288 304 - /* Parse PDA and write the records into the adapter */ 305 - int 306 - spectrum_apply_pda(hermes_t *hw, const struct dblock *first_block, 307 - __le16 *pda) 289 + /* Parse PDA and write the records into the adapter 290 + * 291 + * Attempt to write every records that is in the specified pda 292 + * which also has a valid production data record for the firmware. 293 + */ 294 + int hermes_apply_pda(hermes_t *hw, 295 + const char *first_pdr, 296 + const __le16 *pda) 308 297 { 309 298 int ret; 310 - struct pdi *pdi; 311 - struct pdr *first_pdr; 312 - const struct dblock *blk = first_block; 299 + const struct pdi *pdi; 300 + struct pdr *pdr; 313 301 314 - /* Skip all blocks to locate Plug Data References */ 315 - while (dblock_addr(blk) != BLOCK_END) 316 - blk = (struct dblock *) &blk->data[dblock_len(blk)]; 317 - 318 - first_pdr = (struct pdr *) blk; 302 + pdr = (struct pdr *) first_pdr; 319 303 320 304 /* Go through every PDI and plug them into the adapter */ 321 - pdi = (struct pdi *) (pda + 2); 305 + pdi = (const struct pdi *) (pda + 2); 322 306 while (pdi_id(pdi) != PDI_END) { 323 - ret = spectrum_plug_pdi(hw, first_pdr, pdi); 307 + ret = hermes_plug_pdi(hw, pdr, pdi); 324 308 if (ret) 325 309 return ret; 326 310 327 311 /* Increment to the next PDI */ 328 - pdi = (struct pdi *) &pdi->data[pdi_len(pdi)]; 312 + pdi = (const struct pdi *) &pdi->data[pdi_len(pdi)]; 329 313 } 330 314 return 0; 331 315 } 332 - EXPORT_SYMBOL(spectrum_apply_pda); 316 + EXPORT_SYMBOL(hermes_apply_pda); 333 317 334 - /* Load firmware blocks into the adapter */ 335 - int 336 - spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block) 318 + /* Identify the total number of bytes in all blocks 319 + * including the header data. 320 + */ 321 + size_t 322 + hermes_blocks_length(const char *first_block) 323 + { 324 + const struct dblock *blk = (const struct dblock *) first_block; 325 + int total_len = 0; 326 + int len; 327 + 328 + /* Skip all blocks to locate Plug Data References 329 + * (Spectrum CS) */ 330 + while (dblock_addr(blk) != BLOCK_END) { 331 + len = dblock_len(blk); 332 + total_len += sizeof(*blk) + len; 333 + blk = (struct dblock *) &blk->data[len]; 334 + } 335 + 336 + return total_len; 337 + } 338 + EXPORT_SYMBOL(hermes_blocks_length); 339 + 340 + /*** Hermes programming ***/ 341 + 342 + /* Program the data blocks */ 343 + int hermes_program(hermes_t *hw, const char *first_block, const char *end) 337 344 { 338 345 const struct dblock *blk; 339 346 u32 blkaddr; 340 347 u32 blklen; 348 + #if LIMIT_PROGRAM_SIZE 349 + u32 addr; 350 + u32 len; 351 + #endif 341 352 342 - blk = first_block; 353 + blk = (const struct dblock *) first_block; 354 + 355 + if ((const char *) blk > (end - sizeof(*blk))) 356 + return -EIO; 357 + 343 358 blkaddr = dblock_addr(blk); 344 359 blklen = dblock_len(blk); 345 360 346 - while (dblock_addr(blk) != BLOCK_END) { 347 - spectrum_aux_setaddr(hw, blkaddr); 361 + while ((blkaddr != BLOCK_END) && 362 + (((const char *) blk + blklen) <= end)) { 363 + printk(KERN_DEBUG PFX 364 + "Programming block of length %d to address 0x%08x\n", 365 + blklen, blkaddr); 366 + 367 + #if !LIMIT_PROGRAM_SIZE 368 + /* wl_lkm driver splits this into writes of 2000 bytes */ 369 + hermes_aux_setaddr(hw, blkaddr); 348 370 hermes_write_bytes(hw, HERMES_AUXDATA, blk->data, 349 371 blklen); 372 + #else 373 + len = (blklen < MAX_DL_SIZE) ? blklen : MAX_DL_SIZE; 374 + addr = blkaddr; 350 375 351 - blk = (struct dblock *) &blk->data[blklen]; 376 + while (addr < (blkaddr + blklen)) { 377 + printk(KERN_DEBUG PFX 378 + "Programming subblock of length %d " 379 + "to address 0x%08x. Data @ %p\n", 380 + len, addr, &blk->data[addr - blkaddr]); 381 + 382 + hermes_aux_setaddr(hw, addr); 383 + hermes_write_bytes(hw, HERMES_AUXDATA, 384 + &blk->data[addr - blkaddr], 385 + len); 386 + 387 + addr += len; 388 + len = ((blkaddr + blklen - addr) < MAX_DL_SIZE) ? 389 + (blkaddr + blklen - addr) : MAX_DL_SIZE; 390 + } 391 + #endif 392 + blk = (const struct dblock *) &blk->data[blklen]; 393 + 394 + if ((const char *) blk > (end - sizeof(*blk))) 395 + return -EIO; 396 + 352 397 blkaddr = dblock_addr(blk); 353 398 blklen = dblock_len(blk); 354 399 } 355 400 return 0; 356 401 } 357 - EXPORT_SYMBOL(spectrum_load_blocks); 402 + EXPORT_SYMBOL(hermes_program); 358 403 359 404 static int __init init_hermes_dld(void) 360 405 {
+10 -12
drivers/net/wireless/hermes_dld.h
··· 27 27 28 28 #include "hermes.h" 29 29 30 - /* Position of PDA in the adapter memory */ 31 - #define EEPROM_ADDR 0x3000 32 - #define EEPROM_LEN 0x200 33 - #define PDA_OFFSET 0x100 30 + int hermes_program(hermes_t *hw, const char *first_block, const char *end); 34 31 35 - #define PDA_ADDR (EEPROM_ADDR + PDA_OFFSET) 36 - #define PDA_WORDS ((EEPROM_LEN - PDA_OFFSET) / 2) 32 + int hermes_read_pda(hermes_t *hw, 33 + __le16 *pda, 34 + u32 pda_addr, 35 + u16 pda_len, 36 + int use_eeprom); 37 + int hermes_apply_pda(hermes_t *hw, 38 + const char *first_pdr, 39 + const __le16 *pda); 37 40 38 - struct dblock; 39 - 40 - int spectrum_read_pda(hermes_t *hw, __le16 *pda, int pda_len); 41 - int spectrum_apply_pda(hermes_t *hw, const struct dblock *first_block, 42 - __le16 *pda); 43 - int spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block); 41 + size_t hermes_blocks_length(const char *first_block); 44 42 45 43 #endif /* _HERMES_DLD_H */
+14 -9
drivers/net/wireless/spectrum_cs.c
··· 166 166 */ 167 167 static int 168 168 spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link, 169 - const unsigned char *image, int secondary) 169 + const unsigned char *image, const unsigned char *end, 170 + int secondary) 170 171 { 171 172 int ret; 172 173 const unsigned char *ptr; 173 - const struct dblock *first_block; 174 + const unsigned char *first_block; 174 175 175 176 /* Plug Data Area (PDA) */ 176 177 __le16 pda[PDA_WORDS]; ··· 179 178 /* Binary block begins after the 0x1A marker */ 180 179 ptr = image; 181 180 while (*ptr++ != TEXT_END); 182 - first_block = (const struct dblock *) ptr; 181 + first_block = ptr; 183 182 184 - /* Read the PDA */ 183 + /* Read the PDA from EEPROM */ 185 184 if (secondary) { 186 - ret = spectrum_read_pda(hw, pda, sizeof(pda)); 185 + ret = hermes_read_pda(hw, pda, PDA_ADDR, sizeof(pda), 1); 187 186 if (ret) 188 187 return ret; 189 188 } ··· 194 193 return ret; 195 194 196 195 /* Program the adapter with new firmware */ 197 - ret = spectrum_load_blocks(hw, first_block); 196 + ret = hermes_program(hw, first_block, end); 198 197 if (ret) 199 198 return ret; 200 199 201 200 /* Write the PDA to the adapter */ 202 201 if (secondary) { 203 - ret = spectrum_apply_pda(hw, first_block, pda); 202 + size_t len = hermes_blocks_length(first_block); 203 + ptr = first_block + len; 204 + ret = hermes_apply_pda(hw, ptr, pda); 204 205 if (ret) 205 206 return ret; 206 207 } ··· 245 242 } 246 243 247 244 /* Load primary firmware */ 248 - ret = spectrum_dl_image(hw, link, fw_entry->data, 0); 245 + ret = spectrum_dl_image(hw, link, fw_entry->data, 246 + fw_entry->data + fw_entry->size, 0); 249 247 release_firmware(fw_entry); 250 248 if (ret) { 251 249 printk(KERN_ERR PFX "Primary firmware download failed\n"); ··· 261 257 } 262 258 263 259 /* Load secondary firmware */ 264 - ret = spectrum_dl_image(hw, link, fw_entry->data, 1); 260 + ret = spectrum_dl_image(hw, link, fw_entry->data, 261 + fw_entry->data + fw_entry->size, 1); 265 262 release_firmware(fw_entry); 266 263 if (ret) { 267 264 printk(KERN_ERR PFX "Secondary firmware download failed\n");