diff options
Diffstat (limited to 'drivers/pcmcia/cistpl.c')
-rw-r--r-- | drivers/pcmcia/cistpl.c | 1490 |
1 files changed, 1490 insertions, 0 deletions
diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c new file mode 100644 index 000000000000..e29a6ddf2fd7 --- /dev/null +++ b/drivers/pcmcia/cistpl.c | |||
@@ -0,0 +1,1490 @@ | |||
1 | /* | ||
2 | * cistpl.c -- 16-bit PCMCIA Card Information Structure parser | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * The initial developer of the original code is David A. Hinds | ||
9 | * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds | ||
10 | * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. | ||
11 | * | ||
12 | * (C) 1999 David A. Hinds | ||
13 | */ | ||
14 | |||
15 | #include <linux/config.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/moduleparam.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/string.h> | ||
20 | #include <linux/major.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/timer.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/mm.h> | ||
25 | #include <linux/sched.h> | ||
26 | #include <linux/pci.h> | ||
27 | #include <linux/ioport.h> | ||
28 | #include <asm/io.h> | ||
29 | #include <asm/byteorder.h> | ||
30 | |||
31 | #include <pcmcia/cs_types.h> | ||
32 | #include <pcmcia/ss.h> | ||
33 | #include <pcmcia/cs.h> | ||
34 | #include <pcmcia/bulkmem.h> | ||
35 | #include <pcmcia/cisreg.h> | ||
36 | #include <pcmcia/cistpl.h> | ||
37 | #include "cs_internal.h" | ||
38 | |||
39 | static const u_char mantissa[] = { | ||
40 | 10, 12, 13, 15, 20, 25, 30, 35, | ||
41 | 40, 45, 50, 55, 60, 70, 80, 90 | ||
42 | }; | ||
43 | |||
44 | static const u_int exponent[] = { | ||
45 | 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 | ||
46 | }; | ||
47 | |||
48 | /* Convert an extended speed byte to a time in nanoseconds */ | ||
49 | #define SPEED_CVT(v) \ | ||
50 | (mantissa[(((v)>>3)&15)-1] * exponent[(v)&7] / 10) | ||
51 | /* Convert a power byte to a current in 0.1 microamps */ | ||
52 | #define POWER_CVT(v) \ | ||
53 | (mantissa[((v)>>3)&15] * exponent[(v)&7] / 10) | ||
54 | #define POWER_SCALE(v) (exponent[(v)&7]) | ||
55 | |||
56 | /* Upper limit on reasonable # of tuples */ | ||
57 | #define MAX_TUPLES 200 | ||
58 | |||
59 | /*====================================================================*/ | ||
60 | |||
61 | /* Parameters that can be set with 'insmod' */ | ||
62 | |||
63 | #define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444) | ||
64 | |||
65 | INT_MODULE_PARM(cis_width, 0); /* 16-bit CIS? */ | ||
66 | |||
67 | void release_cis_mem(struct pcmcia_socket *s) | ||
68 | { | ||
69 | if (s->cis_mem.flags & MAP_ACTIVE) { | ||
70 | s->cis_mem.flags &= ~MAP_ACTIVE; | ||
71 | s->ops->set_mem_map(s, &s->cis_mem); | ||
72 | if (s->cis_mem.res) { | ||
73 | release_resource(s->cis_mem.res); | ||
74 | kfree(s->cis_mem.res); | ||
75 | s->cis_mem.res = NULL; | ||
76 | } | ||
77 | iounmap(s->cis_virt); | ||
78 | s->cis_virt = NULL; | ||
79 | } | ||
80 | } | ||
81 | EXPORT_SYMBOL(release_cis_mem); | ||
82 | |||
83 | /* | ||
84 | * Map the card memory at "card_offset" into virtual space. | ||
85 | * If flags & MAP_ATTRIB, map the attribute space, otherwise | ||
86 | * map the memory space. | ||
87 | */ | ||
88 | static void __iomem * | ||
89 | set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags) | ||
90 | { | ||
91 | pccard_mem_map *mem = &s->cis_mem; | ||
92 | if (!(s->features & SS_CAP_STATIC_MAP) && mem->res == NULL) { | ||
93 | mem->res = find_mem_region(0, s->map_size, s->map_size, 0, s); | ||
94 | if (mem->res == NULL) { | ||
95 | printk(KERN_NOTICE "cs: unable to map card memory!\n"); | ||
96 | return NULL; | ||
97 | } | ||
98 | s->cis_virt = ioremap(mem->res->start, s->map_size); | ||
99 | } | ||
100 | mem->card_start = card_offset; | ||
101 | mem->flags = flags; | ||
102 | s->ops->set_mem_map(s, mem); | ||
103 | if (s->features & SS_CAP_STATIC_MAP) { | ||
104 | if (s->cis_virt) | ||
105 | iounmap(s->cis_virt); | ||
106 | s->cis_virt = ioremap(mem->static_start, s->map_size); | ||
107 | } | ||
108 | return s->cis_virt; | ||
109 | } | ||
110 | |||
111 | /*====================================================================== | ||
112 | |||
113 | Low-level functions to read and write CIS memory. I think the | ||
114 | write routine is only useful for writing one-byte registers. | ||
115 | |||
116 | ======================================================================*/ | ||
117 | |||
118 | /* Bits in attr field */ | ||
119 | #define IS_ATTR 1 | ||
120 | #define IS_INDIRECT 8 | ||
121 | |||
122 | int read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, | ||
123 | u_int len, void *ptr) | ||
124 | { | ||
125 | void __iomem *sys, *end; | ||
126 | unsigned char *buf = ptr; | ||
127 | |||
128 | cs_dbg(s, 3, "read_cis_mem(%d, %#x, %u)\n", attr, addr, len); | ||
129 | |||
130 | if (attr & IS_INDIRECT) { | ||
131 | /* Indirect accesses use a bunch of special registers at fixed | ||
132 | locations in common memory */ | ||
133 | u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN; | ||
134 | if (attr & IS_ATTR) { | ||
135 | addr *= 2; | ||
136 | flags = ICTRL0_AUTOINC; | ||
137 | } | ||
138 | |||
139 | sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0)); | ||
140 | if (!sys) { | ||
141 | memset(ptr, 0xff, len); | ||
142 | return -1; | ||
143 | } | ||
144 | |||
145 | writeb(flags, sys+CISREG_ICTRL0); | ||
146 | writeb(addr & 0xff, sys+CISREG_IADDR0); | ||
147 | writeb((addr>>8) & 0xff, sys+CISREG_IADDR1); | ||
148 | writeb((addr>>16) & 0xff, sys+CISREG_IADDR2); | ||
149 | writeb((addr>>24) & 0xff, sys+CISREG_IADDR3); | ||
150 | for ( ; len > 0; len--, buf++) | ||
151 | *buf = readb(sys+CISREG_IDATA0); | ||
152 | } else { | ||
153 | u_int inc = 1, card_offset, flags; | ||
154 | |||
155 | flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0); | ||
156 | if (attr) { | ||
157 | flags |= MAP_ATTRIB; | ||
158 | inc++; | ||
159 | addr *= 2; | ||
160 | } | ||
161 | |||
162 | card_offset = addr & ~(s->map_size-1); | ||
163 | while (len) { | ||
164 | sys = set_cis_map(s, card_offset, flags); | ||
165 | if (!sys) { | ||
166 | memset(ptr, 0xff, len); | ||
167 | return -1; | ||
168 | } | ||
169 | end = sys + s->map_size; | ||
170 | sys = sys + (addr & (s->map_size-1)); | ||
171 | for ( ; len > 0; len--, buf++, sys += inc) { | ||
172 | if (sys == end) | ||
173 | break; | ||
174 | *buf = readb(sys); | ||
175 | } | ||
176 | card_offset += s->map_size; | ||
177 | addr = 0; | ||
178 | } | ||
179 | } | ||
180 | cs_dbg(s, 3, " %#2.2x %#2.2x %#2.2x %#2.2x ...\n", | ||
181 | *(u_char *)(ptr+0), *(u_char *)(ptr+1), | ||
182 | *(u_char *)(ptr+2), *(u_char *)(ptr+3)); | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | void write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, | ||
187 | u_int len, void *ptr) | ||
188 | { | ||
189 | void __iomem *sys, *end; | ||
190 | unsigned char *buf = ptr; | ||
191 | |||
192 | cs_dbg(s, 3, "write_cis_mem(%d, %#x, %u)\n", attr, addr, len); | ||
193 | |||
194 | if (attr & IS_INDIRECT) { | ||
195 | /* Indirect accesses use a bunch of special registers at fixed | ||
196 | locations in common memory */ | ||
197 | u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN; | ||
198 | if (attr & IS_ATTR) { | ||
199 | addr *= 2; | ||
200 | flags = ICTRL0_AUTOINC; | ||
201 | } | ||
202 | |||
203 | sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0)); | ||
204 | if (!sys) | ||
205 | return; /* FIXME: Error */ | ||
206 | |||
207 | writeb(flags, sys+CISREG_ICTRL0); | ||
208 | writeb(addr & 0xff, sys+CISREG_IADDR0); | ||
209 | writeb((addr>>8) & 0xff, sys+CISREG_IADDR1); | ||
210 | writeb((addr>>16) & 0xff, sys+CISREG_IADDR2); | ||
211 | writeb((addr>>24) & 0xff, sys+CISREG_IADDR3); | ||
212 | for ( ; len > 0; len--, buf++) | ||
213 | writeb(*buf, sys+CISREG_IDATA0); | ||
214 | } else { | ||
215 | u_int inc = 1, card_offset, flags; | ||
216 | |||
217 | flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0); | ||
218 | if (attr & IS_ATTR) { | ||
219 | flags |= MAP_ATTRIB; | ||
220 | inc++; | ||
221 | addr *= 2; | ||
222 | } | ||
223 | |||
224 | card_offset = addr & ~(s->map_size-1); | ||
225 | while (len) { | ||
226 | sys = set_cis_map(s, card_offset, flags); | ||
227 | if (!sys) | ||
228 | return; /* FIXME: error */ | ||
229 | |||
230 | end = sys + s->map_size; | ||
231 | sys = sys + (addr & (s->map_size-1)); | ||
232 | for ( ; len > 0; len--, buf++, sys += inc) { | ||
233 | if (sys == end) | ||
234 | break; | ||
235 | writeb(*buf, sys); | ||
236 | } | ||
237 | card_offset += s->map_size; | ||
238 | addr = 0; | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | |||
243 | /*====================================================================== | ||
244 | |||
245 | This is a wrapper around read_cis_mem, with the same interface, | ||
246 | but which caches information, for cards whose CIS may not be | ||
247 | readable all the time. | ||
248 | |||
249 | ======================================================================*/ | ||
250 | |||
251 | static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, | ||
252 | u_int len, void *ptr) | ||
253 | { | ||
254 | struct cis_cache_entry *cis; | ||
255 | int ret; | ||
256 | |||
257 | if (s->fake_cis) { | ||
258 | if (s->fake_cis_len > addr+len) | ||
259 | memcpy(ptr, s->fake_cis+addr, len); | ||
260 | else | ||
261 | memset(ptr, 0xff, len); | ||
262 | return; | ||
263 | } | ||
264 | |||
265 | list_for_each_entry(cis, &s->cis_cache, node) { | ||
266 | if (cis->addr == addr && cis->len == len && cis->attr == attr) { | ||
267 | memcpy(ptr, cis->cache, len); | ||
268 | return; | ||
269 | } | ||
270 | } | ||
271 | |||
272 | #ifdef CONFIG_CARDBUS | ||
273 | if (s->state & SOCKET_CARDBUS) | ||
274 | ret = read_cb_mem(s, attr, addr, len, ptr); | ||
275 | else | ||
276 | #endif | ||
277 | ret = read_cis_mem(s, attr, addr, len, ptr); | ||
278 | |||
279 | if (ret == 0) { | ||
280 | /* Copy data into the cache */ | ||
281 | cis = kmalloc(sizeof(struct cis_cache_entry) + len, GFP_KERNEL); | ||
282 | if (cis) { | ||
283 | cis->addr = addr; | ||
284 | cis->len = len; | ||
285 | cis->attr = attr; | ||
286 | memcpy(cis->cache, ptr, len); | ||
287 | list_add(&cis->node, &s->cis_cache); | ||
288 | } | ||
289 | } | ||
290 | } | ||
291 | |||
292 | static void | ||
293 | remove_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len) | ||
294 | { | ||
295 | struct cis_cache_entry *cis; | ||
296 | |||
297 | list_for_each_entry(cis, &s->cis_cache, node) | ||
298 | if (cis->addr == addr && cis->len == len && cis->attr == attr) { | ||
299 | list_del(&cis->node); | ||
300 | kfree(cis); | ||
301 | break; | ||
302 | } | ||
303 | } | ||
304 | |||
305 | void destroy_cis_cache(struct pcmcia_socket *s) | ||
306 | { | ||
307 | struct list_head *l, *n; | ||
308 | |||
309 | list_for_each_safe(l, n, &s->cis_cache) { | ||
310 | struct cis_cache_entry *cis = list_entry(l, struct cis_cache_entry, node); | ||
311 | |||
312 | list_del(&cis->node); | ||
313 | kfree(cis); | ||
314 | } | ||
315 | |||
316 | /* | ||
317 | * If there was a fake CIS, destroy that as well. | ||
318 | */ | ||
319 | if (s->fake_cis) { | ||
320 | kfree(s->fake_cis); | ||
321 | s->fake_cis = NULL; | ||
322 | } | ||
323 | } | ||
324 | EXPORT_SYMBOL(destroy_cis_cache); | ||
325 | |||
326 | /*====================================================================== | ||
327 | |||
328 | This verifies if the CIS of a card matches what is in the CIS | ||
329 | cache. | ||
330 | |||
331 | ======================================================================*/ | ||
332 | |||
333 | int verify_cis_cache(struct pcmcia_socket *s) | ||
334 | { | ||
335 | struct cis_cache_entry *cis; | ||
336 | char *buf; | ||
337 | |||
338 | buf = kmalloc(256, GFP_KERNEL); | ||
339 | if (buf == NULL) | ||
340 | return -1; | ||
341 | list_for_each_entry(cis, &s->cis_cache, node) { | ||
342 | int len = cis->len; | ||
343 | |||
344 | if (len > 256) | ||
345 | len = 256; | ||
346 | #ifdef CONFIG_CARDBUS | ||
347 | if (s->state & SOCKET_CARDBUS) | ||
348 | read_cb_mem(s, cis->attr, cis->addr, len, buf); | ||
349 | else | ||
350 | #endif | ||
351 | read_cis_mem(s, cis->attr, cis->addr, len, buf); | ||
352 | |||
353 | if (memcmp(buf, cis->cache, len) != 0) { | ||
354 | kfree(buf); | ||
355 | return -1; | ||
356 | } | ||
357 | } | ||
358 | kfree(buf); | ||
359 | return 0; | ||
360 | } | ||
361 | |||
362 | /*====================================================================== | ||
363 | |||
364 | For really bad cards, we provide a facility for uploading a | ||
365 | replacement CIS. | ||
366 | |||
367 | ======================================================================*/ | ||
368 | |||
369 | int pcmcia_replace_cis(struct pcmcia_socket *s, cisdump_t *cis) | ||
370 | { | ||
371 | if (s->fake_cis != NULL) { | ||
372 | kfree(s->fake_cis); | ||
373 | s->fake_cis = NULL; | ||
374 | } | ||
375 | if (cis->Length > CISTPL_MAX_CIS_SIZE) | ||
376 | return CS_BAD_SIZE; | ||
377 | s->fake_cis = kmalloc(cis->Length, GFP_KERNEL); | ||
378 | if (s->fake_cis == NULL) | ||
379 | return CS_OUT_OF_RESOURCE; | ||
380 | s->fake_cis_len = cis->Length; | ||
381 | memcpy(s->fake_cis, cis->Data, cis->Length); | ||
382 | return CS_SUCCESS; | ||
383 | } | ||
384 | |||
385 | /*====================================================================== | ||
386 | |||
387 | The high-level CIS tuple services | ||
388 | |||
389 | ======================================================================*/ | ||
390 | |||
391 | typedef struct tuple_flags { | ||
392 | u_int link_space:4; | ||
393 | u_int has_link:1; | ||
394 | u_int mfc_fn:3; | ||
395 | u_int space:4; | ||
396 | } tuple_flags; | ||
397 | |||
398 | #define LINK_SPACE(f) (((tuple_flags *)(&(f)))->link_space) | ||
399 | #define HAS_LINK(f) (((tuple_flags *)(&(f)))->has_link) | ||
400 | #define MFC_FN(f) (((tuple_flags *)(&(f)))->mfc_fn) | ||
401 | #define SPACE(f) (((tuple_flags *)(&(f)))->space) | ||
402 | |||
403 | int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int func, tuple_t *tuple); | ||
404 | |||
405 | int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple_t *tuple) | ||
406 | { | ||
407 | if (!s) | ||
408 | return CS_BAD_HANDLE; | ||
409 | if (!(s->state & SOCKET_PRESENT)) | ||
410 | return CS_NO_CARD; | ||
411 | tuple->TupleLink = tuple->Flags = 0; | ||
412 | #ifdef CONFIG_CARDBUS | ||
413 | if (s->state & SOCKET_CARDBUS) { | ||
414 | struct pci_dev *dev = s->cb_dev; | ||
415 | u_int ptr; | ||
416 | pci_bus_read_config_dword(dev->subordinate, 0, PCI_CARDBUS_CIS, &ptr); | ||
417 | tuple->CISOffset = ptr & ~7; | ||
418 | SPACE(tuple->Flags) = (ptr & 7); | ||
419 | } else | ||
420 | #endif | ||
421 | { | ||
422 | /* Assume presence of a LONGLINK_C to address 0 */ | ||
423 | tuple->CISOffset = tuple->LinkOffset = 0; | ||
424 | SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1; | ||
425 | } | ||
426 | if (!(s->state & SOCKET_CARDBUS) && (s->functions > 1) && | ||
427 | !(tuple->Attributes & TUPLE_RETURN_COMMON)) { | ||
428 | cisdata_t req = tuple->DesiredTuple; | ||
429 | tuple->DesiredTuple = CISTPL_LONGLINK_MFC; | ||
430 | if (pccard_get_next_tuple(s, function, tuple) == CS_SUCCESS) { | ||
431 | tuple->DesiredTuple = CISTPL_LINKTARGET; | ||
432 | if (pccard_get_next_tuple(s, function, tuple) != CS_SUCCESS) | ||
433 | return CS_NO_MORE_ITEMS; | ||
434 | } else | ||
435 | tuple->CISOffset = tuple->TupleLink = 0; | ||
436 | tuple->DesiredTuple = req; | ||
437 | } | ||
438 | return pccard_get_next_tuple(s, function, tuple); | ||
439 | } | ||
440 | EXPORT_SYMBOL(pccard_get_first_tuple); | ||
441 | |||
442 | static int follow_link(struct pcmcia_socket *s, tuple_t *tuple) | ||
443 | { | ||
444 | u_char link[5]; | ||
445 | u_int ofs; | ||
446 | |||
447 | if (MFC_FN(tuple->Flags)) { | ||
448 | /* Get indirect link from the MFC tuple */ | ||
449 | read_cis_cache(s, LINK_SPACE(tuple->Flags), | ||
450 | tuple->LinkOffset, 5, link); | ||
451 | ofs = le32_to_cpu(*(u_int *)(link+1)); | ||
452 | SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR); | ||
453 | /* Move to the next indirect link */ | ||
454 | tuple->LinkOffset += 5; | ||
455 | MFC_FN(tuple->Flags)--; | ||
456 | } else if (HAS_LINK(tuple->Flags)) { | ||
457 | ofs = tuple->LinkOffset; | ||
458 | SPACE(tuple->Flags) = LINK_SPACE(tuple->Flags); | ||
459 | HAS_LINK(tuple->Flags) = 0; | ||
460 | } else { | ||
461 | return -1; | ||
462 | } | ||
463 | if (!(s->state & SOCKET_CARDBUS) && SPACE(tuple->Flags)) { | ||
464 | /* This is ugly, but a common CIS error is to code the long | ||
465 | link offset incorrectly, so we check the right spot... */ | ||
466 | read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link); | ||
467 | if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) && | ||
468 | (strncmp(link+2, "CIS", 3) == 0)) | ||
469 | return ofs; | ||
470 | remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5); | ||
471 | /* Then, we try the wrong spot... */ | ||
472 | ofs = ofs >> 1; | ||
473 | } | ||
474 | read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link); | ||
475 | if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) && | ||
476 | (strncmp(link+2, "CIS", 3) == 0)) | ||
477 | return ofs; | ||
478 | remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5); | ||
479 | return -1; | ||
480 | } | ||
481 | |||
482 | int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_t *tuple) | ||
483 | { | ||
484 | u_char link[2], tmp; | ||
485 | int ofs, i, attr; | ||
486 | |||
487 | if (!s) | ||
488 | return CS_BAD_HANDLE; | ||
489 | if (!(s->state & SOCKET_PRESENT)) | ||
490 | return CS_NO_CARD; | ||
491 | |||
492 | link[1] = tuple->TupleLink; | ||
493 | ofs = tuple->CISOffset + tuple->TupleLink; | ||
494 | attr = SPACE(tuple->Flags); | ||
495 | |||
496 | for (i = 0; i < MAX_TUPLES; i++) { | ||
497 | if (link[1] == 0xff) { | ||
498 | link[0] = CISTPL_END; | ||
499 | } else { | ||
500 | read_cis_cache(s, attr, ofs, 2, link); | ||
501 | if (link[0] == CISTPL_NULL) { | ||
502 | ofs++; continue; | ||
503 | } | ||
504 | } | ||
505 | |||
506 | /* End of chain? Follow long link if possible */ | ||
507 | if (link[0] == CISTPL_END) { | ||
508 | if ((ofs = follow_link(s, tuple)) < 0) | ||
509 | return CS_NO_MORE_ITEMS; | ||
510 | attr = SPACE(tuple->Flags); | ||
511 | read_cis_cache(s, attr, ofs, 2, link); | ||
512 | } | ||
513 | |||
514 | /* Is this a link tuple? Make a note of it */ | ||
515 | if ((link[0] == CISTPL_LONGLINK_A) || | ||
516 | (link[0] == CISTPL_LONGLINK_C) || | ||
517 | (link[0] == CISTPL_LONGLINK_MFC) || | ||
518 | (link[0] == CISTPL_LINKTARGET) || | ||
519 | (link[0] == CISTPL_INDIRECT) || | ||
520 | (link[0] == CISTPL_NO_LINK)) { | ||
521 | switch (link[0]) { | ||
522 | case CISTPL_LONGLINK_A: | ||
523 | HAS_LINK(tuple->Flags) = 1; | ||
524 | LINK_SPACE(tuple->Flags) = attr | IS_ATTR; | ||
525 | read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset); | ||
526 | break; | ||
527 | case CISTPL_LONGLINK_C: | ||
528 | HAS_LINK(tuple->Flags) = 1; | ||
529 | LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR; | ||
530 | read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset); | ||
531 | break; | ||
532 | case CISTPL_INDIRECT: | ||
533 | HAS_LINK(tuple->Flags) = 1; | ||
534 | LINK_SPACE(tuple->Flags) = IS_ATTR | IS_INDIRECT; | ||
535 | tuple->LinkOffset = 0; | ||
536 | break; | ||
537 | case CISTPL_LONGLINK_MFC: | ||
538 | tuple->LinkOffset = ofs + 3; | ||
539 | LINK_SPACE(tuple->Flags) = attr; | ||
540 | if (function == BIND_FN_ALL) { | ||
541 | /* Follow all the MFC links */ | ||
542 | read_cis_cache(s, attr, ofs+2, 1, &tmp); | ||
543 | MFC_FN(tuple->Flags) = tmp; | ||
544 | } else { | ||
545 | /* Follow exactly one of the links */ | ||
546 | MFC_FN(tuple->Flags) = 1; | ||
547 | tuple->LinkOffset += function * 5; | ||
548 | } | ||
549 | break; | ||
550 | case CISTPL_NO_LINK: | ||
551 | HAS_LINK(tuple->Flags) = 0; | ||
552 | break; | ||
553 | } | ||
554 | if ((tuple->Attributes & TUPLE_RETURN_LINK) && | ||
555 | (tuple->DesiredTuple == RETURN_FIRST_TUPLE)) | ||
556 | break; | ||
557 | } else | ||
558 | if (tuple->DesiredTuple == RETURN_FIRST_TUPLE) | ||
559 | break; | ||
560 | |||
561 | if (link[0] == tuple->DesiredTuple) | ||
562 | break; | ||
563 | ofs += link[1] + 2; | ||
564 | } | ||
565 | if (i == MAX_TUPLES) { | ||
566 | cs_dbg(s, 1, "cs: overrun in pcmcia_get_next_tuple\n"); | ||
567 | return CS_NO_MORE_ITEMS; | ||
568 | } | ||
569 | |||
570 | tuple->TupleCode = link[0]; | ||
571 | tuple->TupleLink = link[1]; | ||
572 | tuple->CISOffset = ofs + 2; | ||
573 | return CS_SUCCESS; | ||
574 | } | ||
575 | EXPORT_SYMBOL(pccard_get_next_tuple); | ||
576 | |||
577 | /*====================================================================*/ | ||
578 | |||
579 | #define _MIN(a, b) (((a) < (b)) ? (a) : (b)) | ||
580 | |||
581 | int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple) | ||
582 | { | ||
583 | u_int len; | ||
584 | |||
585 | if (!s) | ||
586 | return CS_BAD_HANDLE; | ||
587 | |||
588 | if (tuple->TupleLink < tuple->TupleOffset) | ||
589 | return CS_NO_MORE_ITEMS; | ||
590 | len = tuple->TupleLink - tuple->TupleOffset; | ||
591 | tuple->TupleDataLen = tuple->TupleLink; | ||
592 | if (len == 0) | ||
593 | return CS_SUCCESS; | ||
594 | read_cis_cache(s, SPACE(tuple->Flags), | ||
595 | tuple->CISOffset + tuple->TupleOffset, | ||
596 | _MIN(len, tuple->TupleDataMax), tuple->TupleData); | ||
597 | return CS_SUCCESS; | ||
598 | } | ||
599 | EXPORT_SYMBOL(pccard_get_tuple_data); | ||
600 | |||
601 | |||
602 | /*====================================================================== | ||
603 | |||
604 | Parsing routines for individual tuples | ||
605 | |||
606 | ======================================================================*/ | ||
607 | |||
608 | static int parse_device(tuple_t *tuple, cistpl_device_t *device) | ||
609 | { | ||
610 | int i; | ||
611 | u_char scale; | ||
612 | u_char *p, *q; | ||
613 | |||
614 | p = (u_char *)tuple->TupleData; | ||
615 | q = p + tuple->TupleDataLen; | ||
616 | |||
617 | device->ndev = 0; | ||
618 | for (i = 0; i < CISTPL_MAX_DEVICES; i++) { | ||
619 | |||
620 | if (*p == 0xff) break; | ||
621 | device->dev[i].type = (*p >> 4); | ||
622 | device->dev[i].wp = (*p & 0x08) ? 1 : 0; | ||
623 | switch (*p & 0x07) { | ||
624 | case 0: device->dev[i].speed = 0; break; | ||
625 | case 1: device->dev[i].speed = 250; break; | ||
626 | case 2: device->dev[i].speed = 200; break; | ||
627 | case 3: device->dev[i].speed = 150; break; | ||
628 | case 4: device->dev[i].speed = 100; break; | ||
629 | case 7: | ||
630 | if (++p == q) return CS_BAD_TUPLE; | ||
631 | device->dev[i].speed = SPEED_CVT(*p); | ||
632 | while (*p & 0x80) | ||
633 | if (++p == q) return CS_BAD_TUPLE; | ||
634 | break; | ||
635 | default: | ||
636 | return CS_BAD_TUPLE; | ||
637 | } | ||
638 | |||
639 | if (++p == q) return CS_BAD_TUPLE; | ||
640 | if (*p == 0xff) break; | ||
641 | scale = *p & 7; | ||
642 | if (scale == 7) return CS_BAD_TUPLE; | ||
643 | device->dev[i].size = ((*p >> 3) + 1) * (512 << (scale*2)); | ||
644 | device->ndev++; | ||
645 | if (++p == q) break; | ||
646 | } | ||
647 | |||
648 | return CS_SUCCESS; | ||
649 | } | ||
650 | |||
651 | /*====================================================================*/ | ||
652 | |||
653 | static int parse_checksum(tuple_t *tuple, cistpl_checksum_t *csum) | ||
654 | { | ||
655 | u_char *p; | ||
656 | if (tuple->TupleDataLen < 5) | ||
657 | return CS_BAD_TUPLE; | ||
658 | p = (u_char *)tuple->TupleData; | ||
659 | csum->addr = tuple->CISOffset+(short)le16_to_cpu(*(u_short *)p)-2; | ||
660 | csum->len = le16_to_cpu(*(u_short *)(p + 2)); | ||
661 | csum->sum = *(p+4); | ||
662 | return CS_SUCCESS; | ||
663 | } | ||
664 | |||
665 | /*====================================================================*/ | ||
666 | |||
667 | static int parse_longlink(tuple_t *tuple, cistpl_longlink_t *link) | ||
668 | { | ||
669 | if (tuple->TupleDataLen < 4) | ||
670 | return CS_BAD_TUPLE; | ||
671 | link->addr = le32_to_cpu(*(u_int *)tuple->TupleData); | ||
672 | return CS_SUCCESS; | ||
673 | } | ||
674 | |||
675 | /*====================================================================*/ | ||
676 | |||
677 | static int parse_longlink_mfc(tuple_t *tuple, | ||
678 | cistpl_longlink_mfc_t *link) | ||
679 | { | ||
680 | u_char *p; | ||
681 | int i; | ||
682 | |||
683 | p = (u_char *)tuple->TupleData; | ||
684 | |||
685 | link->nfn = *p; p++; | ||
686 | if (tuple->TupleDataLen <= link->nfn*5) | ||
687 | return CS_BAD_TUPLE; | ||
688 | for (i = 0; i < link->nfn; i++) { | ||
689 | link->fn[i].space = *p; p++; | ||
690 | link->fn[i].addr = le32_to_cpu(*(u_int *)p); p += 4; | ||
691 | } | ||
692 | return CS_SUCCESS; | ||
693 | } | ||
694 | |||
695 | /*====================================================================*/ | ||
696 | |||
697 | static int parse_strings(u_char *p, u_char *q, int max, | ||
698 | char *s, u_char *ofs, u_char *found) | ||
699 | { | ||
700 | int i, j, ns; | ||
701 | |||
702 | if (p == q) return CS_BAD_TUPLE; | ||
703 | ns = 0; j = 0; | ||
704 | for (i = 0; i < max; i++) { | ||
705 | if (*p == 0xff) break; | ||
706 | ofs[i] = j; | ||
707 | ns++; | ||
708 | for (;;) { | ||
709 | s[j++] = (*p == 0xff) ? '\0' : *p; | ||
710 | if ((*p == '\0') || (*p == 0xff)) break; | ||
711 | if (++p == q) return CS_BAD_TUPLE; | ||
712 | } | ||
713 | if ((*p == 0xff) || (++p == q)) break; | ||
714 | } | ||
715 | if (found) { | ||
716 | *found = ns; | ||
717 | return CS_SUCCESS; | ||
718 | } else { | ||
719 | return (ns == max) ? CS_SUCCESS : CS_BAD_TUPLE; | ||
720 | } | ||
721 | } | ||
722 | |||
723 | /*====================================================================*/ | ||
724 | |||
725 | static int parse_vers_1(tuple_t *tuple, cistpl_vers_1_t *vers_1) | ||
726 | { | ||
727 | u_char *p, *q; | ||
728 | |||
729 | p = (u_char *)tuple->TupleData; | ||
730 | q = p + tuple->TupleDataLen; | ||
731 | |||
732 | vers_1->major = *p; p++; | ||
733 | vers_1->minor = *p; p++; | ||
734 | if (p >= q) return CS_BAD_TUPLE; | ||
735 | |||
736 | return parse_strings(p, q, CISTPL_VERS_1_MAX_PROD_STRINGS, | ||
737 | vers_1->str, vers_1->ofs, &vers_1->ns); | ||
738 | } | ||
739 | |||
740 | /*====================================================================*/ | ||
741 | |||
742 | static int parse_altstr(tuple_t *tuple, cistpl_altstr_t *altstr) | ||
743 | { | ||
744 | u_char *p, *q; | ||
745 | |||
746 | p = (u_char *)tuple->TupleData; | ||
747 | q = p + tuple->TupleDataLen; | ||
748 | |||
749 | return parse_strings(p, q, CISTPL_MAX_ALTSTR_STRINGS, | ||
750 | altstr->str, altstr->ofs, &altstr->ns); | ||
751 | } | ||
752 | |||
753 | /*====================================================================*/ | ||
754 | |||
755 | static int parse_jedec(tuple_t *tuple, cistpl_jedec_t *jedec) | ||
756 | { | ||
757 | u_char *p, *q; | ||
758 | int nid; | ||
759 | |||
760 | p = (u_char *)tuple->TupleData; | ||
761 | q = p + tuple->TupleDataLen; | ||
762 | |||
763 | for (nid = 0; nid < CISTPL_MAX_DEVICES; nid++) { | ||
764 | if (p > q-2) break; | ||
765 | jedec->id[nid].mfr = p[0]; | ||
766 | jedec->id[nid].info = p[1]; | ||
767 | p += 2; | ||
768 | } | ||
769 | jedec->nid = nid; | ||
770 | return CS_SUCCESS; | ||
771 | } | ||
772 | |||
773 | /*====================================================================*/ | ||
774 | |||
775 | static int parse_manfid(tuple_t *tuple, cistpl_manfid_t *m) | ||
776 | { | ||
777 | u_short *p; | ||
778 | if (tuple->TupleDataLen < 4) | ||
779 | return CS_BAD_TUPLE; | ||
780 | p = (u_short *)tuple->TupleData; | ||
781 | m->manf = le16_to_cpu(p[0]); | ||
782 | m->card = le16_to_cpu(p[1]); | ||
783 | return CS_SUCCESS; | ||
784 | } | ||
785 | |||
786 | /*====================================================================*/ | ||
787 | |||
788 | static int parse_funcid(tuple_t *tuple, cistpl_funcid_t *f) | ||
789 | { | ||
790 | u_char *p; | ||
791 | if (tuple->TupleDataLen < 2) | ||
792 | return CS_BAD_TUPLE; | ||
793 | p = (u_char *)tuple->TupleData; | ||
794 | f->func = p[0]; | ||
795 | f->sysinit = p[1]; | ||
796 | return CS_SUCCESS; | ||
797 | } | ||
798 | |||
799 | /*====================================================================*/ | ||
800 | |||
801 | static int parse_funce(tuple_t *tuple, cistpl_funce_t *f) | ||
802 | { | ||
803 | u_char *p; | ||
804 | int i; | ||
805 | if (tuple->TupleDataLen < 1) | ||
806 | return CS_BAD_TUPLE; | ||
807 | p = (u_char *)tuple->TupleData; | ||
808 | f->type = p[0]; | ||
809 | for (i = 1; i < tuple->TupleDataLen; i++) | ||
810 | f->data[i-1] = p[i]; | ||
811 | return CS_SUCCESS; | ||
812 | } | ||
813 | |||
814 | /*====================================================================*/ | ||
815 | |||
816 | static int parse_config(tuple_t *tuple, cistpl_config_t *config) | ||
817 | { | ||
818 | int rasz, rmsz, i; | ||
819 | u_char *p; | ||
820 | |||
821 | p = (u_char *)tuple->TupleData; | ||
822 | rasz = *p & 0x03; | ||
823 | rmsz = (*p & 0x3c) >> 2; | ||
824 | if (tuple->TupleDataLen < rasz+rmsz+4) | ||
825 | return CS_BAD_TUPLE; | ||
826 | config->last_idx = *(++p); | ||
827 | p++; | ||
828 | config->base = 0; | ||
829 | for (i = 0; i <= rasz; i++) | ||
830 | config->base += p[i] << (8*i); | ||
831 | p += rasz+1; | ||
832 | for (i = 0; i < 4; i++) | ||
833 | config->rmask[i] = 0; | ||
834 | for (i = 0; i <= rmsz; i++) | ||
835 | config->rmask[i>>2] += p[i] << (8*(i%4)); | ||
836 | config->subtuples = tuple->TupleDataLen - (rasz+rmsz+4); | ||
837 | return CS_SUCCESS; | ||
838 | } | ||
839 | |||
840 | /*====================================================================== | ||
841 | |||
842 | The following routines are all used to parse the nightmarish | ||
843 | config table entries. | ||
844 | |||
845 | ======================================================================*/ | ||
846 | |||
847 | static u_char *parse_power(u_char *p, u_char *q, | ||
848 | cistpl_power_t *pwr) | ||
849 | { | ||
850 | int i; | ||
851 | u_int scale; | ||
852 | |||
853 | if (p == q) return NULL; | ||
854 | pwr->present = *p; | ||
855 | pwr->flags = 0; | ||
856 | p++; | ||
857 | for (i = 0; i < 7; i++) | ||
858 | if (pwr->present & (1<<i)) { | ||
859 | if (p == q) return NULL; | ||
860 | pwr->param[i] = POWER_CVT(*p); | ||
861 | scale = POWER_SCALE(*p); | ||
862 | while (*p & 0x80) { | ||
863 | if (++p == q) return NULL; | ||
864 | if ((*p & 0x7f) < 100) | ||
865 | pwr->param[i] += (*p & 0x7f) * scale / 100; | ||
866 | else if (*p == 0x7d) | ||
867 | pwr->flags |= CISTPL_POWER_HIGHZ_OK; | ||
868 | else if (*p == 0x7e) | ||
869 | pwr->param[i] = 0; | ||
870 | else if (*p == 0x7f) | ||
871 | pwr->flags |= CISTPL_POWER_HIGHZ_REQ; | ||
872 | else | ||
873 | return NULL; | ||
874 | } | ||
875 | p++; | ||
876 | } | ||
877 | return p; | ||
878 | } | ||
879 | |||
880 | /*====================================================================*/ | ||
881 | |||
882 | static u_char *parse_timing(u_char *p, u_char *q, | ||
883 | cistpl_timing_t *timing) | ||
884 | { | ||
885 | u_char scale; | ||
886 | |||
887 | if (p == q) return NULL; | ||
888 | scale = *p; | ||
889 | if ((scale & 3) != 3) { | ||
890 | if (++p == q) return NULL; | ||
891 | timing->wait = SPEED_CVT(*p); | ||
892 | timing->waitscale = exponent[scale & 3]; | ||
893 | } else | ||
894 | timing->wait = 0; | ||
895 | scale >>= 2; | ||
896 | if ((scale & 7) != 7) { | ||
897 | if (++p == q) return NULL; | ||
898 | timing->ready = SPEED_CVT(*p); | ||
899 | timing->rdyscale = exponent[scale & 7]; | ||
900 | } else | ||
901 | timing->ready = 0; | ||
902 | scale >>= 3; | ||
903 | if (scale != 7) { | ||
904 | if (++p == q) return NULL; | ||
905 | timing->reserved = SPEED_CVT(*p); | ||
906 | timing->rsvscale = exponent[scale]; | ||
907 | } else | ||
908 | timing->reserved = 0; | ||
909 | p++; | ||
910 | return p; | ||
911 | } | ||
912 | |||
913 | /*====================================================================*/ | ||
914 | |||
915 | static u_char *parse_io(u_char *p, u_char *q, cistpl_io_t *io) | ||
916 | { | ||
917 | int i, j, bsz, lsz; | ||
918 | |||
919 | if (p == q) return NULL; | ||
920 | io->flags = *p; | ||
921 | |||
922 | if (!(*p & 0x80)) { | ||
923 | io->nwin = 1; | ||
924 | io->win[0].base = 0; | ||
925 | io->win[0].len = (1 << (io->flags & CISTPL_IO_LINES_MASK)); | ||
926 | return p+1; | ||
927 | } | ||
928 | |||
929 | if (++p == q) return NULL; | ||
930 | io->nwin = (*p & 0x0f) + 1; | ||
931 | bsz = (*p & 0x30) >> 4; | ||
932 | if (bsz == 3) bsz++; | ||
933 | lsz = (*p & 0xc0) >> 6; | ||
934 | if (lsz == 3) lsz++; | ||
935 | p++; | ||
936 | |||
937 | for (i = 0; i < io->nwin; i++) { | ||
938 | io->win[i].base = 0; | ||
939 | io->win[i].len = 1; | ||
940 | for (j = 0; j < bsz; j++, p++) { | ||
941 | if (p == q) return NULL; | ||
942 | io->win[i].base += *p << (j*8); | ||
943 | } | ||
944 | for (j = 0; j < lsz; j++, p++) { | ||
945 | if (p == q) return NULL; | ||
946 | io->win[i].len += *p << (j*8); | ||
947 | } | ||
948 | } | ||
949 | return p; | ||
950 | } | ||
951 | |||
952 | /*====================================================================*/ | ||
953 | |||
954 | static u_char *parse_mem(u_char *p, u_char *q, cistpl_mem_t *mem) | ||
955 | { | ||
956 | int i, j, asz, lsz, has_ha; | ||
957 | u_int len, ca, ha; | ||
958 | |||
959 | if (p == q) return NULL; | ||
960 | |||
961 | mem->nwin = (*p & 0x07) + 1; | ||
962 | lsz = (*p & 0x18) >> 3; | ||
963 | asz = (*p & 0x60) >> 5; | ||
964 | has_ha = (*p & 0x80); | ||
965 | if (++p == q) return NULL; | ||
966 | |||
967 | for (i = 0; i < mem->nwin; i++) { | ||
968 | len = ca = ha = 0; | ||
969 | for (j = 0; j < lsz; j++, p++) { | ||
970 | if (p == q) return NULL; | ||
971 | len += *p << (j*8); | ||
972 | } | ||
973 | for (j = 0; j < asz; j++, p++) { | ||
974 | if (p == q) return NULL; | ||
975 | ca += *p << (j*8); | ||
976 | } | ||
977 | if (has_ha) | ||
978 | for (j = 0; j < asz; j++, p++) { | ||
979 | if (p == q) return NULL; | ||
980 | ha += *p << (j*8); | ||
981 | } | ||
982 | mem->win[i].len = len << 8; | ||
983 | mem->win[i].card_addr = ca << 8; | ||
984 | mem->win[i].host_addr = ha << 8; | ||
985 | } | ||
986 | return p; | ||
987 | } | ||
988 | |||
989 | /*====================================================================*/ | ||
990 | |||
991 | static u_char *parse_irq(u_char *p, u_char *q, cistpl_irq_t *irq) | ||
992 | { | ||
993 | if (p == q) return NULL; | ||
994 | irq->IRQInfo1 = *p; p++; | ||
995 | if (irq->IRQInfo1 & IRQ_INFO2_VALID) { | ||
996 | if (p+2 > q) return NULL; | ||
997 | irq->IRQInfo2 = (p[1]<<8) + p[0]; | ||
998 | p += 2; | ||
999 | } | ||
1000 | return p; | ||
1001 | } | ||
1002 | |||
1003 | /*====================================================================*/ | ||
1004 | |||
1005 | static int parse_cftable_entry(tuple_t *tuple, | ||
1006 | cistpl_cftable_entry_t *entry) | ||
1007 | { | ||
1008 | u_char *p, *q, features; | ||
1009 | |||
1010 | p = tuple->TupleData; | ||
1011 | q = p + tuple->TupleDataLen; | ||
1012 | entry->index = *p & 0x3f; | ||
1013 | entry->flags = 0; | ||
1014 | if (*p & 0x40) | ||
1015 | entry->flags |= CISTPL_CFTABLE_DEFAULT; | ||
1016 | if (*p & 0x80) { | ||
1017 | if (++p == q) return CS_BAD_TUPLE; | ||
1018 | if (*p & 0x10) | ||
1019 | entry->flags |= CISTPL_CFTABLE_BVDS; | ||
1020 | if (*p & 0x20) | ||
1021 | entry->flags |= CISTPL_CFTABLE_WP; | ||
1022 | if (*p & 0x40) | ||
1023 | entry->flags |= CISTPL_CFTABLE_RDYBSY; | ||
1024 | if (*p & 0x80) | ||
1025 | entry->flags |= CISTPL_CFTABLE_MWAIT; | ||
1026 | entry->interface = *p & 0x0f; | ||
1027 | } else | ||
1028 | entry->interface = 0; | ||
1029 | |||
1030 | /* Process optional features */ | ||
1031 | if (++p == q) return CS_BAD_TUPLE; | ||
1032 | features = *p; p++; | ||
1033 | |||
1034 | /* Power options */ | ||
1035 | if ((features & 3) > 0) { | ||
1036 | p = parse_power(p, q, &entry->vcc); | ||
1037 | if (p == NULL) return CS_BAD_TUPLE; | ||
1038 | } else | ||
1039 | entry->vcc.present = 0; | ||
1040 | if ((features & 3) > 1) { | ||
1041 | p = parse_power(p, q, &entry->vpp1); | ||
1042 | if (p == NULL) return CS_BAD_TUPLE; | ||
1043 | } else | ||
1044 | entry->vpp1.present = 0; | ||
1045 | if ((features & 3) > 2) { | ||
1046 | p = parse_power(p, q, &entry->vpp2); | ||
1047 | if (p == NULL) return CS_BAD_TUPLE; | ||
1048 | } else | ||
1049 | entry->vpp2.present = 0; | ||
1050 | |||
1051 | /* Timing options */ | ||
1052 | if (features & 0x04) { | ||
1053 | p = parse_timing(p, q, &entry->timing); | ||
1054 | if (p == NULL) return CS_BAD_TUPLE; | ||
1055 | } else { | ||
1056 | entry->timing.wait = 0; | ||
1057 | entry->timing.ready = 0; | ||
1058 | entry->timing.reserved = 0; | ||
1059 | } | ||
1060 | |||
1061 | /* I/O window options */ | ||
1062 | if (features & 0x08) { | ||
1063 | p = parse_io(p, q, &entry->io); | ||
1064 | if (p == NULL) return CS_BAD_TUPLE; | ||
1065 | } else | ||
1066 | entry->io.nwin = 0; | ||
1067 | |||
1068 | /* Interrupt options */ | ||
1069 | if (features & 0x10) { | ||
1070 | p = parse_irq(p, q, &entry->irq); | ||
1071 | if (p == NULL) return CS_BAD_TUPLE; | ||
1072 | } else | ||
1073 | entry->irq.IRQInfo1 = 0; | ||
1074 | |||
1075 | switch (features & 0x60) { | ||
1076 | case 0x00: | ||
1077 | entry->mem.nwin = 0; | ||
1078 | break; | ||
1079 | case 0x20: | ||
1080 | entry->mem.nwin = 1; | ||
1081 | entry->mem.win[0].len = le16_to_cpu(*(u_short *)p) << 8; | ||
1082 | entry->mem.win[0].card_addr = 0; | ||
1083 | entry->mem.win[0].host_addr = 0; | ||
1084 | p += 2; | ||
1085 | if (p > q) return CS_BAD_TUPLE; | ||
1086 | break; | ||
1087 | case 0x40: | ||
1088 | entry->mem.nwin = 1; | ||
1089 | entry->mem.win[0].len = le16_to_cpu(*(u_short *)p) << 8; | ||
1090 | entry->mem.win[0].card_addr = | ||
1091 | le16_to_cpu(*(u_short *)(p+2)) << 8; | ||
1092 | entry->mem.win[0].host_addr = 0; | ||
1093 | p += 4; | ||
1094 | if (p > q) return CS_BAD_TUPLE; | ||
1095 | break; | ||
1096 | case 0x60: | ||
1097 | p = parse_mem(p, q, &entry->mem); | ||
1098 | if (p == NULL) return CS_BAD_TUPLE; | ||
1099 | break; | ||
1100 | } | ||
1101 | |||
1102 | /* Misc features */ | ||
1103 | if (features & 0x80) { | ||
1104 | if (p == q) return CS_BAD_TUPLE; | ||
1105 | entry->flags |= (*p << 8); | ||
1106 | while (*p & 0x80) | ||
1107 | if (++p == q) return CS_BAD_TUPLE; | ||
1108 | p++; | ||
1109 | } | ||
1110 | |||
1111 | entry->subtuples = q-p; | ||
1112 | |||
1113 | return CS_SUCCESS; | ||
1114 | } | ||
1115 | |||
1116 | /*====================================================================*/ | ||
1117 | |||
1118 | #ifdef CONFIG_CARDBUS | ||
1119 | |||
1120 | static int parse_bar(tuple_t *tuple, cistpl_bar_t *bar) | ||
1121 | { | ||
1122 | u_char *p; | ||
1123 | if (tuple->TupleDataLen < 6) | ||
1124 | return CS_BAD_TUPLE; | ||
1125 | p = (u_char *)tuple->TupleData; | ||
1126 | bar->attr = *p; | ||
1127 | p += 2; | ||
1128 | bar->size = le32_to_cpu(*(u_int *)p); | ||
1129 | return CS_SUCCESS; | ||
1130 | } | ||
1131 | |||
1132 | static int parse_config_cb(tuple_t *tuple, cistpl_config_t *config) | ||
1133 | { | ||
1134 | u_char *p; | ||
1135 | |||
1136 | p = (u_char *)tuple->TupleData; | ||
1137 | if ((*p != 3) || (tuple->TupleDataLen < 6)) | ||
1138 | return CS_BAD_TUPLE; | ||
1139 | config->last_idx = *(++p); | ||
1140 | p++; | ||
1141 | config->base = le32_to_cpu(*(u_int *)p); | ||
1142 | config->subtuples = tuple->TupleDataLen - 6; | ||
1143 | return CS_SUCCESS; | ||
1144 | } | ||
1145 | |||
1146 | static int parse_cftable_entry_cb(tuple_t *tuple, | ||
1147 | cistpl_cftable_entry_cb_t *entry) | ||
1148 | { | ||
1149 | u_char *p, *q, features; | ||
1150 | |||
1151 | p = tuple->TupleData; | ||
1152 | q = p + tuple->TupleDataLen; | ||
1153 | entry->index = *p & 0x3f; | ||
1154 | entry->flags = 0; | ||
1155 | if (*p & 0x40) | ||
1156 | entry->flags |= CISTPL_CFTABLE_DEFAULT; | ||
1157 | |||
1158 | /* Process optional features */ | ||
1159 | if (++p == q) return CS_BAD_TUPLE; | ||
1160 | features = *p; p++; | ||
1161 | |||
1162 | /* Power options */ | ||
1163 | if ((features & 3) > 0) { | ||
1164 | p = parse_power(p, q, &entry->vcc); | ||
1165 | if (p == NULL) return CS_BAD_TUPLE; | ||
1166 | } else | ||
1167 | entry->vcc.present = 0; | ||
1168 | if ((features & 3) > 1) { | ||
1169 | p = parse_power(p, q, &entry->vpp1); | ||
1170 | if (p == NULL) return CS_BAD_TUPLE; | ||
1171 | } else | ||
1172 | entry->vpp1.present = 0; | ||
1173 | if ((features & 3) > 2) { | ||
1174 | p = parse_power(p, q, &entry->vpp2); | ||
1175 | if (p == NULL) return CS_BAD_TUPLE; | ||
1176 | } else | ||
1177 | entry->vpp2.present = 0; | ||
1178 | |||
1179 | /* I/O window options */ | ||
1180 | if (features & 0x08) { | ||
1181 | if (p == q) return CS_BAD_TUPLE; | ||
1182 | entry->io = *p; p++; | ||
1183 | } else | ||
1184 | entry->io = 0; | ||
1185 | |||
1186 | /* Interrupt options */ | ||
1187 | if (features & 0x10) { | ||
1188 | p = parse_irq(p, q, &entry->irq); | ||
1189 | if (p == NULL) return CS_BAD_TUPLE; | ||
1190 | } else | ||
1191 | entry->irq.IRQInfo1 = 0; | ||
1192 | |||
1193 | if (features & 0x20) { | ||
1194 | if (p == q) return CS_BAD_TUPLE; | ||
1195 | entry->mem = *p; p++; | ||
1196 | } else | ||
1197 | entry->mem = 0; | ||
1198 | |||
1199 | /* Misc features */ | ||
1200 | if (features & 0x80) { | ||
1201 | if (p == q) return CS_BAD_TUPLE; | ||
1202 | entry->flags |= (*p << 8); | ||
1203 | if (*p & 0x80) { | ||
1204 | if (++p == q) return CS_BAD_TUPLE; | ||
1205 | entry->flags |= (*p << 16); | ||
1206 | } | ||
1207 | while (*p & 0x80) | ||
1208 | if (++p == q) return CS_BAD_TUPLE; | ||
1209 | p++; | ||
1210 | } | ||
1211 | |||
1212 | entry->subtuples = q-p; | ||
1213 | |||
1214 | return CS_SUCCESS; | ||
1215 | } | ||
1216 | |||
1217 | #endif | ||
1218 | |||
1219 | /*====================================================================*/ | ||
1220 | |||
1221 | static int parse_device_geo(tuple_t *tuple, cistpl_device_geo_t *geo) | ||
1222 | { | ||
1223 | u_char *p, *q; | ||
1224 | int n; | ||
1225 | |||
1226 | p = (u_char *)tuple->TupleData; | ||
1227 | q = p + tuple->TupleDataLen; | ||
1228 | |||
1229 | for (n = 0; n < CISTPL_MAX_DEVICES; n++) { | ||
1230 | if (p > q-6) break; | ||
1231 | geo->geo[n].buswidth = p[0]; | ||
1232 | geo->geo[n].erase_block = 1 << (p[1]-1); | ||
1233 | geo->geo[n].read_block = 1 << (p[2]-1); | ||
1234 | geo->geo[n].write_block = 1 << (p[3]-1); | ||
1235 | geo->geo[n].partition = 1 << (p[4]-1); | ||
1236 | geo->geo[n].interleave = 1 << (p[5]-1); | ||
1237 | p += 6; | ||
1238 | } | ||
1239 | geo->ngeo = n; | ||
1240 | return CS_SUCCESS; | ||
1241 | } | ||
1242 | |||
1243 | /*====================================================================*/ | ||
1244 | |||
1245 | static int parse_vers_2(tuple_t *tuple, cistpl_vers_2_t *v2) | ||
1246 | { | ||
1247 | u_char *p, *q; | ||
1248 | |||
1249 | if (tuple->TupleDataLen < 10) | ||
1250 | return CS_BAD_TUPLE; | ||
1251 | |||
1252 | p = tuple->TupleData; | ||
1253 | q = p + tuple->TupleDataLen; | ||
1254 | |||
1255 | v2->vers = p[0]; | ||
1256 | v2->comply = p[1]; | ||
1257 | v2->dindex = le16_to_cpu(*(u_short *)(p+2)); | ||
1258 | v2->vspec8 = p[6]; | ||
1259 | v2->vspec9 = p[7]; | ||
1260 | v2->nhdr = p[8]; | ||
1261 | p += 9; | ||
1262 | return parse_strings(p, q, 2, v2->str, &v2->vendor, NULL); | ||
1263 | } | ||
1264 | |||
1265 | /*====================================================================*/ | ||
1266 | |||
1267 | static int parse_org(tuple_t *tuple, cistpl_org_t *org) | ||
1268 | { | ||
1269 | u_char *p, *q; | ||
1270 | int i; | ||
1271 | |||
1272 | p = tuple->TupleData; | ||
1273 | q = p + tuple->TupleDataLen; | ||
1274 | if (p == q) return CS_BAD_TUPLE; | ||
1275 | org->data_org = *p; | ||
1276 | if (++p == q) return CS_BAD_TUPLE; | ||
1277 | for (i = 0; i < 30; i++) { | ||
1278 | org->desc[i] = *p; | ||
1279 | if (*p == '\0') break; | ||
1280 | if (++p == q) return CS_BAD_TUPLE; | ||
1281 | } | ||
1282 | return CS_SUCCESS; | ||
1283 | } | ||
1284 | |||
1285 | /*====================================================================*/ | ||
1286 | |||
1287 | static int parse_format(tuple_t *tuple, cistpl_format_t *fmt) | ||
1288 | { | ||
1289 | u_char *p; | ||
1290 | |||
1291 | if (tuple->TupleDataLen < 10) | ||
1292 | return CS_BAD_TUPLE; | ||
1293 | |||
1294 | p = tuple->TupleData; | ||
1295 | |||
1296 | fmt->type = p[0]; | ||
1297 | fmt->edc = p[1]; | ||
1298 | fmt->offset = le32_to_cpu(*(u_int *)(p+2)); | ||
1299 | fmt->length = le32_to_cpu(*(u_int *)(p+6)); | ||
1300 | |||
1301 | return CS_SUCCESS; | ||
1302 | } | ||
1303 | |||
1304 | /*====================================================================*/ | ||
1305 | |||
1306 | int pccard_parse_tuple(tuple_t *tuple, cisparse_t *parse) | ||
1307 | { | ||
1308 | int ret = CS_SUCCESS; | ||
1309 | |||
1310 | if (tuple->TupleDataLen > tuple->TupleDataMax) | ||
1311 | return CS_BAD_TUPLE; | ||
1312 | switch (tuple->TupleCode) { | ||
1313 | case CISTPL_DEVICE: | ||
1314 | case CISTPL_DEVICE_A: | ||
1315 | ret = parse_device(tuple, &parse->device); | ||
1316 | break; | ||
1317 | #ifdef CONFIG_CARDBUS | ||
1318 | case CISTPL_BAR: | ||
1319 | ret = parse_bar(tuple, &parse->bar); | ||
1320 | break; | ||
1321 | case CISTPL_CONFIG_CB: | ||
1322 | ret = parse_config_cb(tuple, &parse->config); | ||
1323 | break; | ||
1324 | case CISTPL_CFTABLE_ENTRY_CB: | ||
1325 | ret = parse_cftable_entry_cb(tuple, &parse->cftable_entry_cb); | ||
1326 | break; | ||
1327 | #endif | ||
1328 | case CISTPL_CHECKSUM: | ||
1329 | ret = parse_checksum(tuple, &parse->checksum); | ||
1330 | break; | ||
1331 | case CISTPL_LONGLINK_A: | ||
1332 | case CISTPL_LONGLINK_C: | ||
1333 | ret = parse_longlink(tuple, &parse->longlink); | ||
1334 | break; | ||
1335 | case CISTPL_LONGLINK_MFC: | ||
1336 | ret = parse_longlink_mfc(tuple, &parse->longlink_mfc); | ||
1337 | break; | ||
1338 | case CISTPL_VERS_1: | ||
1339 | ret = parse_vers_1(tuple, &parse->version_1); | ||
1340 | break; | ||
1341 | case CISTPL_ALTSTR: | ||
1342 | ret = parse_altstr(tuple, &parse->altstr); | ||
1343 | break; | ||
1344 | case CISTPL_JEDEC_A: | ||
1345 | case CISTPL_JEDEC_C: | ||
1346 | ret = parse_jedec(tuple, &parse->jedec); | ||
1347 | break; | ||
1348 | case CISTPL_MANFID: | ||
1349 | ret = parse_manfid(tuple, &parse->manfid); | ||
1350 | break; | ||
1351 | case CISTPL_FUNCID: | ||
1352 | ret = parse_funcid(tuple, &parse->funcid); | ||
1353 | break; | ||
1354 | case CISTPL_FUNCE: | ||
1355 | ret = parse_funce(tuple, &parse->funce); | ||
1356 | break; | ||
1357 | case CISTPL_CONFIG: | ||
1358 | ret = parse_config(tuple, &parse->config); | ||
1359 | break; | ||
1360 | case CISTPL_CFTABLE_ENTRY: | ||
1361 | ret = parse_cftable_entry(tuple, &parse->cftable_entry); | ||
1362 | break; | ||
1363 | case CISTPL_DEVICE_GEO: | ||
1364 | case CISTPL_DEVICE_GEO_A: | ||
1365 | ret = parse_device_geo(tuple, &parse->device_geo); | ||
1366 | break; | ||
1367 | case CISTPL_VERS_2: | ||
1368 | ret = parse_vers_2(tuple, &parse->vers_2); | ||
1369 | break; | ||
1370 | case CISTPL_ORG: | ||
1371 | ret = parse_org(tuple, &parse->org); | ||
1372 | break; | ||
1373 | case CISTPL_FORMAT: | ||
1374 | case CISTPL_FORMAT_A: | ||
1375 | ret = parse_format(tuple, &parse->format); | ||
1376 | break; | ||
1377 | case CISTPL_NO_LINK: | ||
1378 | case CISTPL_LINKTARGET: | ||
1379 | ret = CS_SUCCESS; | ||
1380 | break; | ||
1381 | default: | ||
1382 | ret = CS_UNSUPPORTED_FUNCTION; | ||
1383 | break; | ||
1384 | } | ||
1385 | return ret; | ||
1386 | } | ||
1387 | EXPORT_SYMBOL(pccard_parse_tuple); | ||
1388 | |||
1389 | /*====================================================================== | ||
1390 | |||
1391 | This is used internally by Card Services to look up CIS stuff. | ||
1392 | |||
1393 | ======================================================================*/ | ||
1394 | |||
1395 | int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, cisdata_t code, void *parse) | ||
1396 | { | ||
1397 | tuple_t tuple; | ||
1398 | cisdata_t *buf; | ||
1399 | int ret; | ||
1400 | |||
1401 | buf = kmalloc(256, GFP_KERNEL); | ||
1402 | if (buf == NULL) | ||
1403 | return CS_OUT_OF_RESOURCE; | ||
1404 | tuple.DesiredTuple = code; | ||
1405 | tuple.Attributes = TUPLE_RETURN_COMMON; | ||
1406 | ret = pccard_get_first_tuple(s, function, &tuple); | ||
1407 | if (ret != CS_SUCCESS) goto done; | ||
1408 | tuple.TupleData = buf; | ||
1409 | tuple.TupleOffset = 0; | ||
1410 | tuple.TupleDataMax = 255; | ||
1411 | ret = pccard_get_tuple_data(s, &tuple); | ||
1412 | if (ret != CS_SUCCESS) goto done; | ||
1413 | ret = pccard_parse_tuple(&tuple, parse); | ||
1414 | done: | ||
1415 | kfree(buf); | ||
1416 | return ret; | ||
1417 | } | ||
1418 | EXPORT_SYMBOL(pccard_read_tuple); | ||
1419 | |||
1420 | /*====================================================================== | ||
1421 | |||
1422 | This tries to determine if a card has a sensible CIS. It returns | ||
1423 | the number of tuples in the CIS, or 0 if the CIS looks bad. The | ||
1424 | checks include making sure several critical tuples are present and | ||
1425 | valid; seeing if the total number of tuples is reasonable; and | ||
1426 | looking for tuples that use reserved codes. | ||
1427 | |||
1428 | ======================================================================*/ | ||
1429 | |||
1430 | int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, cisinfo_t *info) | ||
1431 | { | ||
1432 | tuple_t *tuple; | ||
1433 | cisparse_t *p; | ||
1434 | int ret, reserved, dev_ok = 0, ident_ok = 0; | ||
1435 | |||
1436 | if (!s) | ||
1437 | return CS_BAD_HANDLE; | ||
1438 | |||
1439 | tuple = kmalloc(sizeof(*tuple), GFP_KERNEL); | ||
1440 | if (tuple == NULL) | ||
1441 | return CS_OUT_OF_RESOURCE; | ||
1442 | p = kmalloc(sizeof(*p), GFP_KERNEL); | ||
1443 | if (p == NULL) { | ||
1444 | kfree(tuple); | ||
1445 | return CS_OUT_OF_RESOURCE; | ||
1446 | } | ||
1447 | |||
1448 | info->Chains = reserved = 0; | ||
1449 | tuple->DesiredTuple = RETURN_FIRST_TUPLE; | ||
1450 | tuple->Attributes = TUPLE_RETURN_COMMON; | ||
1451 | ret = pccard_get_first_tuple(s, function, tuple); | ||
1452 | if (ret != CS_SUCCESS) | ||
1453 | goto done; | ||
1454 | |||
1455 | /* First tuple should be DEVICE; we should really have either that | ||
1456 | or a CFTABLE_ENTRY of some sort */ | ||
1457 | if ((tuple->TupleCode == CISTPL_DEVICE) || | ||
1458 | (pccard_read_tuple(s, function, CISTPL_CFTABLE_ENTRY, p) == CS_SUCCESS) || | ||
1459 | (pccard_read_tuple(s, function, CISTPL_CFTABLE_ENTRY_CB, p) == CS_SUCCESS)) | ||
1460 | dev_ok++; | ||
1461 | |||
1462 | /* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2 | ||
1463 | tuple, for card identification. Certain old D-Link and Linksys | ||
1464 | cards have only a broken VERS_2 tuple; hence the bogus test. */ | ||
1465 | if ((pccard_read_tuple(s, function, CISTPL_MANFID, p) == CS_SUCCESS) || | ||
1466 | (pccard_read_tuple(s, function, CISTPL_VERS_1, p) == CS_SUCCESS) || | ||
1467 | (pccard_read_tuple(s, function, CISTPL_VERS_2, p) != CS_NO_MORE_ITEMS)) | ||
1468 | ident_ok++; | ||
1469 | |||
1470 | if (!dev_ok && !ident_ok) | ||
1471 | goto done; | ||
1472 | |||
1473 | for (info->Chains = 1; info->Chains < MAX_TUPLES; info->Chains++) { | ||
1474 | ret = pccard_get_next_tuple(s, function, tuple); | ||
1475 | if (ret != CS_SUCCESS) break; | ||
1476 | if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) || | ||
1477 | ((tuple->TupleCode > 0x47) && (tuple->TupleCode < 0x80)) || | ||
1478 | ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff))) | ||
1479 | reserved++; | ||
1480 | } | ||
1481 | if ((info->Chains == MAX_TUPLES) || (reserved > 5) || | ||
1482 | ((!dev_ok || !ident_ok) && (info->Chains > 10))) | ||
1483 | info->Chains = 0; | ||
1484 | |||
1485 | done: | ||
1486 | kfree(tuple); | ||
1487 | kfree(p); | ||
1488 | return CS_SUCCESS; | ||
1489 | } | ||
1490 | EXPORT_SYMBOL(pccard_validate_cis); | ||