blob: 04bf1ba607f7535bee7184fced8dd6c3732afca8 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/module.h>
16#include <linux/moduleparam.h>
17#include <linux/kernel.h>
18#include <linux/string.h>
19#include <linux/major.h>
20#include <linux/errno.h>
21#include <linux/timer.h>
22#include <linux/slab.h>
23#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include <linux/pci.h>
25#include <linux/ioport.h>
Dominik Brodowski9fea84f2009-12-07 22:11:45 +010026#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include <asm/byteorder.h>
Daniel Ritzdc0cf6a2007-10-16 01:23:52 -070028#include <asm/unaligned.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
30#include <pcmcia/cs_types.h>
31#include <pcmcia/ss.h>
32#include <pcmcia/cs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <pcmcia/cisreg.h>
34#include <pcmcia/cistpl.h>
35#include "cs_internal.h"
36
37static const u_char mantissa[] = {
38 10, 12, 13, 15, 20, 25, 30, 35,
39 40, 45, 50, 55, 60, 70, 80, 90
40};
41
42static const u_int exponent[] = {
43 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
44};
45
46/* Convert an extended speed byte to a time in nanoseconds */
47#define SPEED_CVT(v) \
48 (mantissa[(((v)>>3)&15)-1] * exponent[(v)&7] / 10)
49/* Convert a power byte to a current in 0.1 microamps */
50#define POWER_CVT(v) \
51 (mantissa[((v)>>3)&15] * exponent[(v)&7] / 10)
52#define POWER_SCALE(v) (exponent[(v)&7])
53
54/* Upper limit on reasonable # of tuples */
55#define MAX_TUPLES 200
56
57/*====================================================================*/
58
59/* Parameters that can be set with 'insmod' */
60
Pavel Machek37f77952005-09-07 16:00:26 -070061/* 16-bit CIS? */
62static int cis_width;
63module_param(cis_width, int, 0444);
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
65void release_cis_mem(struct pcmcia_socket *s)
66{
67 if (s->cis_mem.flags & MAP_ACTIVE) {
68 s->cis_mem.flags &= ~MAP_ACTIVE;
69 s->ops->set_mem_map(s, &s->cis_mem);
70 if (s->cis_mem.res) {
71 release_resource(s->cis_mem.res);
72 kfree(s->cis_mem.res);
73 s->cis_mem.res = NULL;
74 }
75 iounmap(s->cis_virt);
76 s->cis_virt = NULL;
77 }
78}
79EXPORT_SYMBOL(release_cis_mem);
80
81/*
82 * Map the card memory at "card_offset" into virtual space.
83 * If flags & MAP_ATTRIB, map the attribute space, otherwise
84 * map the memory space.
85 */
86static void __iomem *
87set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags)
88{
Dominik Brodowski2e5a3e72005-07-28 01:07:23 -070089 pccard_mem_map *mem = &s->cis_mem;
90 int ret;
Dominik Brodowski2ad0a0a2005-06-27 16:28:58 -070091
Dominik Brodowski2e5a3e72005-07-28 01:07:23 -070092 if (!(s->features & SS_CAP_STATIC_MAP) && (mem->res == NULL)) {
93 mem->res = pcmcia_find_mem_region(0, s->map_size, s->map_size, 0, s);
94 if (mem->res == NULL) {
Dominik Brodowski2e55bf62008-08-02 18:08:38 +020095 dev_printk(KERN_NOTICE, &s->dev,
96 "cs: unable to map card memory!\n");
Dominik Brodowski2e5a3e72005-07-28 01:07:23 -070097 return NULL;
98 }
99 s->cis_virt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 }
Dominik Brodowski2ad0a0a2005-06-27 16:28:58 -0700101
Dominik Brodowski2e5a3e72005-07-28 01:07:23 -0700102 if (!(s->features & SS_CAP_STATIC_MAP) && (!s->cis_virt))
103 s->cis_virt = ioremap(mem->res->start, s->map_size);
104
105 mem->card_start = card_offset;
106 mem->flags = flags;
107
108 ret = s->ops->set_mem_map(s, mem);
109 if (ret) {
110 iounmap(s->cis_virt);
111 s->cis_virt = NULL;
112 return NULL;
113 }
114
115 if (s->features & SS_CAP_STATIC_MAP) {
116 if (s->cis_virt)
117 iounmap(s->cis_virt);
118 s->cis_virt = ioremap(mem->static_start, s->map_size);
119 }
120
121 return s->cis_virt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122}
123
124/*======================================================================
125
126 Low-level functions to read and write CIS memory. I think the
127 write routine is only useful for writing one-byte registers.
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100128
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129======================================================================*/
130
131/* Bits in attr field */
132#define IS_ATTR 1
133#define IS_INDIRECT 8
134
Dominik Brodowskie6ea0b9e2005-06-27 16:28:52 -0700135int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 u_int len, void *ptr)
137{
138 void __iomem *sys, *end;
139 unsigned char *buf = ptr;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100140
Dominik Brodowskid50dbec2009-10-23 12:51:28 +0200141 dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142
143 if (attr & IS_INDIRECT) {
144 /* Indirect accesses use a bunch of special registers at fixed
145 locations in common memory */
146 u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
147 if (attr & IS_ATTR) {
148 addr *= 2;
149 flags = ICTRL0_AUTOINC;
150 }
151
152 sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
153 if (!sys) {
154 memset(ptr, 0xff, len);
155 return -1;
156 }
157
158 writeb(flags, sys+CISREG_ICTRL0);
159 writeb(addr & 0xff, sys+CISREG_IADDR0);
160 writeb((addr>>8) & 0xff, sys+CISREG_IADDR1);
161 writeb((addr>>16) & 0xff, sys+CISREG_IADDR2);
162 writeb((addr>>24) & 0xff, sys+CISREG_IADDR3);
163 for ( ; len > 0; len--, buf++)
164 *buf = readb(sys+CISREG_IDATA0);
165 } else {
166 u_int inc = 1, card_offset, flags;
167
168 flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
169 if (attr) {
170 flags |= MAP_ATTRIB;
171 inc++;
172 addr *= 2;
173 }
174
175 card_offset = addr & ~(s->map_size-1);
176 while (len) {
177 sys = set_cis_map(s, card_offset, flags);
178 if (!sys) {
179 memset(ptr, 0xff, len);
180 return -1;
181 }
182 end = sys + s->map_size;
183 sys = sys + (addr & (s->map_size-1));
184 for ( ; len > 0; len--, buf++, sys += inc) {
185 if (sys == end)
186 break;
187 *buf = readb(sys);
188 }
189 card_offset += s->map_size;
190 addr = 0;
191 }
192 }
Dominik Brodowskid50dbec2009-10-23 12:51:28 +0200193 dev_dbg(&s->dev, " %#2.2x %#2.2x %#2.2x %#2.2x ...\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 *(u_char *)(ptr+0), *(u_char *)(ptr+1),
195 *(u_char *)(ptr+2), *(u_char *)(ptr+3));
196 return 0;
197}
Dominik Brodowski1a8d4662005-06-27 16:28:53 -0700198EXPORT_SYMBOL(pcmcia_read_cis_mem);
199
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200
Dominik Brodowskie6ea0b9e2005-06-27 16:28:52 -0700201void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 u_int len, void *ptr)
203{
204 void __iomem *sys, *end;
205 unsigned char *buf = ptr;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100206
Dominik Brodowskid50dbec2009-10-23 12:51:28 +0200207 dev_dbg(&s->dev, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208
209 if (attr & IS_INDIRECT) {
210 /* Indirect accesses use a bunch of special registers at fixed
211 locations in common memory */
212 u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
213 if (attr & IS_ATTR) {
214 addr *= 2;
215 flags = ICTRL0_AUTOINC;
216 }
217
218 sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
219 if (!sys)
220 return; /* FIXME: Error */
221
222 writeb(flags, sys+CISREG_ICTRL0);
223 writeb(addr & 0xff, sys+CISREG_IADDR0);
224 writeb((addr>>8) & 0xff, sys+CISREG_IADDR1);
225 writeb((addr>>16) & 0xff, sys+CISREG_IADDR2);
226 writeb((addr>>24) & 0xff, sys+CISREG_IADDR3);
227 for ( ; len > 0; len--, buf++)
228 writeb(*buf, sys+CISREG_IDATA0);
229 } else {
230 u_int inc = 1, card_offset, flags;
231
232 flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
233 if (attr & IS_ATTR) {
234 flags |= MAP_ATTRIB;
235 inc++;
236 addr *= 2;
237 }
238
239 card_offset = addr & ~(s->map_size-1);
240 while (len) {
241 sys = set_cis_map(s, card_offset, flags);
242 if (!sys)
243 return; /* FIXME: error */
244
245 end = sys + s->map_size;
246 sys = sys + (addr & (s->map_size-1));
247 for ( ; len > 0; len--, buf++, sys += inc) {
248 if (sys == end)
249 break;
250 writeb(*buf, sys);
251 }
252 card_offset += s->map_size;
253 addr = 0;
254 }
255 }
256}
Dominik Brodowski1a8d4662005-06-27 16:28:53 -0700257EXPORT_SYMBOL(pcmcia_write_cis_mem);
258
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259
260/*======================================================================
261
262 This is a wrapper around read_cis_mem, with the same interface,
263 but which caches information, for cards whose CIS may not be
264 readable all the time.
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100265
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266======================================================================*/
267
268static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
Dominik Brodowski53efec92008-07-28 19:44:05 +0200269 size_t len, void *ptr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270{
271 struct cis_cache_entry *cis;
272 int ret;
273
274 if (s->fake_cis) {
Dominik Brodowski53efec92008-07-28 19:44:05 +0200275 if (s->fake_cis_len >= addr+len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 memcpy(ptr, s->fake_cis+addr, len);
277 else
278 memset(ptr, 0xff, len);
279 return;
280 }
281
282 list_for_each_entry(cis, &s->cis_cache, node) {
283 if (cis->addr == addr && cis->len == len && cis->attr == attr) {
284 memcpy(ptr, cis->cache, len);
285 return;
286 }
287 }
288
289#ifdef CONFIG_CARDBUS
290 if (s->state & SOCKET_CARDBUS)
291 ret = read_cb_mem(s, attr, addr, len, ptr);
292 else
293#endif
Dominik Brodowskie6ea0b9e2005-06-27 16:28:52 -0700294 ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295
296 if (ret == 0) {
297 /* Copy data into the cache */
298 cis = kmalloc(sizeof(struct cis_cache_entry) + len, GFP_KERNEL);
299 if (cis) {
300 cis->addr = addr;
301 cis->len = len;
302 cis->attr = attr;
303 memcpy(cis->cache, ptr, len);
304 list_add(&cis->node, &s->cis_cache);
305 }
306 }
307}
308
309static void
310remove_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len)
311{
312 struct cis_cache_entry *cis;
313
314 list_for_each_entry(cis, &s->cis_cache, node)
315 if (cis->addr == addr && cis->len == len && cis->attr == attr) {
316 list_del(&cis->node);
317 kfree(cis);
318 break;
319 }
320}
321
Dominik Brodowski904e3772010-01-02 12:28:04 +0100322/**
323 * destroy_cis_cache() - destroy the CIS cache
324 * @s: pcmcia_socket for which CIS cache shall be destroyed
325 *
326 * This destroys the CIS cache but keeps any fake CIS alive.
327 */
328
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329void destroy_cis_cache(struct pcmcia_socket *s)
330{
331 struct list_head *l, *n;
Dominik Brodowski904e3772010-01-02 12:28:04 +0100332 struct cis_cache_entry *cis;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333
334 list_for_each_safe(l, n, &s->cis_cache) {
Dominik Brodowski904e3772010-01-02 12:28:04 +0100335 cis = list_entry(l, struct cis_cache_entry, node);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 list_del(&cis->node);
337 kfree(cis);
338 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339}
340EXPORT_SYMBOL(destroy_cis_cache);
341
342/*======================================================================
343
344 This verifies if the CIS of a card matches what is in the CIS
345 cache.
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100346
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347======================================================================*/
348
349int verify_cis_cache(struct pcmcia_socket *s)
350{
351 struct cis_cache_entry *cis;
352 char *buf;
353
354 buf = kmalloc(256, GFP_KERNEL);
Dominik Brodowskie6895972008-11-02 19:55:45 +0100355 if (buf == NULL) {
Dominik Brodowski11683862008-08-03 10:22:47 +0200356 dev_printk(KERN_WARNING, &s->dev,
357 "no memory for verifying CIS\n");
358 return -ENOMEM;
Dominik Brodowskie6895972008-11-02 19:55:45 +0100359 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 list_for_each_entry(cis, &s->cis_cache, node) {
361 int len = cis->len;
362
363 if (len > 256)
364 len = 256;
365#ifdef CONFIG_CARDBUS
366 if (s->state & SOCKET_CARDBUS)
367 read_cb_mem(s, cis->attr, cis->addr, len, buf);
368 else
369#endif
Dominik Brodowskie6ea0b9e2005-06-27 16:28:52 -0700370 pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
372 if (memcmp(buf, cis->cache, len) != 0) {
373 kfree(buf);
374 return -1;
375 }
376 }
377 kfree(buf);
378 return 0;
379}
380
381/*======================================================================
382
383 For really bad cards, we provide a facility for uploading a
384 replacement CIS.
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100385
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386======================================================================*/
387
Dominik Brodowski53efec92008-07-28 19:44:05 +0200388int pcmcia_replace_cis(struct pcmcia_socket *s,
389 const u8 *data, const size_t len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390{
Dominik Brodowski11683862008-08-03 10:22:47 +0200391 if (len > CISTPL_MAX_CIS_SIZE) {
392 dev_printk(KERN_WARNING, &s->dev, "replacement CIS too big\n");
393 return -EINVAL;
394 }
395 kfree(s->fake_cis);
396 s->fake_cis = kmalloc(len, GFP_KERNEL);
397 if (s->fake_cis == NULL) {
398 dev_printk(KERN_WARNING, &s->dev, "no memory to replace CIS\n");
399 return -ENOMEM;
400 }
401 s->fake_cis_len = len;
402 memcpy(s->fake_cis, data, len);
403 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404}
Dominik Brodowski33519dd2005-06-27 16:28:53 -0700405EXPORT_SYMBOL(pcmcia_replace_cis);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406
407/*======================================================================
408
409 The high-level CIS tuple services
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100410
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411======================================================================*/
412
413typedef struct tuple_flags {
414 u_int link_space:4;
415 u_int has_link:1;
416 u_int mfc_fn:3;
417 u_int space:4;
418} tuple_flags;
419
420#define LINK_SPACE(f) (((tuple_flags *)(&(f)))->link_space)
421#define HAS_LINK(f) (((tuple_flags *)(&(f)))->has_link)
422#define MFC_FN(f) (((tuple_flags *)(&(f)))->mfc_fn)
423#define SPACE(f) (((tuple_flags *)(&(f)))->space)
424
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple_t *tuple)
426{
427 if (!s)
Dominik Brodowskiffb8da22008-08-03 11:18:00 +0200428 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 if (!(s->state & SOCKET_PRESENT))
Dominik Brodowski3939c1ef2008-08-03 11:10:56 +0200430 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 tuple->TupleLink = tuple->Flags = 0;
432#ifdef CONFIG_CARDBUS
433 if (s->state & SOCKET_CARDBUS) {
434 struct pci_dev *dev = s->cb_dev;
435 u_int ptr;
436 pci_bus_read_config_dword(dev->subordinate, 0, PCI_CARDBUS_CIS, &ptr);
437 tuple->CISOffset = ptr & ~7;
438 SPACE(tuple->Flags) = (ptr & 7);
439 } else
440#endif
441 {
442 /* Assume presence of a LONGLINK_C to address 0 */
443 tuple->CISOffset = tuple->LinkOffset = 0;
444 SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1;
445 }
446 if (!(s->state & SOCKET_CARDBUS) && (s->functions > 1) &&
447 !(tuple->Attributes & TUPLE_RETURN_COMMON)) {
448 cisdata_t req = tuple->DesiredTuple;
449 tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200450 if (pccard_get_next_tuple(s, function, tuple) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 tuple->DesiredTuple = CISTPL_LINKTARGET;
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200452 if (pccard_get_next_tuple(s, function, tuple) != 0)
Dominik Brodowski635d19b2008-08-03 11:47:29 +0200453 return -ENOSPC;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 } else
455 tuple->CISOffset = tuple->TupleLink = 0;
456 tuple->DesiredTuple = req;
457 }
458 return pccard_get_next_tuple(s, function, tuple);
459}
460EXPORT_SYMBOL(pccard_get_first_tuple);
461
462static int follow_link(struct pcmcia_socket *s, tuple_t *tuple)
463{
464 u_char link[5];
465 u_int ofs;
466
467 if (MFC_FN(tuple->Flags)) {
468 /* Get indirect link from the MFC tuple */
469 read_cis_cache(s, LINK_SPACE(tuple->Flags),
470 tuple->LinkOffset, 5, link);
Harvey Harrison6b1e6f62008-04-29 01:03:39 -0700471 ofs = get_unaligned_le32(link + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR);
473 /* Move to the next indirect link */
474 tuple->LinkOffset += 5;
475 MFC_FN(tuple->Flags)--;
476 } else if (HAS_LINK(tuple->Flags)) {
477 ofs = tuple->LinkOffset;
478 SPACE(tuple->Flags) = LINK_SPACE(tuple->Flags);
479 HAS_LINK(tuple->Flags) = 0;
480 } else {
481 return -1;
482 }
483 if (!(s->state & SOCKET_CARDBUS) && SPACE(tuple->Flags)) {
484 /* This is ugly, but a common CIS error is to code the long
485 link offset incorrectly, so we check the right spot... */
486 read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
487 if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
488 (strncmp(link+2, "CIS", 3) == 0))
489 return ofs;
490 remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5);
491 /* Then, we try the wrong spot... */
492 ofs = ofs >> 1;
493 }
494 read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
495 if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
496 (strncmp(link+2, "CIS", 3) == 0))
497 return ofs;
498 remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5);
499 return -1;
500}
501
502int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_t *tuple)
503{
504 u_char link[2], tmp;
505 int ofs, i, attr;
506
507 if (!s)
Dominik Brodowskiffb8da22008-08-03 11:18:00 +0200508 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 if (!(s->state & SOCKET_PRESENT))
Dominik Brodowski3939c1ef2008-08-03 11:10:56 +0200510 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
512 link[1] = tuple->TupleLink;
513 ofs = tuple->CISOffset + tuple->TupleLink;
514 attr = SPACE(tuple->Flags);
515
516 for (i = 0; i < MAX_TUPLES; i++) {
517 if (link[1] == 0xff) {
518 link[0] = CISTPL_END;
519 } else {
520 read_cis_cache(s, attr, ofs, 2, link);
521 if (link[0] == CISTPL_NULL) {
522 ofs++; continue;
523 }
524 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100525
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 /* End of chain? Follow long link if possible */
527 if (link[0] == CISTPL_END) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100528 ofs = follow_link(s, tuple);
529 if (ofs < 0)
Dominik Brodowski635d19b2008-08-03 11:47:29 +0200530 return -ENOSPC;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 attr = SPACE(tuple->Flags);
532 read_cis_cache(s, attr, ofs, 2, link);
533 }
534
535 /* Is this a link tuple? Make a note of it */
536 if ((link[0] == CISTPL_LONGLINK_A) ||
537 (link[0] == CISTPL_LONGLINK_C) ||
538 (link[0] == CISTPL_LONGLINK_MFC) ||
539 (link[0] == CISTPL_LINKTARGET) ||
540 (link[0] == CISTPL_INDIRECT) ||
541 (link[0] == CISTPL_NO_LINK)) {
542 switch (link[0]) {
543 case CISTPL_LONGLINK_A:
544 HAS_LINK(tuple->Flags) = 1;
545 LINK_SPACE(tuple->Flags) = attr | IS_ATTR;
546 read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
547 break;
548 case CISTPL_LONGLINK_C:
549 HAS_LINK(tuple->Flags) = 1;
550 LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR;
551 read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
552 break;
553 case CISTPL_INDIRECT:
554 HAS_LINK(tuple->Flags) = 1;
555 LINK_SPACE(tuple->Flags) = IS_ATTR | IS_INDIRECT;
556 tuple->LinkOffset = 0;
557 break;
558 case CISTPL_LONGLINK_MFC:
559 tuple->LinkOffset = ofs + 3;
560 LINK_SPACE(tuple->Flags) = attr;
561 if (function == BIND_FN_ALL) {
562 /* Follow all the MFC links */
563 read_cis_cache(s, attr, ofs+2, 1, &tmp);
564 MFC_FN(tuple->Flags) = tmp;
565 } else {
566 /* Follow exactly one of the links */
567 MFC_FN(tuple->Flags) = 1;
568 tuple->LinkOffset += function * 5;
569 }
570 break;
571 case CISTPL_NO_LINK:
572 HAS_LINK(tuple->Flags) = 0;
573 break;
574 }
575 if ((tuple->Attributes & TUPLE_RETURN_LINK) &&
576 (tuple->DesiredTuple == RETURN_FIRST_TUPLE))
577 break;
578 } else
579 if (tuple->DesiredTuple == RETURN_FIRST_TUPLE)
580 break;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100581
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 if (link[0] == tuple->DesiredTuple)
583 break;
584 ofs += link[1] + 2;
585 }
586 if (i == MAX_TUPLES) {
Dominik Brodowskid50dbec2009-10-23 12:51:28 +0200587 dev_dbg(&s->dev, "cs: overrun in pcmcia_get_next_tuple\n");
Dominik Brodowski635d19b2008-08-03 11:47:29 +0200588 return -ENOSPC;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100590
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 tuple->TupleCode = link[0];
592 tuple->TupleLink = link[1];
593 tuple->CISOffset = ofs + 2;
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200594 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595}
596EXPORT_SYMBOL(pccard_get_next_tuple);
597
598/*====================================================================*/
599
600#define _MIN(a, b) (((a) < (b)) ? (a) : (b))
601
602int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple)
603{
604 u_int len;
605
606 if (!s)
Dominik Brodowskiffb8da22008-08-03 11:18:00 +0200607 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608
609 if (tuple->TupleLink < tuple->TupleOffset)
Dominik Brodowski635d19b2008-08-03 11:47:29 +0200610 return -ENOSPC;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 len = tuple->TupleLink - tuple->TupleOffset;
612 tuple->TupleDataLen = tuple->TupleLink;
613 if (len == 0)
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200614 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 read_cis_cache(s, SPACE(tuple->Flags),
616 tuple->CISOffset + tuple->TupleOffset,
617 _MIN(len, tuple->TupleDataMax), tuple->TupleData);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200618 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619}
620EXPORT_SYMBOL(pccard_get_tuple_data);
621
622
623/*======================================================================
624
625 Parsing routines for individual tuples
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100626
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627======================================================================*/
628
629static int parse_device(tuple_t *tuple, cistpl_device_t *device)
630{
631 int i;
632 u_char scale;
633 u_char *p, *q;
634
635 p = (u_char *)tuple->TupleData;
636 q = p + tuple->TupleDataLen;
637
638 device->ndev = 0;
639 for (i = 0; i < CISTPL_MAX_DEVICES; i++) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100640
641 if (*p == 0xff)
642 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 device->dev[i].type = (*p >> 4);
644 device->dev[i].wp = (*p & 0x08) ? 1 : 0;
645 switch (*p & 0x07) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100646 case 0:
647 device->dev[i].speed = 0;
648 break;
649 case 1:
650 device->dev[i].speed = 250;
651 break;
652 case 2:
653 device->dev[i].speed = 200;
654 break;
655 case 3:
656 device->dev[i].speed = 150;
657 break;
658 case 4:
659 device->dev[i].speed = 100;
660 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 case 7:
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +0200662 if (++p == q)
663 return -EINVAL;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100664 device->dev[i].speed = SPEED_CVT(*p);
665 while (*p & 0x80)
666 if (++p == q)
667 return -EINVAL;
668 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 default:
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100670 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 }
672
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +0200673 if (++p == q)
674 return -EINVAL;
675 if (*p == 0xff)
676 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 scale = *p & 7;
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +0200678 if (scale == 7)
679 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 device->dev[i].size = ((*p >> 3) + 1) * (512 << (scale*2));
681 device->ndev++;
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +0200682 if (++p == q)
683 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100685
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200686 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687}
688
689/*====================================================================*/
690
691static int parse_checksum(tuple_t *tuple, cistpl_checksum_t *csum)
692{
693 u_char *p;
694 if (tuple->TupleDataLen < 5)
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +0200695 return -EINVAL;
Daniel Ritzdc0cf6a2007-10-16 01:23:52 -0700696 p = (u_char *) tuple->TupleData;
Harvey Harrison6b1e6f62008-04-29 01:03:39 -0700697 csum->addr = tuple->CISOffset + get_unaligned_le16(p) - 2;
698 csum->len = get_unaligned_le16(p + 2);
Daniel Ritzdc0cf6a2007-10-16 01:23:52 -0700699 csum->sum = *(p + 4);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200700 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701}
702
703/*====================================================================*/
704
705static int parse_longlink(tuple_t *tuple, cistpl_longlink_t *link)
706{
707 if (tuple->TupleDataLen < 4)
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +0200708 return -EINVAL;
Harvey Harrison6b1e6f62008-04-29 01:03:39 -0700709 link->addr = get_unaligned_le32(tuple->TupleData);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200710 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711}
712
713/*====================================================================*/
714
715static int parse_longlink_mfc(tuple_t *tuple,
716 cistpl_longlink_mfc_t *link)
717{
718 u_char *p;
719 int i;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100720
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 p = (u_char *)tuple->TupleData;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100722
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 link->nfn = *p; p++;
724 if (tuple->TupleDataLen <= link->nfn*5)
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +0200725 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 for (i = 0; i < link->nfn; i++) {
727 link->fn[i].space = *p; p++;
Harvey Harrison6b1e6f62008-04-29 01:03:39 -0700728 link->fn[i].addr = get_unaligned_le32(p);
Daniel Ritzdc0cf6a2007-10-16 01:23:52 -0700729 p += 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 }
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200731 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732}
733
734/*====================================================================*/
735
736static int parse_strings(u_char *p, u_char *q, int max,
737 char *s, u_char *ofs, u_char *found)
738{
739 int i, j, ns;
740
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +0200741 if (p == q)
742 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 ns = 0; j = 0;
744 for (i = 0; i < max; i++) {
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +0200745 if (*p == 0xff)
746 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 ofs[i] = j;
748 ns++;
749 for (;;) {
750 s[j++] = (*p == 0xff) ? '\0' : *p;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100751 if ((*p == '\0') || (*p == 0xff))
752 break;
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +0200753 if (++p == q)
754 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100756 if ((*p == 0xff) || (++p == q))
757 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 }
759 if (found) {
760 *found = ns;
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200761 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 } else {
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +0200763 return (ns == max) ? 0 : -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 }
765}
766
767/*====================================================================*/
768
769static int parse_vers_1(tuple_t *tuple, cistpl_vers_1_t *vers_1)
770{
771 u_char *p, *q;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100772
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 p = (u_char *)tuple->TupleData;
774 q = p + tuple->TupleDataLen;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100775
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 vers_1->major = *p; p++;
777 vers_1->minor = *p; p++;
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +0200778 if (p >= q)
779 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780
781 return parse_strings(p, q, CISTPL_VERS_1_MAX_PROD_STRINGS,
782 vers_1->str, vers_1->ofs, &vers_1->ns);
783}
784
785/*====================================================================*/
786
787static int parse_altstr(tuple_t *tuple, cistpl_altstr_t *altstr)
788{
789 u_char *p, *q;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100790
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 p = (u_char *)tuple->TupleData;
792 q = p + tuple->TupleDataLen;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100793
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 return parse_strings(p, q, CISTPL_MAX_ALTSTR_STRINGS,
795 altstr->str, altstr->ofs, &altstr->ns);
796}
797
798/*====================================================================*/
799
800static int parse_jedec(tuple_t *tuple, cistpl_jedec_t *jedec)
801{
802 u_char *p, *q;
803 int nid;
804
805 p = (u_char *)tuple->TupleData;
806 q = p + tuple->TupleDataLen;
807
808 for (nid = 0; nid < CISTPL_MAX_DEVICES; nid++) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100809 if (p > q-2)
810 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 jedec->id[nid].mfr = p[0];
812 jedec->id[nid].info = p[1];
813 p += 2;
814 }
815 jedec->nid = nid;
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200816 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817}
818
819/*====================================================================*/
820
821static int parse_manfid(tuple_t *tuple, cistpl_manfid_t *m)
822{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 if (tuple->TupleDataLen < 4)
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +0200824 return -EINVAL;
Harvey Harrison6b1e6f62008-04-29 01:03:39 -0700825 m->manf = get_unaligned_le16(tuple->TupleData);
826 m->card = get_unaligned_le16(tuple->TupleData + 2);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200827 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828}
829
830/*====================================================================*/
831
832static int parse_funcid(tuple_t *tuple, cistpl_funcid_t *f)
833{
834 u_char *p;
835 if (tuple->TupleDataLen < 2)
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +0200836 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 p = (u_char *)tuple->TupleData;
838 f->func = p[0];
839 f->sysinit = p[1];
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200840 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841}
842
843/*====================================================================*/
844
845static int parse_funce(tuple_t *tuple, cistpl_funce_t *f)
846{
847 u_char *p;
848 int i;
849 if (tuple->TupleDataLen < 1)
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +0200850 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 p = (u_char *)tuple->TupleData;
852 f->type = p[0];
853 for (i = 1; i < tuple->TupleDataLen; i++)
854 f->data[i-1] = p[i];
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200855 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856}
857
858/*====================================================================*/
859
860static int parse_config(tuple_t *tuple, cistpl_config_t *config)
861{
862 int rasz, rmsz, i;
863 u_char *p;
864
865 p = (u_char *)tuple->TupleData;
866 rasz = *p & 0x03;
867 rmsz = (*p & 0x3c) >> 2;
868 if (tuple->TupleDataLen < rasz+rmsz+4)
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +0200869 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 config->last_idx = *(++p);
871 p++;
872 config->base = 0;
873 for (i = 0; i <= rasz; i++)
874 config->base += p[i] << (8*i);
875 p += rasz+1;
876 for (i = 0; i < 4; i++)
877 config->rmask[i] = 0;
878 for (i = 0; i <= rmsz; i++)
879 config->rmask[i>>2] += p[i] << (8*(i%4));
880 config->subtuples = tuple->TupleDataLen - (rasz+rmsz+4);
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200881 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882}
883
884/*======================================================================
885
886 The following routines are all used to parse the nightmarish
887 config table entries.
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100888
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889======================================================================*/
890
891static u_char *parse_power(u_char *p, u_char *q,
892 cistpl_power_t *pwr)
893{
894 int i;
895 u_int scale;
896
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100897 if (p == q)
898 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 pwr->present = *p;
900 pwr->flags = 0;
901 p++;
902 for (i = 0; i < 7; i++)
903 if (pwr->present & (1<<i)) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100904 if (p == q)
905 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 pwr->param[i] = POWER_CVT(*p);
907 scale = POWER_SCALE(*p);
908 while (*p & 0x80) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100909 if (++p == q)
910 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 if ((*p & 0x7f) < 100)
912 pwr->param[i] += (*p & 0x7f) * scale / 100;
913 else if (*p == 0x7d)
914 pwr->flags |= CISTPL_POWER_HIGHZ_OK;
915 else if (*p == 0x7e)
916 pwr->param[i] = 0;
917 else if (*p == 0x7f)
918 pwr->flags |= CISTPL_POWER_HIGHZ_REQ;
919 else
920 return NULL;
921 }
922 p++;
923 }
924 return p;
925}
926
927/*====================================================================*/
928
929static u_char *parse_timing(u_char *p, u_char *q,
930 cistpl_timing_t *timing)
931{
932 u_char scale;
933
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100934 if (p == q)
935 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 scale = *p;
937 if ((scale & 3) != 3) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100938 if (++p == q)
939 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 timing->wait = SPEED_CVT(*p);
941 timing->waitscale = exponent[scale & 3];
942 } else
943 timing->wait = 0;
944 scale >>= 2;
945 if ((scale & 7) != 7) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100946 if (++p == q)
947 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 timing->ready = SPEED_CVT(*p);
949 timing->rdyscale = exponent[scale & 7];
950 } else
951 timing->ready = 0;
952 scale >>= 3;
953 if (scale != 7) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100954 if (++p == q)
955 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 timing->reserved = SPEED_CVT(*p);
957 timing->rsvscale = exponent[scale];
958 } else
959 timing->reserved = 0;
960 p++;
961 return p;
962}
963
964/*====================================================================*/
965
966static u_char *parse_io(u_char *p, u_char *q, cistpl_io_t *io)
967{
968 int i, j, bsz, lsz;
969
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100970 if (p == q)
971 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 io->flags = *p;
973
974 if (!(*p & 0x80)) {
975 io->nwin = 1;
976 io->win[0].base = 0;
977 io->win[0].len = (1 << (io->flags & CISTPL_IO_LINES_MASK));
978 return p+1;
979 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100980
981 if (++p == q)
982 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 io->nwin = (*p & 0x0f) + 1;
984 bsz = (*p & 0x30) >> 4;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100985 if (bsz == 3)
986 bsz++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 lsz = (*p & 0xc0) >> 6;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100988 if (lsz == 3)
989 lsz++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 p++;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100991
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 for (i = 0; i < io->nwin; i++) {
993 io->win[i].base = 0;
994 io->win[i].len = 1;
995 for (j = 0; j < bsz; j++, p++) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100996 if (p == q)
997 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 io->win[i].base += *p << (j*8);
999 }
1000 for (j = 0; j < lsz; j++, p++) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001001 if (p == q)
1002 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 io->win[i].len += *p << (j*8);
1004 }
1005 }
1006 return p;
1007}
1008
1009/*====================================================================*/
1010
1011static u_char *parse_mem(u_char *p, u_char *q, cistpl_mem_t *mem)
1012{
1013 int i, j, asz, lsz, has_ha;
1014 u_int len, ca, ha;
1015
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001016 if (p == q)
1017 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018
1019 mem->nwin = (*p & 0x07) + 1;
1020 lsz = (*p & 0x18) >> 3;
1021 asz = (*p & 0x60) >> 5;
1022 has_ha = (*p & 0x80);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001023 if (++p == q)
1024 return NULL;
1025
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 for (i = 0; i < mem->nwin; i++) {
1027 len = ca = ha = 0;
1028 for (j = 0; j < lsz; j++, p++) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001029 if (p == q)
1030 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 len += *p << (j*8);
1032 }
1033 for (j = 0; j < asz; j++, p++) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001034 if (p == q)
1035 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 ca += *p << (j*8);
1037 }
1038 if (has_ha)
1039 for (j = 0; j < asz; j++, p++) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001040 if (p == q)
1041 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 ha += *p << (j*8);
1043 }
1044 mem->win[i].len = len << 8;
1045 mem->win[i].card_addr = ca << 8;
1046 mem->win[i].host_addr = ha << 8;
1047 }
1048 return p;
1049}
1050
1051/*====================================================================*/
1052
1053static u_char *parse_irq(u_char *p, u_char *q, cistpl_irq_t *irq)
1054{
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001055 if (p == q)
1056 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 irq->IRQInfo1 = *p; p++;
1058 if (irq->IRQInfo1 & IRQ_INFO2_VALID) {
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001059 if (p+2 > q)
1060 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 irq->IRQInfo2 = (p[1]<<8) + p[0];
1062 p += 2;
1063 }
1064 return p;
1065}
1066
1067/*====================================================================*/
1068
1069static int parse_cftable_entry(tuple_t *tuple,
1070 cistpl_cftable_entry_t *entry)
1071{
1072 u_char *p, *q, features;
1073
1074 p = tuple->TupleData;
1075 q = p + tuple->TupleDataLen;
1076 entry->index = *p & 0x3f;
1077 entry->flags = 0;
1078 if (*p & 0x40)
1079 entry->flags |= CISTPL_CFTABLE_DEFAULT;
1080 if (*p & 0x80) {
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001081 if (++p == q)
1082 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 if (*p & 0x10)
1084 entry->flags |= CISTPL_CFTABLE_BVDS;
1085 if (*p & 0x20)
1086 entry->flags |= CISTPL_CFTABLE_WP;
1087 if (*p & 0x40)
1088 entry->flags |= CISTPL_CFTABLE_RDYBSY;
1089 if (*p & 0x80)
1090 entry->flags |= CISTPL_CFTABLE_MWAIT;
1091 entry->interface = *p & 0x0f;
1092 } else
1093 entry->interface = 0;
1094
1095 /* Process optional features */
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001096 if (++p == q)
1097 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 features = *p; p++;
1099
1100 /* Power options */
1101 if ((features & 3) > 0) {
1102 p = parse_power(p, q, &entry->vcc);
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001103 if (p == NULL)
1104 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 } else
1106 entry->vcc.present = 0;
1107 if ((features & 3) > 1) {
1108 p = parse_power(p, q, &entry->vpp1);
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001109 if (p == NULL)
1110 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 } else
1112 entry->vpp1.present = 0;
1113 if ((features & 3) > 2) {
1114 p = parse_power(p, q, &entry->vpp2);
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001115 if (p == NULL)
1116 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 } else
1118 entry->vpp2.present = 0;
1119
1120 /* Timing options */
1121 if (features & 0x04) {
1122 p = parse_timing(p, q, &entry->timing);
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001123 if (p == NULL)
1124 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 } else {
1126 entry->timing.wait = 0;
1127 entry->timing.ready = 0;
1128 entry->timing.reserved = 0;
1129 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001130
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 /* I/O window options */
1132 if (features & 0x08) {
1133 p = parse_io(p, q, &entry->io);
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001134 if (p == NULL)
1135 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 } else
1137 entry->io.nwin = 0;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001138
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 /* Interrupt options */
1140 if (features & 0x10) {
1141 p = parse_irq(p, q, &entry->irq);
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001142 if (p == NULL)
1143 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 } else
1145 entry->irq.IRQInfo1 = 0;
1146
1147 switch (features & 0x60) {
1148 case 0x00:
1149 entry->mem.nwin = 0;
1150 break;
1151 case 0x20:
1152 entry->mem.nwin = 1;
Harvey Harrison6b1e6f62008-04-29 01:03:39 -07001153 entry->mem.win[0].len = get_unaligned_le16(p) << 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 entry->mem.win[0].card_addr = 0;
1155 entry->mem.win[0].host_addr = 0;
1156 p += 2;
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001157 if (p > q)
1158 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 break;
1160 case 0x40:
1161 entry->mem.nwin = 1;
Harvey Harrison6b1e6f62008-04-29 01:03:39 -07001162 entry->mem.win[0].len = get_unaligned_le16(p) << 8;
1163 entry->mem.win[0].card_addr = get_unaligned_le16(p + 2) << 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 entry->mem.win[0].host_addr = 0;
1165 p += 4;
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001166 if (p > q)
1167 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 break;
1169 case 0x60:
1170 p = parse_mem(p, q, &entry->mem);
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001171 if (p == NULL)
1172 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 break;
1174 }
1175
1176 /* Misc features */
1177 if (features & 0x80) {
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001178 if (p == q)
1179 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 entry->flags |= (*p << 8);
1181 while (*p & 0x80)
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001182 if (++p == q)
1183 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 p++;
1185 }
1186
1187 entry->subtuples = q-p;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001188
Dominik Brodowski4c89e882008-08-03 10:07:45 +02001189 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190}
1191
1192/*====================================================================*/
1193
1194#ifdef CONFIG_CARDBUS
1195
1196static int parse_bar(tuple_t *tuple, cistpl_bar_t *bar)
1197{
1198 u_char *p;
1199 if (tuple->TupleDataLen < 6)
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001200 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 p = (u_char *)tuple->TupleData;
1202 bar->attr = *p;
1203 p += 2;
Harvey Harrison6b1e6f62008-04-29 01:03:39 -07001204 bar->size = get_unaligned_le32(p);
Dominik Brodowski4c89e882008-08-03 10:07:45 +02001205 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206}
1207
1208static int parse_config_cb(tuple_t *tuple, cistpl_config_t *config)
1209{
1210 u_char *p;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001211
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 p = (u_char *)tuple->TupleData;
1213 if ((*p != 3) || (tuple->TupleDataLen < 6))
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001214 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 config->last_idx = *(++p);
1216 p++;
Harvey Harrison6b1e6f62008-04-29 01:03:39 -07001217 config->base = get_unaligned_le32(p);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 config->subtuples = tuple->TupleDataLen - 6;
Dominik Brodowski4c89e882008-08-03 10:07:45 +02001219 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220}
1221
1222static int parse_cftable_entry_cb(tuple_t *tuple,
1223 cistpl_cftable_entry_cb_t *entry)
1224{
1225 u_char *p, *q, features;
1226
1227 p = tuple->TupleData;
1228 q = p + tuple->TupleDataLen;
1229 entry->index = *p & 0x3f;
1230 entry->flags = 0;
1231 if (*p & 0x40)
1232 entry->flags |= CISTPL_CFTABLE_DEFAULT;
1233
1234 /* Process optional features */
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001235 if (++p == q)
1236 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237 features = *p; p++;
1238
1239 /* Power options */
1240 if ((features & 3) > 0) {
1241 p = parse_power(p, q, &entry->vcc);
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001242 if (p == NULL)
1243 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 } else
1245 entry->vcc.present = 0;
1246 if ((features & 3) > 1) {
1247 p = parse_power(p, q, &entry->vpp1);
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001248 if (p == NULL)
1249 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 } else
1251 entry->vpp1.present = 0;
1252 if ((features & 3) > 2) {
1253 p = parse_power(p, q, &entry->vpp2);
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001254 if (p == NULL)
1255 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256 } else
1257 entry->vpp2.present = 0;
1258
1259 /* I/O window options */
1260 if (features & 0x08) {
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001261 if (p == q)
1262 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263 entry->io = *p; p++;
1264 } else
1265 entry->io = 0;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001266
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 /* Interrupt options */
1268 if (features & 0x10) {
1269 p = parse_irq(p, q, &entry->irq);
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001270 if (p == NULL)
1271 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 } else
1273 entry->irq.IRQInfo1 = 0;
1274
1275 if (features & 0x20) {
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001276 if (p == q)
1277 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 entry->mem = *p; p++;
1279 } else
1280 entry->mem = 0;
1281
1282 /* Misc features */
1283 if (features & 0x80) {
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001284 if (p == q)
1285 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 entry->flags |= (*p << 8);
1287 if (*p & 0x80) {
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001288 if (++p == q)
1289 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 entry->flags |= (*p << 16);
1291 }
1292 while (*p & 0x80)
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001293 if (++p == q)
1294 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 p++;
1296 }
1297
1298 entry->subtuples = q-p;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001299
Dominik Brodowski4c89e882008-08-03 10:07:45 +02001300 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301}
1302
1303#endif
1304
1305/*====================================================================*/
1306
1307static int parse_device_geo(tuple_t *tuple, cistpl_device_geo_t *geo)
1308{
1309 u_char *p, *q;
1310 int n;
1311
1312 p = (u_char *)tuple->TupleData;
1313 q = p + tuple->TupleDataLen;
1314
1315 for (n = 0; n < CISTPL_MAX_DEVICES; n++) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001316 if (p > q-6)
1317 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 geo->geo[n].buswidth = p[0];
1319 geo->geo[n].erase_block = 1 << (p[1]-1);
1320 geo->geo[n].read_block = 1 << (p[2]-1);
1321 geo->geo[n].write_block = 1 << (p[3]-1);
1322 geo->geo[n].partition = 1 << (p[4]-1);
1323 geo->geo[n].interleave = 1 << (p[5]-1);
1324 p += 6;
1325 }
1326 geo->ngeo = n;
Dominik Brodowski4c89e882008-08-03 10:07:45 +02001327 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328}
1329
1330/*====================================================================*/
1331
1332static int parse_vers_2(tuple_t *tuple, cistpl_vers_2_t *v2)
1333{
1334 u_char *p, *q;
1335
1336 if (tuple->TupleDataLen < 10)
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001337 return -EINVAL;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001338
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 p = tuple->TupleData;
1340 q = p + tuple->TupleDataLen;
1341
1342 v2->vers = p[0];
1343 v2->comply = p[1];
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001344 v2->dindex = get_unaligned_le16(p + 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345 v2->vspec8 = p[6];
1346 v2->vspec9 = p[7];
1347 v2->nhdr = p[8];
1348 p += 9;
1349 return parse_strings(p, q, 2, v2->str, &v2->vendor, NULL);
1350}
1351
1352/*====================================================================*/
1353
1354static int parse_org(tuple_t *tuple, cistpl_org_t *org)
1355{
1356 u_char *p, *q;
1357 int i;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001358
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 p = tuple->TupleData;
1360 q = p + tuple->TupleDataLen;
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001361 if (p == q)
1362 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 org->data_org = *p;
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001364 if (++p == q)
1365 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 for (i = 0; i < 30; i++) {
1367 org->desc[i] = *p;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001368 if (*p == '\0')
1369 break;
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001370 if (++p == q)
1371 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 }
Dominik Brodowski4c89e882008-08-03 10:07:45 +02001373 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374}
1375
1376/*====================================================================*/
1377
1378static int parse_format(tuple_t *tuple, cistpl_format_t *fmt)
1379{
1380 u_char *p;
1381
1382 if (tuple->TupleDataLen < 10)
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001383 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384
1385 p = tuple->TupleData;
1386
1387 fmt->type = p[0];
1388 fmt->edc = p[1];
Harvey Harrison6b1e6f62008-04-29 01:03:39 -07001389 fmt->offset = get_unaligned_le32(p + 2);
1390 fmt->length = get_unaligned_le32(p + 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391
Dominik Brodowski4c89e882008-08-03 10:07:45 +02001392 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393}
1394
1395/*====================================================================*/
1396
Dominik Brodowski2f3061e2008-08-31 15:50:33 +02001397int pcmcia_parse_tuple(tuple_t *tuple, cisparse_t *parse)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398{
Dominik Brodowski4c89e882008-08-03 10:07:45 +02001399 int ret = 0;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001400
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 if (tuple->TupleDataLen > tuple->TupleDataMax)
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001402 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403 switch (tuple->TupleCode) {
1404 case CISTPL_DEVICE:
1405 case CISTPL_DEVICE_A:
1406 ret = parse_device(tuple, &parse->device);
1407 break;
1408#ifdef CONFIG_CARDBUS
1409 case CISTPL_BAR:
1410 ret = parse_bar(tuple, &parse->bar);
1411 break;
1412 case CISTPL_CONFIG_CB:
1413 ret = parse_config_cb(tuple, &parse->config);
1414 break;
1415 case CISTPL_CFTABLE_ENTRY_CB:
1416 ret = parse_cftable_entry_cb(tuple, &parse->cftable_entry_cb);
1417 break;
1418#endif
1419 case CISTPL_CHECKSUM:
1420 ret = parse_checksum(tuple, &parse->checksum);
1421 break;
1422 case CISTPL_LONGLINK_A:
1423 case CISTPL_LONGLINK_C:
1424 ret = parse_longlink(tuple, &parse->longlink);
1425 break;
1426 case CISTPL_LONGLINK_MFC:
1427 ret = parse_longlink_mfc(tuple, &parse->longlink_mfc);
1428 break;
1429 case CISTPL_VERS_1:
1430 ret = parse_vers_1(tuple, &parse->version_1);
1431 break;
1432 case CISTPL_ALTSTR:
1433 ret = parse_altstr(tuple, &parse->altstr);
1434 break;
1435 case CISTPL_JEDEC_A:
1436 case CISTPL_JEDEC_C:
1437 ret = parse_jedec(tuple, &parse->jedec);
1438 break;
1439 case CISTPL_MANFID:
1440 ret = parse_manfid(tuple, &parse->manfid);
1441 break;
1442 case CISTPL_FUNCID:
1443 ret = parse_funcid(tuple, &parse->funcid);
1444 break;
1445 case CISTPL_FUNCE:
1446 ret = parse_funce(tuple, &parse->funce);
1447 break;
1448 case CISTPL_CONFIG:
1449 ret = parse_config(tuple, &parse->config);
1450 break;
1451 case CISTPL_CFTABLE_ENTRY:
1452 ret = parse_cftable_entry(tuple, &parse->cftable_entry);
1453 break;
1454 case CISTPL_DEVICE_GEO:
1455 case CISTPL_DEVICE_GEO_A:
1456 ret = parse_device_geo(tuple, &parse->device_geo);
1457 break;
1458 case CISTPL_VERS_2:
1459 ret = parse_vers_2(tuple, &parse->vers_2);
1460 break;
1461 case CISTPL_ORG:
1462 ret = parse_org(tuple, &parse->org);
1463 break;
1464 case CISTPL_FORMAT:
1465 case CISTPL_FORMAT_A:
1466 ret = parse_format(tuple, &parse->format);
1467 break;
1468 case CISTPL_NO_LINK:
1469 case CISTPL_LINKTARGET:
Dominik Brodowski4c89e882008-08-03 10:07:45 +02001470 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 break;
1472 default:
Dominik Brodowskide6405e2008-08-03 10:47:59 +02001473 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 break;
1475 }
Dominik Brodowski3f9c5f42008-08-03 12:22:40 +02001476 if (ret)
Dominik Brodowskid50dbec2009-10-23 12:51:28 +02001477 pr_debug("parse_tuple failed %d\n", ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 return ret;
1479}
Dominik Brodowski2f3061e2008-08-31 15:50:33 +02001480EXPORT_SYMBOL(pcmcia_parse_tuple);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481
1482/*======================================================================
1483
1484 This is used internally by Card Services to look up CIS stuff.
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001485
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486======================================================================*/
1487
1488int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, cisdata_t code, void *parse)
1489{
1490 tuple_t tuple;
1491 cisdata_t *buf;
1492 int ret;
1493
1494 buf = kmalloc(256, GFP_KERNEL);
Dominik Brodowski11683862008-08-03 10:22:47 +02001495 if (buf == NULL) {
1496 dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
1497 return -ENOMEM;
1498 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 tuple.DesiredTuple = code;
Dominik Brodowski84897fc2009-10-18 23:51:09 +02001500 tuple.Attributes = 0;
1501 if (function == BIND_FN_ALL)
1502 tuple.Attributes = TUPLE_RETURN_COMMON;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 ret = pccard_get_first_tuple(s, function, &tuple);
Dominik Brodowski4c89e882008-08-03 10:07:45 +02001504 if (ret != 0)
1505 goto done;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506 tuple.TupleData = buf;
1507 tuple.TupleOffset = 0;
1508 tuple.TupleDataMax = 255;
1509 ret = pccard_get_tuple_data(s, &tuple);
Dominik Brodowski4c89e882008-08-03 10:07:45 +02001510 if (ret != 0)
1511 goto done;
Dominik Brodowski2f3061e2008-08-31 15:50:33 +02001512 ret = pcmcia_parse_tuple(&tuple, parse);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513done:
1514 kfree(buf);
1515 return ret;
1516}
1517EXPORT_SYMBOL(pccard_read_tuple);
1518
Dominik Brodowski91284222009-10-18 23:32:33 +02001519
1520/**
1521 * pccard_loop_tuple() - loop over tuples in the CIS
1522 * @s: the struct pcmcia_socket where the card is inserted
1523 * @function: the device function we loop for
1524 * @code: which CIS code shall we look for?
1525 * @parse: buffer where the tuple shall be parsed (or NULL, if no parse)
1526 * @priv_data: private data to be passed to the loop_tuple function.
1527 * @loop_tuple: function to call for each CIS entry of type @function. IT
1528 * gets passed the raw tuple, the paresed tuple (if @parse is
1529 * set) and @priv_data.
1530 *
1531 * pccard_loop_tuple() loops over all CIS entries of type @function, and
1532 * calls the @loop_tuple function for each entry. If the call to @loop_tuple
1533 * returns 0, the loop exits. Returns 0 on success or errorcode otherwise.
1534 */
1535int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function,
1536 cisdata_t code, cisparse_t *parse, void *priv_data,
1537 int (*loop_tuple) (tuple_t *tuple,
1538 cisparse_t *parse,
1539 void *priv_data))
1540{
1541 tuple_t tuple;
1542 cisdata_t *buf;
1543 int ret;
1544
1545 buf = kzalloc(256, GFP_KERNEL);
1546 if (buf == NULL) {
1547 dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
1548 return -ENOMEM;
1549 }
1550
1551 tuple.TupleData = buf;
1552 tuple.TupleDataMax = 255;
1553 tuple.TupleOffset = 0;
1554 tuple.DesiredTuple = code;
1555 tuple.Attributes = 0;
1556
1557 ret = pccard_get_first_tuple(s, function, &tuple);
1558 while (!ret) {
1559 if (pccard_get_tuple_data(s, &tuple))
1560 goto next_entry;
1561
1562 if (parse)
1563 if (pcmcia_parse_tuple(&tuple, parse))
1564 goto next_entry;
1565
1566 ret = loop_tuple(&tuple, parse, priv_data);
1567 if (!ret)
1568 break;
1569
1570next_entry:
1571 ret = pccard_get_next_tuple(s, function, &tuple);
1572 }
1573
1574 kfree(buf);
1575 return ret;
1576}
1577EXPORT_SYMBOL(pccard_loop_tuple);
1578
1579
Dominik Brodowskif131ddc2010-01-02 12:58:10 +01001580/**
1581 * pccard_validate_cis() - check whether card has a sensible CIS
1582 * @s: the struct pcmcia_socket we are to check
1583 * @info: returns the number of tuples in the (valid) CIS, or 0
1584 *
1585 * This tries to determine if a card has a sensible CIS. In @info, it
1586 * returns the number of tuples in the CIS, or 0 if the CIS looks bad. The
1587 * checks include making sure several critical tuples are present and
1588 * valid; seeing if the total number of tuples is reasonable; and
1589 * looking for tuples that use reserved codes.
1590 *
1591 * The function returns 0 on success.
1592 */
Dominik Brodowski84897fc2009-10-18 23:51:09 +02001593int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594{
Dominik Brodowskif131ddc2010-01-02 12:58:10 +01001595 tuple_t *tuple;
1596 cisparse_t *p;
1597 unsigned int count = 0;
1598 int ret, reserved, dev_ok = 0, ident_ok = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599
Dominik Brodowskif131ddc2010-01-02 12:58:10 +01001600 if (!s)
1601 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602
Dominik Brodowskif131ddc2010-01-02 12:58:10 +01001603 /* We do not want to validate the CIS cache... */
1604 destroy_cis_cache(s);
Dominik Brodowski904e3772010-01-02 12:28:04 +01001605
Dominik Brodowskif131ddc2010-01-02 12:58:10 +01001606 tuple = kmalloc(sizeof(*tuple), GFP_KERNEL);
1607 if (tuple == NULL) {
1608 dev_warn(&s->dev, "no memory to validate CIS\n");
1609 return -ENOMEM;
1610 }
1611 p = kmalloc(sizeof(*p), GFP_KERNEL);
1612 if (p == NULL) {
1613 kfree(tuple);
1614 dev_warn(&s->dev, "no memory to validate CIS\n");
1615 return -ENOMEM;
1616 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617
Dominik Brodowskif131ddc2010-01-02 12:58:10 +01001618 count = reserved = 0;
1619 tuple->DesiredTuple = RETURN_FIRST_TUPLE;
1620 tuple->Attributes = TUPLE_RETURN_COMMON;
1621 ret = pccard_get_first_tuple(s, BIND_FN_ALL, tuple);
Dominik Brodowski4c89e882008-08-03 10:07:45 +02001622 if (ret != 0)
Dominik Brodowskif131ddc2010-01-02 12:58:10 +01001623 goto done;
1624
1625 /* First tuple should be DEVICE; we should really have either that
1626 or a CFTABLE_ENTRY of some sort */
1627 if ((tuple->TupleCode == CISTPL_DEVICE) ||
1628 (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY, p)) ||
1629 (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY_CB, p)))
1630 dev_ok++;
1631
1632 /* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2
1633 tuple, for card identification. Certain old D-Link and Linksys
1634 cards have only a broken VERS_2 tuple; hence the bogus test. */
1635 if ((pccard_read_tuple(s, BIND_FN_ALL, CISTPL_MANFID, p) == 0) ||
1636 (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_1, p) == 0) ||
1637 (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_2, p) != -ENOSPC))
1638 ident_ok++;
1639
1640 if (!dev_ok && !ident_ok)
1641 goto done;
1642
1643 for (count = 1; count < MAX_TUPLES; count++) {
1644 ret = pccard_get_next_tuple(s, BIND_FN_ALL, tuple);
1645 if (ret != 0)
1646 break;
1647 if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) ||
1648 ((tuple->TupleCode > 0x47) && (tuple->TupleCode < 0x80)) ||
1649 ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff)))
1650 reserved++;
1651 }
1652 if ((count == MAX_TUPLES) || (reserved > 5) ||
1653 ((!dev_ok || !ident_ok) && (count > 10)))
1654 count = 0;
1655
1656 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657
1658done:
Dominik Brodowskif131ddc2010-01-02 12:58:10 +01001659 /* invalidate CIS cache on failure */
1660 if (!dev_ok || !ident_ok || !count) {
1661 destroy_cis_cache(s);
1662 ret = -EIO;
1663 }
Dominik Brodowski904e3772010-01-02 12:28:04 +01001664
Dominik Brodowskif131ddc2010-01-02 12:58:10 +01001665 if (info)
1666 *info = count;
1667 kfree(tuple);
1668 kfree(p);
1669 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670}
1671EXPORT_SYMBOL(pccard_validate_cis);