aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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");