aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDavid Kilroy <kilroyd@gmail.com>2008-08-21 18:27:52 -0400
committerJohn W. Linville <linville@tuxdriver.com>2008-08-22 19:28:05 -0400
commite23341809b7b60981d14a368155cd1f0724fb8d5 (patch)
tree0d035f354eec28816b1fcf813b9f5ed665d3d827 /drivers
parentf482eb797a391a098046a934f55af8bd785a4494 (diff)
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/hermes_dld.c209
-rw-r--r--drivers/net/wireless/hermes_dld.h22
-rw-r--r--drivers/net/wireless/spectrum_cs.c23
3 files changed, 182 insertions, 72 deletions
diff --git a/drivers/net/wireless/hermes_dld.c b/drivers/net/wireless/hermes_dld.c
index 9a8ef3040d59..22ae79dae41e 100644
--- a/drivers/net/wireless/hermes_dld.c
+++ b/drivers/net/wireless/hermes_dld.c
@@ -64,14 +64,34 @@ MODULE_LICENSE("Dual MPL/GPL");
64#define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */ 64#define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */
65#define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */ 65#define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */
66#define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */ 66#define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */
67#define HERMES_AUX_DISABLED 0x0000 /* Auxiliary port is closed */
67 68
68#define HERMES_AUX_PW0 0xFE01 69#define HERMES_AUX_PW0 0xFE01
69#define HERMES_AUX_PW1 0xDC23 70#define HERMES_AUX_PW1 0xDC23
70#define HERMES_AUX_PW2 0xBA45 71#define HERMES_AUX_PW2 0xBA45
71 72
72/* End markers */ 73/* End markers used in dblocks */
73#define PDI_END 0x00000000 /* End of PDA */ 74#define PDI_END 0x00000000 /* End of PDA */
74#define BLOCK_END 0xFFFFFFFF /* Last image block */ 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 * The following structures have little-endian fields denoted by 97 * The following structures have little-endian fields denoted by
@@ -112,7 +132,8 @@ struct pdi {
112 char data[0]; /* plug data */ 132 char data[0]; /* plug data */
113} __attribute__ ((packed)); 133} __attribute__ ((packed));
114 134
115/* Functions for access to little-endian data */ 135/*** FW data block access functions ***/
136
116static inline u32 137static inline u32
117dblock_addr(const struct dblock *blk) 138dblock_addr(const struct dblock *blk)
118{ 139{
@@ -125,6 +146,8 @@ dblock_len(const struct dblock *blk)
125 return le16_to_cpu(blk->len); 146 return le16_to_cpu(blk->len);
126} 147}
127 148
149/*** PDR Access functions ***/
150
128static inline u32 151static inline u32
129pdr_id(const struct pdr *pdr) 152pdr_id(const struct pdr *pdr)
130{ 153{
@@ -143,6 +166,8 @@ pdr_len(const struct pdr *pdr)
143 return le32_to_cpu(pdr->len); 166 return le32_to_cpu(pdr->len);
144} 167}
145 168
169/*** PDI Access functions ***/
170
146static inline u32 171static inline u32
147pdi_id(const struct pdi *pdi) 172pdi_id(const struct pdi *pdi)
148{ 173{
@@ -156,49 +181,55 @@ pdi_len(const struct pdi *pdi)
156 return 2 * (le16_to_cpu(pdi->len) - 1); 181 return 2 * (le16_to_cpu(pdi->len) - 1);
157} 182}
158 183
159/* Set address of the auxiliary port */ 184/*** Hermes AUX control ***/
185
160static inline void 186static inline void
161spectrum_aux_setaddr(hermes_t *hw, u32 addr) 187hermes_aux_setaddr(hermes_t *hw, u32 addr)
162{ 188{
163 hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7)); 189 hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7));
164 hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F)); 190 hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F));
165} 191}
166 192
167/* Open access to the auxiliary port */ 193static inline int
168static int 194hermes_aux_control(hermes_t *hw, int enabled)
169spectrum_aux_open(hermes_t *hw)
170{ 195{
196 int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED;
197 int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE;
171 int i; 198 int i;
172 199
173 /* Already open? */ 200 /* Already open? */
174 if (hermes_read_reg(hw, HERMES_CONTROL) == HERMES_AUX_ENABLED) 201 if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state)
175 return 0; 202 return 0;
176 203
177 hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0); 204 hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0);
178 hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1); 205 hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1);
179 hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2); 206 hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2);
180 hermes_write_reg(hw, HERMES_CONTROL, HERMES_AUX_ENABLE); 207 hermes_write_reg(hw, HERMES_CONTROL, action);
181 208
182 for (i = 0; i < 20; i++) { 209 for (i = 0; i < 20; i++) {
183 udelay(10); 210 udelay(10);
184 if (hermes_read_reg(hw, HERMES_CONTROL) == 211 if (hermes_read_reg(hw, HERMES_CONTROL) ==
185 HERMES_AUX_ENABLED) 212 desired_state)
186 return 0; 213 return 0;
187 } 214 }
188 215
189 return -EBUSY; 216 return -EBUSY;
190} 217}
191 218
219/*** Plug Data Functions ***/
220
192/* 221/*
193 * Scan PDR for the record with the specified RECORD_ID. 222 * Scan PDR for the record with the specified RECORD_ID.
194 * If it's not found, return NULL. 223 * If it's not found, return NULL.
195 */ 224 */
196static struct pdr * 225static struct pdr *
197spectrum_find_pdr(struct pdr *first_pdr, u32 record_id) 226hermes_find_pdr(struct pdr *first_pdr, u32 record_id)
198{ 227{
199 struct pdr *pdr = first_pdr; 228 struct pdr *pdr = first_pdr;
229 void *end = (void *)first_pdr + MAX_PDA_SIZE;
200 230
201 while (pdr_id(pdr) != PDI_END) { 231 while (((void *)pdr < end) &&
232 (pdr_id(pdr) != PDI_END)) {
202 /* 233 /*
203 * PDR area is currently not terminated by PDI_END. 234 * PDR area is currently not terminated by PDI_END.
204 * It's followed by CRC records, which have the type 235 * It's followed by CRC records, which have the type
@@ -218,12 +249,12 @@ spectrum_find_pdr(struct pdr *first_pdr, u32 record_id)
218 249
219/* Process one Plug Data Item - find corresponding PDR and plug it */ 250/* Process one Plug Data Item - find corresponding PDR and plug it */
220static int 251static int
221spectrum_plug_pdi(hermes_t *hw, struct pdr *first_pdr, struct pdi *pdi) 252hermes_plug_pdi(hermes_t *hw, struct pdr *first_pdr, const struct pdi *pdi)
222{ 253{
223 struct pdr *pdr; 254 struct pdr *pdr;
224 255
225 /* Find the PDI corresponding to this PDR */ 256 /* Find the PDR corresponding to this PDI */
226 pdr = spectrum_find_pdr(first_pdr, pdi_id(pdi)); 257 pdr = hermes_find_pdr(first_pdr, pdi_id(pdi));
227 258
228 /* No match is found, safe to ignore */ 259 /* No match is found, safe to ignore */
229 if (!pdr) 260 if (!pdr)
@@ -234,96 +265,172 @@ spectrum_plug_pdi(hermes_t *hw, struct pdr *first_pdr, struct pdi *pdi)
234 return -EINVAL; 265 return -EINVAL;
235 266
236 /* do the actual plugging */ 267 /* do the actual plugging */
237 spectrum_aux_setaddr(hw, pdr_addr(pdr)); 268 hermes_aux_setaddr(hw, pdr_addr(pdr));
238 hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi)); 269 hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi));
239 270
240 return 0; 271 return 0;
241} 272}
242 273
243/* Read PDA from the adapter */ 274/* Read PDA from the adapter */
244int 275int hermes_read_pda(hermes_t *hw,
245spectrum_read_pda(hermes_t *hw, __le16 *pda, int pda_len) 276 __le16 *pda,
277 u32 pda_addr,
278 u16 pda_len,
279 int use_eeprom) /* can we get this into hw? */
246{ 280{
247 int ret; 281 int ret;
248 int pda_size; 282 u16 pda_size;
283 u16 data_len = pda_len;
284 __le16 *data = pda;
249 285
250 /* Issue command to read EEPROM */ 286 if (use_eeprom) {
251 ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); 287 /* PDA of spectrum symbol is in eeprom */
252 if (ret) 288
253 return ret; 289 /* Issue command to read EEPROM */
290 ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
291 if (ret)
292 return ret;
293 }
254 294
255 /* Open auxiliary port */ 295 /* Open auxiliary port */
256 ret = spectrum_aux_open(hw); 296 ret = hermes_aux_control(hw, 1);
297 printk(KERN_DEBUG PFX "AUX enable returned %d\n", ret);
257 if (ret) 298 if (ret)
258 return ret; 299 return ret;
259 300
260 /* read PDA from EEPROM */ 301 /* read PDA from EEPROM */
261 spectrum_aux_setaddr(hw, PDA_ADDR); 302 hermes_aux_setaddr(hw, pda_addr);
262 hermes_read_words(hw, HERMES_AUXDATA, pda, pda_len / 2); 303 hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2);
304
305 /* Close aux port */
306 ret = hermes_aux_control(hw, 0);
307 printk(KERN_DEBUG PFX "AUX disable returned %d\n", ret);
263 308
264 /* Check PDA length */ 309 /* Check PDA length */
265 pda_size = le16_to_cpu(pda[0]); 310 pda_size = le16_to_cpu(pda[0]);
311 printk(KERN_DEBUG PFX "Actual PDA length %d, Max allowed %d\n",
312 pda_size, pda_len);
266 if (pda_size > pda_len) 313 if (pda_size > pda_len)
267 return -EINVAL; 314 return -EINVAL;
268 315
269 return 0; 316 return 0;
270} 317}
271EXPORT_SYMBOL(spectrum_read_pda); 318EXPORT_SYMBOL(hermes_read_pda);
272 319
273/* Parse PDA and write the records into the adapter */ 320/* Parse PDA and write the records into the adapter
274int 321 *
275spectrum_apply_pda(hermes_t *hw, const struct dblock *first_block, 322 * Attempt to write every records that is in the specified pda
276 __le16 *pda) 323 * which also has a valid production data record for the firmware.
324 */
325int hermes_apply_pda(hermes_t *hw,
326 const char *first_pdr,
327 const __le16 *pda)
277{ 328{
278 int ret; 329 int ret;
279 struct pdi *pdi; 330 const struct pdi *pdi;
280 struct pdr *first_pdr; 331 struct pdr *pdr;
281 const struct dblock *blk = first_block;
282
283 /* Skip all blocks to locate Plug Data References */
284 while (dblock_addr(blk) != BLOCK_END)
285 blk = (struct dblock *) &blk->data[dblock_len(blk)];
286 332
287 first_pdr = (struct pdr *) blk; 333 pdr = (struct pdr *) first_pdr;
288 334
289 /* Go through every PDI and plug them into the adapter */ 335 /* Go through every PDI and plug them into the adapter */
290 pdi = (struct pdi *) (pda + 2); 336 pdi = (const struct pdi *) (pda + 2);
291 while (pdi_id(pdi) != PDI_END) { 337 while (pdi_id(pdi) != PDI_END) {
292 ret = spectrum_plug_pdi(hw, first_pdr, pdi); 338 ret = hermes_plug_pdi(hw, pdr, pdi);
293 if (ret) 339 if (ret)
294 return ret; 340 return ret;
295 341
296 /* Increment to the next PDI */ 342 /* Increment to the next PDI */
297 pdi = (struct pdi *) &pdi->data[pdi_len(pdi)]; 343 pdi = (const struct pdi *) &pdi->data[pdi_len(pdi)];
298 } 344 }
299 return 0; 345 return 0;
300} 346}
301EXPORT_SYMBOL(spectrum_apply_pda); 347EXPORT_SYMBOL(hermes_apply_pda);
348
349/* Identify the total number of bytes in all blocks
350 * including the header data.
351 */
352size_t
353hermes_blocks_length(const char *first_block)
354{
355 const struct dblock *blk = (const struct dblock *) first_block;
356 int total_len = 0;
357 int len;
358
359 /* Skip all blocks to locate Plug Data References
360 * (Spectrum CS) */
361 while (dblock_addr(blk) != BLOCK_END) {
362 len = dblock_len(blk);
363 total_len += sizeof(*blk) + len;
364 blk = (struct dblock *) &blk->data[len];
365 }
366
367 return total_len;
368}
369EXPORT_SYMBOL(hermes_blocks_length);
370
371/*** Hermes programming ***/
302 372
303/* Load firmware blocks into the adapter */ 373/* Program the data blocks */
304int 374int hermes_program(hermes_t *hw, const char *first_block, const char *end)
305spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block)
306{ 375{
307 const struct dblock *blk; 376 const struct dblock *blk;
308 u32 blkaddr; 377 u32 blkaddr;
309 u32 blklen; 378 u32 blklen;
379#if LIMIT_PROGRAM_SIZE
380 u32 addr;
381 u32 len;
382#endif
383
384 blk = (const struct dblock *) first_block;
385
386 if ((const char *) blk > (end - sizeof(*blk)))
387 return -EIO;
310 388
311 blk = first_block;
312 blkaddr = dblock_addr(blk); 389 blkaddr = dblock_addr(blk);
313 blklen = dblock_len(blk); 390 blklen = dblock_len(blk);
314 391
315 while (dblock_addr(blk) != BLOCK_END) { 392 while ((blkaddr != BLOCK_END) &&
316 spectrum_aux_setaddr(hw, blkaddr); 393 (((const char *) blk + blklen) <= end)) {
394 printk(KERN_DEBUG PFX
395 "Programming block of length %d to address 0x%08x\n",
396 blklen, blkaddr);
397
398#if !LIMIT_PROGRAM_SIZE
399 /* wl_lkm driver splits this into writes of 2000 bytes */
400 hermes_aux_setaddr(hw, blkaddr);
317 hermes_write_bytes(hw, HERMES_AUXDATA, blk->data, 401 hermes_write_bytes(hw, HERMES_AUXDATA, blk->data,
318 blklen); 402 blklen);
403#else
404 len = (blklen < MAX_DL_SIZE) ? blklen : MAX_DL_SIZE;
405 addr = blkaddr;
406
407 while (addr < (blkaddr + blklen)) {
408 printk(KERN_DEBUG PFX
409 "Programming subblock of length %d "
410 "to address 0x%08x. Data @ %p\n",
411 len, addr, &blk->data[addr - blkaddr]);
412
413 hermes_aux_setaddr(hw, addr);
414 hermes_write_bytes(hw, HERMES_AUXDATA,
415 &blk->data[addr - blkaddr],
416 len);
417
418 addr += len;
419 len = ((blkaddr + blklen - addr) < MAX_DL_SIZE) ?
420 (blkaddr + blklen - addr) : MAX_DL_SIZE;
421 }
422#endif
423 blk = (const struct dblock *) &blk->data[blklen];
424
425 if ((const char *) blk > (end - sizeof(*blk)))
426 return -EIO;
319 427
320 blk = (struct dblock *) &blk->data[blklen];
321 blkaddr = dblock_addr(blk); 428 blkaddr = dblock_addr(blk);
322 blklen = dblock_len(blk); 429 blklen = dblock_len(blk);
323 } 430 }
324 return 0; 431 return 0;
325} 432}
326EXPORT_SYMBOL(spectrum_load_blocks); 433EXPORT_SYMBOL(hermes_program);
327 434
328static int __init init_hermes_dld(void) 435static int __init init_hermes_dld(void)
329{ 436{
diff --git a/drivers/net/wireless/hermes_dld.h b/drivers/net/wireless/hermes_dld.h
index 2c8892ac635b..af75c030b11b 100644
--- a/drivers/net/wireless/hermes_dld.h
+++ b/drivers/net/wireless/hermes_dld.h
@@ -27,19 +27,17 @@
27 27
28#include "hermes.h" 28#include "hermes.h"
29 29
30/* Position of PDA in the adapter memory */ 30int hermes_program(hermes_t *hw, const char *first_block, const char *end);
31#define EEPROM_ADDR 0x3000
32#define EEPROM_LEN 0x200
33#define PDA_OFFSET 0x100
34 31
35#define PDA_ADDR (EEPROM_ADDR + PDA_OFFSET) 32int hermes_read_pda(hermes_t *hw,
36#define PDA_WORDS ((EEPROM_LEN - PDA_OFFSET) / 2) 33 __le16 *pda,
34 u32 pda_addr,
35 u16 pda_len,
36 int use_eeprom);
37int hermes_apply_pda(hermes_t *hw,
38 const char *first_pdr,
39 const __le16 *pda);
37 40
38struct dblock; 41size_t hermes_blocks_length(const char *first_block);
39
40int spectrum_read_pda(hermes_t *hw, __le16 *pda, int pda_len);
41int spectrum_apply_pda(hermes_t *hw, const struct dblock *first_block,
42 __le16 *pda);
43int spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block);
44 42
45#endif /* _HERMES_DLD_H */ 43#endif /* _HERMES_DLD_H */
diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c
index 579873d0e8c9..2fb00183cd71 100644
--- a/drivers/net/wireless/spectrum_cs.c
+++ b/drivers/net/wireless/spectrum_cs.c
@@ -166,11 +166,12 @@ spectrum_reset(struct pcmcia_device *link, int idle)
166 */ 166 */
167static int 167static int
168spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link, 168spectrum_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 int ret; 172 int ret;
172 const unsigned char *ptr; 173 const unsigned char *ptr;
173 const struct dblock *first_block; 174 const unsigned char *first_block;
174 175
175 /* Plug Data Area (PDA) */ 176 /* Plug Data Area (PDA) */
176 __le16 pda[PDA_WORDS]; 177 __le16 pda[PDA_WORDS];
@@ -178,11 +179,11 @@ spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link,
178 /* Binary block begins after the 0x1A marker */ 179 /* Binary block begins after the 0x1A marker */
179 ptr = image; 180 ptr = image;
180 while (*ptr++ != TEXT_END); 181 while (*ptr++ != TEXT_END);
181 first_block = (const struct dblock *) ptr; 182 first_block = ptr;
182 183
183 /* Read the PDA */ 184 /* Read the PDA from EEPROM */
184 if (secondary) { 185 if (secondary) {
185 ret = spectrum_read_pda(hw, pda, sizeof(pda)); 186 ret = hermes_read_pda(hw, pda, PDA_ADDR, sizeof(pda), 1);
186 if (ret) 187 if (ret)
187 return ret; 188 return ret;
188 } 189 }
@@ -193,13 +194,15 @@ spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link,
193 return ret; 194 return ret;
194 195
195 /* Program the adapter with new firmware */ 196 /* Program the adapter with new firmware */
196 ret = spectrum_load_blocks(hw, first_block); 197 ret = hermes_program(hw, first_block, end);
197 if (ret) 198 if (ret)
198 return ret; 199 return ret;
199 200
200 /* Write the PDA to the adapter */ 201 /* Write the PDA to the adapter */
201 if (secondary) { 202 if (secondary) {
202 ret = spectrum_apply_pda(hw, first_block, pda); 203 size_t len = hermes_blocks_length(first_block);
204 ptr = first_block + len;
205 ret = hermes_apply_pda(hw, ptr, pda);
203 if (ret) 206 if (ret)
204 return ret; 207 return ret;
205 } 208 }
@@ -242,7 +245,8 @@ spectrum_dl_firmware(hermes_t *hw, struct pcmcia_device *link)
242 } 245 }
243 246
244 /* Load primary firmware */ 247 /* Load primary firmware */
245 ret = spectrum_dl_image(hw, link, fw_entry->data, 0); 248 ret = spectrum_dl_image(hw, link, fw_entry->data,
249 fw_entry->data + fw_entry->size, 0);
246 release_firmware(fw_entry); 250 release_firmware(fw_entry);
247 if (ret) { 251 if (ret) {
248 printk(KERN_ERR PFX "Primary firmware download failed\n"); 252 printk(KERN_ERR PFX "Primary firmware download failed\n");
@@ -257,7 +261,8 @@ spectrum_dl_firmware(hermes_t *hw, struct pcmcia_device *link)
257 } 261 }
258 262
259 /* Load secondary firmware */ 263 /* Load secondary firmware */
260 ret = spectrum_dl_image(hw, link, fw_entry->data, 1); 264 ret = spectrum_dl_image(hw, link, fw_entry->data,
265 fw_entry->data + fw_entry->size, 1);
261 release_firmware(fw_entry); 266 release_firmware(fw_entry);
262 if (ret) { 267 if (ret) {
263 printk(KERN_ERR PFX "Secondary firmware download failed\n"); 268 printk(KERN_ERR PFX "Secondary firmware download failed\n");