Blame view

drivers/pcmcia/cistpl.c 35.1 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * cistpl.c -- 16-bit PCMCIA Card Information Structure parser
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   * The initial developer of the original code is David A. Hinds
   * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
   * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
   *
   * (C) 1999		David A. Hinds
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
16
17
18
19
20
21
22
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/kernel.h>
  #include <linux/string.h>
  #include <linux/major.h>
  #include <linux/errno.h>
  #include <linux/timer.h>
  #include <linux/slab.h>
  #include <linux/mm.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
  #include <linux/pci.h>
  #include <linux/ioport.h>
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
25
  #include <linux/io.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
  #include <asm/byteorder.h>
dc0cf6a26   Daniel Ritz   pcmcia: cistpl: u...
27
  #include <asm/unaligned.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
  #include <pcmcia/ss.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  #include <pcmcia/cisreg.h>
  #include <pcmcia/cistpl.h>
  #include "cs_internal.h"
  
  static const u_char mantissa[] = {
      10, 12, 13, 15, 20, 25, 30, 35,
      40, 45, 50, 55, 60, 70, 80, 90
  };
  
  static const u_int exponent[] = {
      1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
  };
  
  /* Convert an extended speed byte to a time in nanoseconds */
  #define SPEED_CVT(v) \
      (mantissa[(((v)>>3)&15)-1] * exponent[(v)&7] / 10)
  /* Convert a power byte to a current in 0.1 microamps */
  #define POWER_CVT(v) \
      (mantissa[((v)>>3)&15] * exponent[(v)&7] / 10)
  #define POWER_SCALE(v)		(exponent[(v)&7])
  
  /* Upper limit on reasonable # of tuples */
  #define MAX_TUPLES		200
a3d0d4d8d   Dominik Brodowski   pcmcia: move loca...
53
54
  /* Bits in IRQInfo1 field */
  #define IRQ_INFO2_VALID		0x10
37f779554   Pavel Machek   [PATCH] pcmcia: a...
55
56
57
  /* 16-bit CIS? */
  static int cis_width;
  module_param(cis_width, int, 0444);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
59
60
  
  void release_cis_mem(struct pcmcia_socket *s)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
61
62
63
64
65
66
67
68
69
70
71
  	mutex_lock(&s->ops_mutex);
  	if (s->cis_mem.flags & MAP_ACTIVE) {
  		s->cis_mem.flags &= ~MAP_ACTIVE;
  		s->ops->set_mem_map(s, &s->cis_mem);
  		if (s->cis_mem.res) {
  			release_resource(s->cis_mem.res);
  			kfree(s->cis_mem.res);
  			s->cis_mem.res = NULL;
  		}
  		iounmap(s->cis_virt);
  		s->cis_virt = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
  	}
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
73
  	mutex_unlock(&s->ops_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
76
77
78
  /**
   * set_cis_map() - map the card memory at "card_offset" into virtual space.
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
80
   * If flags & MAP_ATTRIB, map the attribute space, otherwise
   * map the memory space.
7ab248554   Dominik Brodowski   pcmcia: add locki...
81
82
   *
   * Must be called with ops_mutex held.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
   */
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
84
85
  static void __iomem *set_cis_map(struct pcmcia_socket *s,
  				unsigned int card_offset, unsigned int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
  {
2e5a3e790   Dominik Brodowski   [PATCH] pcmcia: a...
87
88
89
90
  	pccard_mem_map *mem = &s->cis_mem;
  	int ret;
  
  	if (!(s->features & SS_CAP_STATIC_MAP) && (mem->res == NULL)) {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
91
92
  		mem->res = pcmcia_find_mem_region(0, s->map_size,
  						s->map_size, 0, s);
2e5a3e790   Dominik Brodowski   [PATCH] pcmcia: a...
93
  		if (mem->res == NULL) {
f2e6cf767   Joe Perches   pcmcia: Convert d...
94
95
  			dev_notice(&s->dev, "cs: unable to map card memory!
  ");
2e5a3e790   Dominik Brodowski   [PATCH] pcmcia: a...
96
97
98
  			return NULL;
  		}
  		s->cis_virt = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
  	}
2ad0a0a79   Dominik Brodowski   [PATCH] pcmcia: r...
100

2e5a3e790   Dominik Brodowski   [PATCH] pcmcia: a...
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
  	if (!(s->features & SS_CAP_STATIC_MAP) && (!s->cis_virt))
  		s->cis_virt = ioremap(mem->res->start, s->map_size);
  
  	mem->card_start = card_offset;
  	mem->flags = flags;
  
  	ret = s->ops->set_mem_map(s, mem);
  	if (ret) {
  		iounmap(s->cis_virt);
  		s->cis_virt = NULL;
  		return NULL;
  	}
  
  	if (s->features & SS_CAP_STATIC_MAP) {
  		if (s->cis_virt)
  			iounmap(s->cis_virt);
  		s->cis_virt = ioremap(mem->static_start, s->map_size);
  	}
  
  	return s->cis_virt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
123
124
125
  
  /* Bits in attr field */
  #define IS_ATTR		1
  #define IS_INDIRECT	8
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
126
127
  /**
   * pcmcia_read_cis_mem() - low-level function to read CIS memory
059f667d9   Dominik Brodowski   pcmcia: call pcmc...
128
129
   *
   * must be called with ops_mutex held
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
130
   */
e6ea0b9ec   Dominik Brodowski   [PATCH] pcmcia: r...
131
  int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
  		 u_int len, void *ptr)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
134
135
  	void __iomem *sys, *end;
  	unsigned char *buf = ptr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
137
138
  	dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)
  ", attr, addr, len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
140
141
142
143
144
145
146
147
  	if (attr & IS_INDIRECT) {
  		/* Indirect accesses use a bunch of special registers at fixed
  		   locations in common memory */
  		u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
  		if (attr & IS_ATTR) {
  			addr *= 2;
  			flags = ICTRL0_AUTOINC;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
148

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
149
150
151
152
153
154
  		sys = set_cis_map(s, 0, MAP_ACTIVE |
  				((cis_width) ? MAP_16BIT : 0));
  		if (!sys) {
  			dev_dbg(&s->dev, "could not map memory
  ");
  			memset(ptr, 0xff, len);
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
155
156
157
158
159
160
161
162
163
164
165
166
  			return -1;
  		}
  
  		writeb(flags, sys+CISREG_ICTRL0);
  		writeb(addr & 0xff, sys+CISREG_IADDR0);
  		writeb((addr>>8) & 0xff, sys+CISREG_IADDR1);
  		writeb((addr>>16) & 0xff, sys+CISREG_IADDR2);
  		writeb((addr>>24) & 0xff, sys+CISREG_IADDR3);
  		for ( ; len > 0; len--, buf++)
  			*buf = readb(sys+CISREG_IDATA0);
  	} else {
  		u_int inc = 1, card_offset, flags;
b38a4bd31   Alan Cox   pcmcia cis: on an...
167
  		if (addr > CISTPL_MAX_CIS_SIZE) {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
168
169
  			dev_dbg(&s->dev,
  				"attempt to read CIS mem at addr %#x", addr);
b38a4bd31   Alan Cox   pcmcia cis: on an...
170
171
172
  			memset(ptr, 0xff, len);
  			return -1;
  		}
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
  
  		flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
  		if (attr) {
  			flags |= MAP_ATTRIB;
  			inc++;
  			addr *= 2;
  		}
  
  		card_offset = addr & ~(s->map_size-1);
  		while (len) {
  			sys = set_cis_map(s, card_offset, flags);
  			if (!sys) {
  				dev_dbg(&s->dev, "could not map memory
  ");
  				memset(ptr, 0xff, len);
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
188
189
190
191
192
193
194
195
196
197
198
199
  				return -1;
  			}
  			end = sys + s->map_size;
  			sys = sys + (addr & (s->map_size-1));
  			for ( ; len > 0; len--, buf++, sys += inc) {
  				if (sys == end)
  					break;
  				*buf = readb(sys);
  			}
  			card_offset += s->map_size;
  			addr = 0;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
  	}
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
201
202
203
204
205
  	dev_dbg(&s->dev, "  %#2.2x %#2.2x %#2.2x %#2.2x ...
  ",
  		*(u_char *)(ptr+0), *(u_char *)(ptr+1),
  		*(u_char *)(ptr+2), *(u_char *)(ptr+3));
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
  }
1a8d46631   Dominik Brodowski   [PATCH] pcmcia: m...
207

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
209
210
211
  /**
   * pcmcia_write_cis_mem() - low-level function to write CIS memory
   *
059f667d9   Dominik Brodowski   pcmcia: call pcmc...
212
213
   * Probably only useful for writing one-byte registers. Must be called
   * with ops_mutex held.
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
214
   */
1d5cc192d   Dominik Brodowski   pcmcia: use pcmic...
215
  int pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
217
  		   u_int len, void *ptr)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
218
219
  	void __iomem *sys, *end;
  	unsigned char *buf = ptr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
221
222
223
  	dev_dbg(&s->dev,
  		"pcmcia_write_cis_mem(%d, %#x, %u)
  ", attr, addr, len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
225
226
227
228
229
230
231
232
  	if (attr & IS_INDIRECT) {
  		/* Indirect accesses use a bunch of special registers at fixed
  		   locations in common memory */
  		u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
  		if (attr & IS_ATTR) {
  			addr *= 2;
  			flags = ICTRL0_AUTOINC;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
234
235
236
237
238
  		sys = set_cis_map(s, 0, MAP_ACTIVE |
  				((cis_width) ? MAP_16BIT : 0));
  		if (!sys) {
  			dev_dbg(&s->dev, "could not map memory
  ");
1d5cc192d   Dominik Brodowski   pcmcia: use pcmic...
239
  			return -EINVAL;
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
240
  		}
1a8d46631   Dominik Brodowski   [PATCH] pcmcia: m...
241

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
242
243
244
245
246
247
248
249
250
  		writeb(flags, sys+CISREG_ICTRL0);
  		writeb(addr & 0xff, sys+CISREG_IADDR0);
  		writeb((addr>>8) & 0xff, sys+CISREG_IADDR1);
  		writeb((addr>>16) & 0xff, sys+CISREG_IADDR2);
  		writeb((addr>>24) & 0xff, sys+CISREG_IADDR3);
  		for ( ; len > 0; len--, buf++)
  			writeb(*buf, sys+CISREG_IDATA0);
  	} else {
  		u_int inc = 1, card_offset, flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
252
253
254
255
256
257
  		flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
  		if (attr & IS_ATTR) {
  			flags |= MAP_ATTRIB;
  			inc++;
  			addr *= 2;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
259
260
261
262
263
264
  		card_offset = addr & ~(s->map_size-1);
  		while (len) {
  			sys = set_cis_map(s, card_offset, flags);
  			if (!sys) {
  				dev_dbg(&s->dev, "could not map memory
  ");
1d5cc192d   Dominik Brodowski   pcmcia: use pcmic...
265
  				return -EINVAL;
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
266
267
268
269
270
271
272
273
274
275
276
277
278
  			}
  
  			end = sys + s->map_size;
  			sys = sys + (addr & (s->map_size-1));
  			for ( ; len > 0; len--, buf++, sys += inc) {
  				if (sys == end)
  					break;
  				writeb(*buf, sys);
  			}
  			card_offset += s->map_size;
  			addr = 0;
  		}
  	}
1d5cc192d   Dominik Brodowski   pcmcia: use pcmic...
279
  	return 0;
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
280
  }
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
281

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
283
284
285
286
287
288
289
  /**
   * read_cis_cache() - read CIS memory or its associated cache
   *
   * This is a wrapper around read_cis_mem, with the same interface,
   * but which caches information, for cards whose CIS may not be
   * readable all the time.
   */
d700518a0   Dominik Brodowski   pcmcia: use read_...
290
291
  static int read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
  			size_t len, void *ptr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
  {
57197b9b7   Dominik Brodowski   pcmcia: CardBus d...
293
  	struct cis_cache_entry *cis;
d700518a0   Dominik Brodowski   pcmcia: use read_...
294
  	int ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295

57197b9b7   Dominik Brodowski   pcmcia: CardBus d...
296
  	if (s->state & SOCKET_CARDBUS)
d700518a0   Dominik Brodowski   pcmcia: use read_...
297
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298

8680c4b3f   Dominik Brodowski   pcmcia: also lock...
299
  	mutex_lock(&s->ops_mutex);
57197b9b7   Dominik Brodowski   pcmcia: CardBus d...
300
301
302
  	if (s->fake_cis) {
  		if (s->fake_cis_len >= addr+len)
  			memcpy(ptr, s->fake_cis+addr, len);
d700518a0   Dominik Brodowski   pcmcia: use read_...
303
  		else {
57197b9b7   Dominik Brodowski   pcmcia: CardBus d...
304
  			memset(ptr, 0xff, len);
d700518a0   Dominik Brodowski   pcmcia: use read_...
305
306
  			ret = -EINVAL;
  		}
8680c4b3f   Dominik Brodowski   pcmcia: also lock...
307
  		mutex_unlock(&s->ops_mutex);
d700518a0   Dominik Brodowski   pcmcia: use read_...
308
  		return ret;
57197b9b7   Dominik Brodowski   pcmcia: CardBus d...
309
310
311
312
313
  	}
  
  	list_for_each_entry(cis, &s->cis_cache, node) {
  		if (cis->addr == addr && cis->len == len && cis->attr == attr) {
  			memcpy(ptr, cis->cache, len);
8680c4b3f   Dominik Brodowski   pcmcia: also lock...
314
  			mutex_unlock(&s->ops_mutex);
d700518a0   Dominik Brodowski   pcmcia: use read_...
315
  			return 0;
57197b9b7   Dominik Brodowski   pcmcia: CardBus d...
316
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318

e6ea0b9ec   Dominik Brodowski   [PATCH] pcmcia: r...
319
  	ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
321
322
323
324
325
326
327
328
329
330
331
  
  	if (ret == 0) {
  		/* Copy data into the cache */
  		cis = kmalloc(sizeof(struct cis_cache_entry) + len, GFP_KERNEL);
  		if (cis) {
  			cis->addr = addr;
  			cis->len = len;
  			cis->attr = attr;
  			memcpy(cis->cache, ptr, len);
  			list_add(&cis->node, &s->cis_cache);
  		}
  	}
059f667d9   Dominik Brodowski   pcmcia: call pcmc...
332
  	mutex_unlock(&s->ops_mutex);
d700518a0   Dominik Brodowski   pcmcia: use read_...
333
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
336
337
338
339
  }
  
  static void
  remove_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len)
  {
  	struct cis_cache_entry *cis;
8680c4b3f   Dominik Brodowski   pcmcia: also lock...
340
  	mutex_lock(&s->ops_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
342
343
344
345
346
  	list_for_each_entry(cis, &s->cis_cache, node)
  		if (cis->addr == addr && cis->len == len && cis->attr == attr) {
  			list_del(&cis->node);
  			kfree(cis);
  			break;
  		}
8680c4b3f   Dominik Brodowski   pcmcia: also lock...
347
  	mutex_unlock(&s->ops_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
  }
904e37774   Dominik Brodowski   pcmcia: validate ...
349
350
351
352
  /**
   * destroy_cis_cache() - destroy the CIS cache
   * @s:		pcmcia_socket for which CIS cache shall be destroyed
   *
8680c4b3f   Dominik Brodowski   pcmcia: also lock...
353
354
   * This destroys the CIS cache but keeps any fake CIS alive. Must be
   * called with ops_mutex held.
904e37774   Dominik Brodowski   pcmcia: validate ...
355
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
357
358
  void destroy_cis_cache(struct pcmcia_socket *s)
  {
  	struct list_head *l, *n;
904e37774   Dominik Brodowski   pcmcia: validate ...
359
  	struct cis_cache_entry *cis;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
361
  
  	list_for_each_safe(l, n, &s->cis_cache) {
904e37774   Dominik Brodowski   pcmcia: validate ...
362
  		cis = list_entry(l, struct cis_cache_entry, node);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
365
  		list_del(&cis->node);
  		kfree(cis);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
368
369
370
  /**
   * verify_cis_cache() - does the CIS match what is in the CIS cache?
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
372
373
374
  int verify_cis_cache(struct pcmcia_socket *s)
  {
  	struct cis_cache_entry *cis;
  	char *buf;
d700518a0   Dominik Brodowski   pcmcia: use read_...
375
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376

57197b9b7   Dominik Brodowski   pcmcia: CardBus d...
377
378
  	if (s->state & SOCKET_CARDBUS)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  	buf = kmalloc(256, GFP_KERNEL);
e689597fe   Dominik Brodowski   pcmcia: add brace...
380
  	if (buf == NULL) {
f2e6cf767   Joe Perches   pcmcia: Convert d...
381
382
  		dev_warn(&s->dev, "no memory for verifying CIS
  ");
1168386aa   Dominik Brodowski   pcmcia: deprecate...
383
  		return -ENOMEM;
e689597fe   Dominik Brodowski   pcmcia: add brace...
384
  	}
059f667d9   Dominik Brodowski   pcmcia: call pcmc...
385
  	mutex_lock(&s->ops_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
387
388
389
390
  	list_for_each_entry(cis, &s->cis_cache, node) {
  		int len = cis->len;
  
  		if (len > 256)
  			len = 256;
57197b9b7   Dominik Brodowski   pcmcia: CardBus d...
391

d700518a0   Dominik Brodowski   pcmcia: use read_...
392
393
  		ret = pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf);
  		if (ret || memcmp(buf, cis->cache, len) != 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394
  			kfree(buf);
059f667d9   Dominik Brodowski   pcmcia: call pcmc...
395
  			mutex_unlock(&s->ops_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
397
398
399
  			return -1;
  		}
  	}
  	kfree(buf);
059f667d9   Dominik Brodowski   pcmcia: call pcmc...
400
  	mutex_unlock(&s->ops_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
402
  	return 0;
  }
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
403
404
405
406
407
408
  /**
   * pcmcia_replace_cis() - use a replacement CIS instead of the card's CIS
   *
   * For really bad cards, we provide a facility for uploading a
   * replacement CIS.
   */
53efec951   Dominik Brodowski   pcmcia: only copy...
409
410
  int pcmcia_replace_cis(struct pcmcia_socket *s,
  		       const u8 *data, const size_t len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
  {
1168386aa   Dominik Brodowski   pcmcia: deprecate...
412
  	if (len > CISTPL_MAX_CIS_SIZE) {
f2e6cf767   Joe Perches   pcmcia: Convert d...
413
414
  		dev_warn(&s->dev, "replacement CIS too big
  ");
1168386aa   Dominik Brodowski   pcmcia: deprecate...
415
416
  		return -EINVAL;
  	}
8680c4b3f   Dominik Brodowski   pcmcia: also lock...
417
  	mutex_lock(&s->ops_mutex);
1168386aa   Dominik Brodowski   pcmcia: deprecate...
418
419
420
  	kfree(s->fake_cis);
  	s->fake_cis = kmalloc(len, GFP_KERNEL);
  	if (s->fake_cis == NULL) {
f2e6cf767   Joe Perches   pcmcia: Convert d...
421
422
  		dev_warn(&s->dev, "no memory to replace CIS
  ");
8680c4b3f   Dominik Brodowski   pcmcia: also lock...
423
  		mutex_unlock(&s->ops_mutex);
1168386aa   Dominik Brodowski   pcmcia: deprecate...
424
425
426
427
  		return -ENOMEM;
  	}
  	s->fake_cis_len = len;
  	memcpy(s->fake_cis, data, len);
d700518a0   Dominik Brodowski   pcmcia: use read_...
428
429
  	dev_info(&s->dev, "Using replacement CIS
  ");
8680c4b3f   Dominik Brodowski   pcmcia: also lock...
430
  	mutex_unlock(&s->ops_mutex);
1168386aa   Dominik Brodowski   pcmcia: deprecate...
431
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
  }
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
433
  /* The high-level CIS tuple services */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434

820dc8464   Himangi Saraogi   pcmcia: Remove ty...
435
  struct tuple_flags {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
436
437
438
439
  	u_int		link_space:4;
  	u_int		has_link:1;
  	u_int		mfc_fn:3;
  	u_int		space:4;
820dc8464   Himangi Saraogi   pcmcia: Remove ty...
440
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
441

820dc8464   Himangi Saraogi   pcmcia: Remove ty...
442
443
444
445
  #define LINK_SPACE(f)	(((struct tuple_flags *)(&(f)))->link_space)
  #define HAS_LINK(f)	(((struct tuple_flags *)(&(f)))->has_link)
  #define MFC_FN(f)	(((struct tuple_flags *)(&(f)))->mfc_fn)
  #define SPACE(f)	(((struct tuple_flags *)(&(f)))->space)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
447
448
  int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function,
  			tuple_t *tuple)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
449
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
  	if (!s)
  		return -EINVAL;
  
  	if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS))
  		return -ENODEV;
  	tuple->TupleLink = tuple->Flags = 0;
  
  	/* Assume presence of a LONGLINK_C to address 0 */
  	tuple->CISOffset = tuple->LinkOffset = 0;
  	SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1;
  
  	if ((s->functions > 1) && !(tuple->Attributes & TUPLE_RETURN_COMMON)) {
  		cisdata_t req = tuple->DesiredTuple;
  		tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
  		if (pccard_get_next_tuple(s, function, tuple) == 0) {
  			tuple->DesiredTuple = CISTPL_LINKTARGET;
  			if (pccard_get_next_tuple(s, function, tuple) != 0)
  				return -ENOSPC;
  		} else
  			tuple->CISOffset = tuple->TupleLink = 0;
  		tuple->DesiredTuple = req;
  	}
  	return pccard_get_next_tuple(s, function, tuple);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
474
475
476
  
  static int follow_link(struct pcmcia_socket *s, tuple_t *tuple)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
  	u_char link[5];
  	u_int ofs;
  	int ret;
  
  	if (MFC_FN(tuple->Flags)) {
  		/* Get indirect link from the MFC tuple */
  		ret = read_cis_cache(s, LINK_SPACE(tuple->Flags),
  				tuple->LinkOffset, 5, link);
  		if (ret)
  			return -1;
  		ofs = get_unaligned_le32(link + 1);
  		SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR);
  		/* Move to the next indirect link */
  		tuple->LinkOffset += 5;
  		MFC_FN(tuple->Flags)--;
  	} else if (HAS_LINK(tuple->Flags)) {
  		ofs = tuple->LinkOffset;
  		SPACE(tuple->Flags) = LINK_SPACE(tuple->Flags);
  		HAS_LINK(tuple->Flags) = 0;
  	} else
d700518a0   Dominik Brodowski   pcmcia: use read_...
497
  		return -1;
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
498
499
500
501
502
503
504
505
506
507
508
509
510
511
  
  	if (SPACE(tuple->Flags)) {
  		/* This is ugly, but a common CIS error is to code the long
  		   link offset incorrectly, so we check the right spot... */
  		ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
  		if (ret)
  			return -1;
  		if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
  			(strncmp(link+2, "CIS", 3) == 0))
  			return ofs;
  		remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5);
  		/* Then, we try the wrong spot... */
  		ofs = ofs >> 1;
  	}
d700518a0   Dominik Brodowski   pcmcia: use read_...
512
513
514
  	ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
  	if (ret)
  		return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
  	if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
516
517
  		(strncmp(link+2, "CIS", 3) == 0))
  		return ofs;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
  	remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5);
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
519
  	return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520
  }
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
521
522
  int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function,
  			tuple_t *tuple)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
524
525
526
  	u_char link[2], tmp;
  	int ofs, i, attr;
  	int ret;
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
527

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
528
529
530
531
  	if (!s)
  		return -EINVAL;
  	if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS))
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
  	link[1] = tuple->TupleLink;
  	ofs = tuple->CISOffset + tuple->TupleLink;
  	attr = SPACE(tuple->Flags);
  
  	for (i = 0; i < MAX_TUPLES; i++) {
  		if (link[1] == 0xff)
  			link[0] = CISTPL_END;
  		else {
  			ret = read_cis_cache(s, attr, ofs, 2, link);
  			if (ret)
  				return -1;
  			if (link[0] == CISTPL_NULL) {
  				ofs++;
  				continue;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
548
  		}
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
549

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
550
551
552
553
554
555
556
557
558
559
  		/* End of chain?  Follow long link if possible */
  		if (link[0] == CISTPL_END) {
  			ofs = follow_link(s, tuple);
  			if (ofs < 0)
  				return -ENOSPC;
  			attr = SPACE(tuple->Flags);
  			ret = read_cis_cache(s, attr, ofs, 2, link);
  			if (ret)
  				return -1;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
560

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
  		/* Is this a link tuple?  Make a note of it */
  		if ((link[0] == CISTPL_LONGLINK_A) ||
  			(link[0] == CISTPL_LONGLINK_C) ||
  			(link[0] == CISTPL_LONGLINK_MFC) ||
  			(link[0] == CISTPL_LINKTARGET) ||
  			(link[0] == CISTPL_INDIRECT) ||
  			(link[0] == CISTPL_NO_LINK)) {
  			switch (link[0]) {
  			case CISTPL_LONGLINK_A:
  				HAS_LINK(tuple->Flags) = 1;
  				LINK_SPACE(tuple->Flags) = attr | IS_ATTR;
  				ret = read_cis_cache(s, attr, ofs+2, 4,
  						&tuple->LinkOffset);
  				if (ret)
  					return -1;
  				break;
  			case CISTPL_LONGLINK_C:
  				HAS_LINK(tuple->Flags) = 1;
  				LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR;
  				ret = read_cis_cache(s, attr, ofs+2, 4,
  						&tuple->LinkOffset);
  				if (ret)
  					return -1;
  				break;
  			case CISTPL_INDIRECT:
  				HAS_LINK(tuple->Flags) = 1;
  				LINK_SPACE(tuple->Flags) = IS_ATTR |
  					IS_INDIRECT;
  				tuple->LinkOffset = 0;
  				break;
  			case CISTPL_LONGLINK_MFC:
  				tuple->LinkOffset = ofs + 3;
  				LINK_SPACE(tuple->Flags) = attr;
  				if (function == BIND_FN_ALL) {
  					/* Follow all the MFC links */
  					ret = read_cis_cache(s, attr, ofs+2,
  							1, &tmp);
  					if (ret)
  						return -1;
  					MFC_FN(tuple->Flags) = tmp;
  				} else {
  					/* Follow exactly one of the links */
  					MFC_FN(tuple->Flags) = 1;
  					tuple->LinkOffset += function * 5;
  				}
  				break;
  			case CISTPL_NO_LINK:
  				HAS_LINK(tuple->Flags) = 0;
  				break;
  			}
  			if ((tuple->Attributes & TUPLE_RETURN_LINK) &&
  				(tuple->DesiredTuple == RETURN_FIRST_TUPLE))
  				break;
  		} else
  			if (tuple->DesiredTuple == RETURN_FIRST_TUPLE)
  				break;
  
  		if (link[0] == tuple->DesiredTuple)
  			break;
  		ofs += link[1] + 2;
  	}
  	if (i == MAX_TUPLES) {
  		dev_dbg(&s->dev, "cs: overrun in pcmcia_get_next_tuple
  ");
  		return -ENOSPC;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
628
629
630
631
632
  	tuple->TupleCode = link[0];
  	tuple->TupleLink = link[1];
  	tuple->CISOffset = ofs + 2;
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
633
634
635
  
  int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
636
637
  	u_int len;
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
639
640
  	if (!s)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
642
643
644
645
646
647
648
649
650
651
652
653
  	if (tuple->TupleLink < tuple->TupleOffset)
  		return -ENOSPC;
  	len = tuple->TupleLink - tuple->TupleOffset;
  	tuple->TupleDataLen = tuple->TupleLink;
  	if (len == 0)
  		return 0;
  	ret = read_cis_cache(s, SPACE(tuple->Flags),
  			tuple->CISOffset + tuple->TupleOffset,
  			min(len, (u_int) tuple->TupleDataMax),
  			tuple->TupleData);
  	if (ret)
  		return -1;
4c89e88bf   Dominik Brodowski   pcmcia: deprecate...
654
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
657
  /* Parsing routines for individual tuples */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658
659
660
  
  static int parse_device(tuple_t *tuple, cistpl_device_t *device)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
661
662
663
  	int i;
  	u_char scale;
  	u_char *p, *q;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
664

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
665
666
  	p = (u_char *)tuple->TupleData;
  	q = p + tuple->TupleDataLen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
668
669
  	device->ndev = 0;
  	for (i = 0; i < CISTPL_MAX_DEVICES; i++) {
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
670

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
  		if (*p == 0xff)
  			break;
  		device->dev[i].type = (*p >> 4);
  		device->dev[i].wp = (*p & 0x08) ? 1 : 0;
  		switch (*p & 0x07) {
  		case 0:
  			device->dev[i].speed = 0;
  			break;
  		case 1:
  			device->dev[i].speed = 250;
  			break;
  		case 2:
  			device->dev[i].speed = 200;
  			break;
  		case 3:
  			device->dev[i].speed = 150;
  			break;
  		case 4:
  			device->dev[i].speed = 100;
  			break;
  		case 7:
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
692
693
  			if (++p == q)
  				return -EINVAL;
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
694
695
696
697
698
699
700
701
  			device->dev[i].speed = SPEED_CVT(*p);
  			while (*p & 0x80)
  				if (++p == q)
  					return -EINVAL;
  			break;
  		default:
  			return -EINVAL;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
702

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
703
704
705
706
707
708
709
710
711
712
713
714
  		if (++p == q)
  			return -EINVAL;
  		if (*p == 0xff)
  			break;
  		scale = *p & 7;
  		if (scale == 7)
  			return -EINVAL;
  		device->dev[i].size = ((*p >> 3) + 1) * (512 << (scale*2));
  		device->ndev++;
  		if (++p == q)
  			break;
  	}
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
715

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
716
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
718
719
720
  
  static int parse_checksum(tuple_t *tuple, cistpl_checksum_t *csum)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
721
722
723
724
725
726
727
728
  	u_char *p;
  	if (tuple->TupleDataLen < 5)
  		return -EINVAL;
  	p = (u_char *) tuple->TupleData;
  	csum->addr = tuple->CISOffset + get_unaligned_le16(p) - 2;
  	csum->len = get_unaligned_le16(p + 2);
  	csum->sum = *(p + 4);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
729
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
731
732
  
  static int parse_longlink(tuple_t *tuple, cistpl_longlink_t *link)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
733
734
735
736
  	if (tuple->TupleDataLen < 4)
  		return -EINVAL;
  	link->addr = get_unaligned_le32(tuple->TupleData);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
737
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
738

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
739
  static int parse_longlink_mfc(tuple_t *tuple, cistpl_longlink_mfc_t *link)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
741
742
743
744
745
746
747
748
749
750
751
752
753
754
  	u_char *p;
  	int i;
  
  	p = (u_char *)tuple->TupleData;
  
  	link->nfn = *p; p++;
  	if (tuple->TupleDataLen <= link->nfn*5)
  		return -EINVAL;
  	for (i = 0; i < link->nfn; i++) {
  		link->fn[i].space = *p; p++;
  		link->fn[i].addr = get_unaligned_le32(p);
  		p += 4;
  	}
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
755
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
756
757
758
759
  
  static int parse_strings(u_char *p, u_char *q, int max,
  			 char *s, u_char *ofs, u_char *found)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
760
  	int i, j, ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
761

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
  	if (p == q)
  		return -EINVAL;
  	ns = 0; j = 0;
  	for (i = 0; i < max; i++) {
  		if (*p == 0xff)
  			break;
  		ofs[i] = j;
  		ns++;
  		for (;;) {
  			s[j++] = (*p == 0xff) ? '\0' : *p;
  			if ((*p == '\0') || (*p == 0xff))
  				break;
  			if (++p == q)
  				return -EINVAL;
  		}
  		if ((*p == 0xff) || (++p == q))
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
779
  	}
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
780
781
782
783
  	if (found) {
  		*found = ns;
  		return 0;
  	}
3f9c5f4cb   Dominik Brodowski   pcmcia: deprecate...
784
  	return (ns == max) ? 0 : -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
785
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
786
787
788
  
  static int parse_vers_1(tuple_t *tuple, cistpl_vers_1_t *vers_1)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
789
  	u_char *p, *q;
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
790

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
791
792
  	p = (u_char *)tuple->TupleData;
  	q = p + tuple->TupleDataLen;
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
793

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
794
795
796
797
  	vers_1->major = *p; p++;
  	vers_1->minor = *p; p++;
  	if (p >= q)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
798

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
799
800
  	return parse_strings(p, q, CISTPL_VERS_1_MAX_PROD_STRINGS,
  			vers_1->str, vers_1->ofs, &vers_1->ns);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
801
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
802
803
804
  
  static int parse_altstr(tuple_t *tuple, cistpl_altstr_t *altstr)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
805
  	u_char *p, *q;
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
806

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
807
808
  	p = (u_char *)tuple->TupleData;
  	q = p + tuple->TupleDataLen;
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
809

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
810
811
  	return parse_strings(p, q, CISTPL_MAX_ALTSTR_STRINGS,
  			altstr->str, altstr->ofs, &altstr->ns);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
812
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
813
814
815
  
  static int parse_jedec(tuple_t *tuple, cistpl_jedec_t *jedec)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
816
817
  	u_char *p, *q;
  	int nid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
818

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
819
820
  	p = (u_char *)tuple->TupleData;
  	q = p + tuple->TupleDataLen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
821

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
822
823
824
825
826
827
828
829
830
  	for (nid = 0; nid < CISTPL_MAX_DEVICES; nid++) {
  		if (p > q-2)
  			break;
  		jedec->id[nid].mfr = p[0];
  		jedec->id[nid].info = p[1];
  		p += 2;
  	}
  	jedec->nid = nid;
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
831
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
832
833
834
  
  static int parse_manfid(tuple_t *tuple, cistpl_manfid_t *m)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
835
836
837
838
839
  	if (tuple->TupleDataLen < 4)
  		return -EINVAL;
  	m->manf = get_unaligned_le16(tuple->TupleData);
  	m->card = get_unaligned_le16(tuple->TupleData + 2);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
840
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
841
842
843
  
  static int parse_funcid(tuple_t *tuple, cistpl_funcid_t *f)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
844
845
846
847
848
849
850
  	u_char *p;
  	if (tuple->TupleDataLen < 2)
  		return -EINVAL;
  	p = (u_char *)tuple->TupleData;
  	f->func = p[0];
  	f->sysinit = p[1];
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
851
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
852
853
854
  
  static int parse_funce(tuple_t *tuple, cistpl_funce_t *f)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
855
856
857
858
859
860
861
862
863
  	u_char *p;
  	int i;
  	if (tuple->TupleDataLen < 1)
  		return -EINVAL;
  	p = (u_char *)tuple->TupleData;
  	f->type = p[0];
  	for (i = 1; i < tuple->TupleDataLen; i++)
  		f->data[i-1] = p[i];
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
864
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
865
866
867
  
  static int parse_config(tuple_t *tuple, cistpl_config_t *config)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
  	int rasz, rmsz, i;
  	u_char *p;
  
  	p = (u_char *)tuple->TupleData;
  	rasz = *p & 0x03;
  	rmsz = (*p & 0x3c) >> 2;
  	if (tuple->TupleDataLen < rasz+rmsz+4)
  		return -EINVAL;
  	config->last_idx = *(++p);
  	p++;
  	config->base = 0;
  	for (i = 0; i <= rasz; i++)
  		config->base += p[i] << (8*i);
  	p += rasz+1;
  	for (i = 0; i < 4; i++)
  		config->rmask[i] = 0;
  	for (i = 0; i <= rmsz; i++)
  		config->rmask[i>>2] += p[i] << (8*(i%4));
  	config->subtuples = tuple->TupleDataLen - (rasz+rmsz+4);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888
  }
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
889
890
891
  /* The following routines are all used to parse the nightmarish
   * config table entries.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
892

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
  static u_char *parse_power(u_char *p, u_char *q, cistpl_power_t *pwr)
  {
  	int i;
  	u_int scale;
  
  	if (p == q)
  		return NULL;
  	pwr->present = *p;
  	pwr->flags = 0;
  	p++;
  	for (i = 0; i < 7; i++)
  		if (pwr->present & (1<<i)) {
  			if (p == q)
  				return NULL;
  			pwr->param[i] = POWER_CVT(*p);
  			scale = POWER_SCALE(*p);
  			while (*p & 0x80) {
  				if (++p == q)
  					return NULL;
  				if ((*p & 0x7f) < 100)
  					pwr->param[i] +=
  						(*p & 0x7f) * scale / 100;
  				else if (*p == 0x7d)
  					pwr->flags |= CISTPL_POWER_HIGHZ_OK;
  				else if (*p == 0x7e)
  					pwr->param[i] = 0;
  				else if (*p == 0x7f)
  					pwr->flags |= CISTPL_POWER_HIGHZ_REQ;
  				else
  					return NULL;
  			}
  			p++;
  		}
  	return p;
  }
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
928

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
929

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
930
  static u_char *parse_timing(u_char *p, u_char *q, cistpl_timing_t *timing)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
931
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
932
933
934
935
936
937
  	u_char scale;
  
  	if (p == q)
  		return NULL;
  	scale = *p;
  	if ((scale & 3) != 3) {
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
938
939
  		if (++p == q)
  			return NULL;
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
  		timing->wait = SPEED_CVT(*p);
  		timing->waitscale = exponent[scale & 3];
  	} else
  		timing->wait = 0;
  	scale >>= 2;
  	if ((scale & 7) != 7) {
  		if (++p == q)
  			return NULL;
  		timing->ready = SPEED_CVT(*p);
  		timing->rdyscale = exponent[scale & 7];
  	} else
  		timing->ready = 0;
  	scale >>= 3;
  	if (scale != 7) {
  		if (++p == q)
  			return NULL;
  		timing->reserved = SPEED_CVT(*p);
  		timing->rsvscale = exponent[scale];
  	} else
  		timing->reserved = 0;
  	p++;
  	return p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
962
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
963

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
964
  static u_char *parse_io(u_char *p, u_char *q, cistpl_io_t *io)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
965
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
966
  	int i, j, bsz, lsz;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
967

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
968
  	if (p == q)
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
969
  		return NULL;
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
970
971
972
973
974
975
976
977
  	io->flags = *p;
  
  	if (!(*p & 0x80)) {
  		io->nwin = 1;
  		io->win[0].base = 0;
  		io->win[0].len = (1 << (io->flags & CISTPL_IO_LINES_MASK));
  		return p+1;
  	}
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
978
979
  	if (++p == q)
  		return NULL;
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
980
981
982
983
984
985
986
987
  	io->nwin = (*p & 0x0f) + 1;
  	bsz = (*p & 0x30) >> 4;
  	if (bsz == 3)
  		bsz++;
  	lsz = (*p & 0xc0) >> 6;
  	if (lsz == 3)
  		lsz++;
  	p++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
988

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
989
990
991
992
993
994
995
996
997
998
999
1000
1001
  	for (i = 0; i < io->nwin; i++) {
  		io->win[i].base = 0;
  		io->win[i].len = 1;
  		for (j = 0; j < bsz; j++, p++) {
  			if (p == q)
  				return NULL;
  			io->win[i].base += *p << (j*8);
  		}
  		for (j = 0; j < lsz; j++, p++) {
  			if (p == q)
  				return NULL;
  			io->win[i].len += *p << (j*8);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1002
  	}
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1003
  	return p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1004
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1005
1006
1007
  
  static u_char *parse_mem(u_char *p, u_char *q, cistpl_mem_t *mem)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
  	int i, j, asz, lsz, has_ha;
  	u_int len, ca, ha;
  
  	if (p == q)
  		return NULL;
  
  	mem->nwin = (*p & 0x07) + 1;
  	lsz = (*p & 0x18) >> 3;
  	asz = (*p & 0x60) >> 5;
  	has_ha = (*p & 0x80);
  	if (++p == q)
  		return NULL;
  
  	for (i = 0; i < mem->nwin; i++) {
  		len = ca = ha = 0;
  		for (j = 0; j < lsz; j++, p++) {
  			if (p == q)
  				return NULL;
  			len += *p << (j*8);
  		}
  		for (j = 0; j < asz; j++, p++) {
  			if (p == q)
  				return NULL;
  			ca += *p << (j*8);
  		}
  		if (has_ha)
  			for (j = 0; j < asz; j++, p++) {
  				if (p == q)
  					return NULL;
  				ha += *p << (j*8);
  			}
  		mem->win[i].len = len << 8;
  		mem->win[i].card_addr = ca << 8;
  		mem->win[i].host_addr = ha << 8;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1042
  	}
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1043
  	return p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1044
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1045
1046
1047
  
  static u_char *parse_irq(u_char *p, u_char *q, cistpl_irq_t *irq)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1048
  	if (p == q)
3f9c5f4cb   Dominik Brodowski   pcmcia: deprecate...
1049
  		return NULL;
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1050
1051
1052
1053
1054
1055
1056
1057
  	irq->IRQInfo1 = *p; p++;
  	if (irq->IRQInfo1 & IRQ_INFO2_VALID) {
  		if (p+2 > q)
  			return NULL;
  		irq->IRQInfo2 = (p[1]<<8) + p[0];
  		p += 2;
  	}
  	return p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1058
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1059
1060
1061
1062
  
  static int parse_cftable_entry(tuple_t *tuple,
  			       cistpl_cftable_entry_t *entry)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1063
1064
1065
1066
1067
1068
  	u_char *p, *q, features;
  
  	p = tuple->TupleData;
  	q = p + tuple->TupleDataLen;
  	entry->index = *p & 0x3f;
  	entry->flags = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1069
  	if (*p & 0x40)
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
  		entry->flags |= CISTPL_CFTABLE_DEFAULT;
  	if (*p & 0x80) {
  		if (++p == q)
  			return -EINVAL;
  		if (*p & 0x10)
  			entry->flags |= CISTPL_CFTABLE_BVDS;
  		if (*p & 0x20)
  			entry->flags |= CISTPL_CFTABLE_WP;
  		if (*p & 0x40)
  			entry->flags |= CISTPL_CFTABLE_RDYBSY;
  		if (*p & 0x80)
  			entry->flags |= CISTPL_CFTABLE_MWAIT;
  		entry->interface = *p & 0x0f;
  	} else
  		entry->interface = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1085

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1086
1087
  	/* Process optional features */
  	if (++p == q)
3f9c5f4cb   Dominik Brodowski   pcmcia: deprecate...
1088
  		return -EINVAL;
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1089
  	features = *p; p++;
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
1090

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
  	/* Power options */
  	if ((features & 3) > 0) {
  		p = parse_power(p, q, &entry->vcc);
  		if (p == NULL)
  			return -EINVAL;
  	} else
  		entry->vcc.present = 0;
  	if ((features & 3) > 1) {
  		p = parse_power(p, q, &entry->vpp1);
  		if (p == NULL)
  			return -EINVAL;
  	} else
  		entry->vpp1.present = 0;
  	if ((features & 3) > 2) {
  		p = parse_power(p, q, &entry->vpp2);
  		if (p == NULL)
  			return -EINVAL;
  	} else
  		entry->vpp2.present = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1110

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
  	/* Timing options */
  	if (features & 0x04) {
  		p = parse_timing(p, q, &entry->timing);
  		if (p == NULL)
  			return -EINVAL;
  	} else {
  		entry->timing.wait = 0;
  		entry->timing.ready = 0;
  		entry->timing.reserved = 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1121

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
  	/* I/O window options */
  	if (features & 0x08) {
  		p = parse_io(p, q, &entry->io);
  		if (p == NULL)
  			return -EINVAL;
  	} else
  		entry->io.nwin = 0;
  
  	/* Interrupt options */
  	if (features & 0x10) {
  		p = parse_irq(p, q, &entry->irq);
  		if (p == NULL)
  			return -EINVAL;
  	} else
  		entry->irq.IRQInfo1 = 0;
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
1137

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
  	switch (features & 0x60) {
  	case 0x00:
  		entry->mem.nwin = 0;
  		break;
  	case 0x20:
  		entry->mem.nwin = 1;
  		entry->mem.win[0].len = get_unaligned_le16(p) << 8;
  		entry->mem.win[0].card_addr = 0;
  		entry->mem.win[0].host_addr = 0;
  		p += 2;
  		if (p > q)
  			return -EINVAL;
  		break;
  	case 0x40:
  		entry->mem.nwin = 1;
  		entry->mem.win[0].len = get_unaligned_le16(p) << 8;
  		entry->mem.win[0].card_addr = get_unaligned_le16(p + 2) << 8;
  		entry->mem.win[0].host_addr = 0;
  		p += 4;
  		if (p > q)
  			return -EINVAL;
  		break;
  	case 0x60:
  		p = parse_mem(p, q, &entry->mem);
  		if (p == NULL)
  			return -EINVAL;
  		break;
  	}
  
  	/* Misc features */
  	if (features & 0x80) {
  		if (p == q)
  			return -EINVAL;
  		entry->flags |= (*p << 8);
  		while (*p & 0x80)
  			if (++p == q)
  				return -EINVAL;
  		p++;
  	}
  
  	entry->subtuples = q-p;
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1181
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1182

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1183
1184
  static int parse_device_geo(tuple_t *tuple, cistpl_device_geo_t *geo)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1185
1186
  	u_char *p, *q;
  	int n;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1187

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1188
1189
  	p = (u_char *)tuple->TupleData;
  	q = p + tuple->TupleDataLen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1190

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
  	for (n = 0; n < CISTPL_MAX_DEVICES; n++) {
  		if (p > q-6)
  			break;
  		geo->geo[n].buswidth = p[0];
  		geo->geo[n].erase_block = 1 << (p[1]-1);
  		geo->geo[n].read_block  = 1 << (p[2]-1);
  		geo->geo[n].write_block = 1 << (p[3]-1);
  		geo->geo[n].partition   = 1 << (p[4]-1);
  		geo->geo[n].interleave  = 1 << (p[5]-1);
  		p += 6;
  	}
  	geo->ngeo = n;
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1204
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1205
1206
1207
  
  static int parse_vers_2(tuple_t *tuple, cistpl_vers_2_t *v2)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
  	u_char *p, *q;
  
  	if (tuple->TupleDataLen < 10)
  		return -EINVAL;
  
  	p = tuple->TupleData;
  	q = p + tuple->TupleDataLen;
  
  	v2->vers = p[0];
  	v2->comply = p[1];
  	v2->dindex = get_unaligned_le16(p + 2);
  	v2->vspec8 = p[6];
  	v2->vspec9 = p[7];
  	v2->nhdr = p[8];
  	p += 9;
  	return parse_strings(p, q, 2, v2->str, &v2->vendor, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1224
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1225
1226
1227
  
  static int parse_org(tuple_t *tuple, cistpl_org_t *org)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1228
1229
1230
1231
1232
1233
1234
1235
  	u_char *p, *q;
  	int i;
  
  	p = tuple->TupleData;
  	q = p + tuple->TupleDataLen;
  	if (p == q)
  		return -EINVAL;
  	org->data_org = *p;
3f9c5f4cb   Dominik Brodowski   pcmcia: deprecate...
1236
1237
  	if (++p == q)
  		return -EINVAL;
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1238
1239
1240
1241
1242
1243
1244
1245
  	for (i = 0; i < 30; i++) {
  		org->desc[i] = *p;
  		if (*p == '\0')
  			break;
  		if (++p == q)
  			return -EINVAL;
  	}
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1246
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1247
1248
1249
  
  static int parse_format(tuple_t *tuple, cistpl_format_t *fmt)
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1250
  	u_char *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1251

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1252
1253
  	if (tuple->TupleDataLen < 10)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1254

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1255
  	p = tuple->TupleData;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1256

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1257
1258
1259
1260
  	fmt->type = p[0];
  	fmt->edc = p[1];
  	fmt->offset = get_unaligned_le32(p + 2);
  	fmt->length = get_unaligned_le32(p + 6);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1261

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1262
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1263
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1264

2f3061eb1   Dominik Brodowski   pcmcia: remove un...
1265
  int pcmcia_parse_tuple(tuple_t *tuple, cisparse_t *parse)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1266
  {
6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
  	int ret = 0;
  
  	if (tuple->TupleDataLen > tuple->TupleDataMax)
  		return -EINVAL;
  	switch (tuple->TupleCode) {
  	case CISTPL_DEVICE:
  	case CISTPL_DEVICE_A:
  		ret = parse_device(tuple, &parse->device);
  		break;
  	case CISTPL_CHECKSUM:
  		ret = parse_checksum(tuple, &parse->checksum);
  		break;
  	case CISTPL_LONGLINK_A:
  	case CISTPL_LONGLINK_C:
  		ret = parse_longlink(tuple, &parse->longlink);
  		break;
  	case CISTPL_LONGLINK_MFC:
  		ret = parse_longlink_mfc(tuple, &parse->longlink_mfc);
  		break;
  	case CISTPL_VERS_1:
  		ret = parse_vers_1(tuple, &parse->version_1);
  		break;
  	case CISTPL_ALTSTR:
  		ret = parse_altstr(tuple, &parse->altstr);
  		break;
  	case CISTPL_JEDEC_A:
  	case CISTPL_JEDEC_C:
  		ret = parse_jedec(tuple, &parse->jedec);
  		break;
  	case CISTPL_MANFID:
  		ret = parse_manfid(tuple, &parse->manfid);
  		break;
  	case CISTPL_FUNCID:
  		ret = parse_funcid(tuple, &parse->funcid);
  		break;
  	case CISTPL_FUNCE:
  		ret = parse_funce(tuple, &parse->funce);
  		break;
  	case CISTPL_CONFIG:
  		ret = parse_config(tuple, &parse->config);
  		break;
  	case CISTPL_CFTABLE_ENTRY:
  		ret = parse_cftable_entry(tuple, &parse->cftable_entry);
  		break;
  	case CISTPL_DEVICE_GEO:
  	case CISTPL_DEVICE_GEO_A:
  		ret = parse_device_geo(tuple, &parse->device_geo);
  		break;
  	case CISTPL_VERS_2:
  		ret = parse_vers_2(tuple, &parse->vers_2);
  		break;
  	case CISTPL_ORG:
  		ret = parse_org(tuple, &parse->org);
  		break;
  	case CISTPL_FORMAT:
  	case CISTPL_FORMAT_A:
  		ret = parse_format(tuple, &parse->format);
  		break;
  	case CISTPL_NO_LINK:
  	case CISTPL_LINKTARGET:
  		ret = 0;
  		break;
  	default:
  		ret = -EINVAL;
  		break;
  	}
  	if (ret)
  		pr_debug("parse_tuple failed %d
  ", ret);
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1337
  }
2f3061eb1   Dominik Brodowski   pcmcia: remove un...
1338
  EXPORT_SYMBOL(pcmcia_parse_tuple);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1339

9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
1340

6e83ee075   Dominik Brodowski   pcmcia: CodingSty...
1341
  /**
f131ddc4b   Dominik Brodowski   pcmcia: cleanup p...
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
   * pccard_validate_cis() - check whether card has a sensible CIS
   * @s:		the struct pcmcia_socket we are to check
   * @info:	returns the number of tuples in the (valid) CIS, or 0
   *
   * This tries to determine if a card has a sensible CIS.  In @info, it
   * returns the number of tuples in the CIS, or 0 if the CIS looks bad. The
   * checks include making sure several critical tuples are present and
   * valid; seeing if the total number of tuples is reasonable; and
   * looking for tuples that use reserved codes.
   *
   * The function returns 0 on success.
   */
84897fc05   Dominik Brodowski   pcmcia: pccard_re...
1354
  int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1355
  {
f131ddc4b   Dominik Brodowski   pcmcia: cleanup p...
1356
1357
1358
1359
  	tuple_t *tuple;
  	cisparse_t *p;
  	unsigned int count = 0;
  	int ret, reserved, dev_ok = 0, ident_ok = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1360

f131ddc4b   Dominik Brodowski   pcmcia: cleanup p...
1361
1362
  	if (!s)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1363

8402641b2   Alan Cox   pcmcia: Fix requery
1364
  	if (s->functions || !(s->state & SOCKET_PRESENT)) {
a8408c17d   Dominik Brodowski   pcmcia: avoid lat...
1365
1366
1367
  		WARN_ON(1);
  		return -EINVAL;
  	}
f131ddc4b   Dominik Brodowski   pcmcia: cleanup p...
1368
  	/* We do not want to validate the CIS cache... */
8680c4b3f   Dominik Brodowski   pcmcia: also lock...
1369
  	mutex_lock(&s->ops_mutex);
f131ddc4b   Dominik Brodowski   pcmcia: cleanup p...
1370
  	destroy_cis_cache(s);
8680c4b3f   Dominik Brodowski   pcmcia: also lock...
1371
  	mutex_unlock(&s->ops_mutex);
904e37774   Dominik Brodowski   pcmcia: validate ...
1372

f131ddc4b   Dominik Brodowski   pcmcia: cleanup p...
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
  	tuple = kmalloc(sizeof(*tuple), GFP_KERNEL);
  	if (tuple == NULL) {
  		dev_warn(&s->dev, "no memory to validate CIS
  ");
  		return -ENOMEM;
  	}
  	p = kmalloc(sizeof(*p), GFP_KERNEL);
  	if (p == NULL) {
  		kfree(tuple);
  		dev_warn(&s->dev, "no memory to validate CIS
  ");
  		return -ENOMEM;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1386

f131ddc4b   Dominik Brodowski   pcmcia: cleanup p...
1387
1388
1389
1390
  	count = reserved = 0;
  	tuple->DesiredTuple = RETURN_FIRST_TUPLE;
  	tuple->Attributes = TUPLE_RETURN_COMMON;
  	ret = pccard_get_first_tuple(s, BIND_FN_ALL, tuple);
4c89e88bf   Dominik Brodowski   pcmcia: deprecate...
1391
  	if (ret != 0)
f131ddc4b   Dominik Brodowski   pcmcia: cleanup p...
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
  		goto done;
  
  	/* First tuple should be DEVICE; we should really have either that
  	   or a CFTABLE_ENTRY of some sort */
  	if ((tuple->TupleCode == CISTPL_DEVICE) ||
  	    (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY, p)) ||
  	    (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY_CB, p)))
  		dev_ok++;
  
  	/* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2
  	   tuple, for card identification.  Certain old D-Link and Linksys
  	   cards have only a broken VERS_2 tuple; hence the bogus test. */
  	if ((pccard_read_tuple(s, BIND_FN_ALL, CISTPL_MANFID, p) == 0) ||
  	    (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_1, p) == 0) ||
  	    (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_2, p) != -ENOSPC))
  		ident_ok++;
  
  	if (!dev_ok && !ident_ok)
  		goto done;
  
  	for (count = 1; count < MAX_TUPLES; count++) {
  		ret = pccard_get_next_tuple(s, BIND_FN_ALL, tuple);
  		if (ret != 0)
  			break;
  		if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) ||
  		    ((tuple->TupleCode > 0x47) && (tuple->TupleCode < 0x80)) ||
  		    ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff)))
  			reserved++;
  	}
  	if ((count == MAX_TUPLES) || (reserved > 5) ||
  		((!dev_ok || !ident_ok) && (count > 10)))
  		count = 0;
  
  	ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1426
1427
  
  done:
f131ddc4b   Dominik Brodowski   pcmcia: cleanup p...
1428
1429
  	/* invalidate CIS cache on failure */
  	if (!dev_ok || !ident_ok || !count) {
e8e68fd86   Dominik Brodowski   pcmcia: do not br...
1430
1431
1432
1433
1434
1435
1436
  		mutex_lock(&s->ops_mutex);
  		destroy_cis_cache(s);
  		mutex_unlock(&s->ops_mutex);
  		/* We differentiate between dev_ok, ident_ok and count
  		   failures to allow for an override for anonymous cards
  		   in ds.c */
  		if (!dev_ok || !ident_ok)
1c6c9b1d9   Alan Cox   pcmcia: handle an...
1437
  			ret = -EIO;
e8e68fd86   Dominik Brodowski   pcmcia: do not br...
1438
1439
  		else
  			ret = -EFAULT;
f131ddc4b   Dominik Brodowski   pcmcia: cleanup p...
1440
1441
1442
1443
1444
1445
1446
  	}
  
  	if (info)
  		*info = count;
  	kfree(tuple);
  	kfree(p);
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1447
  }
6e7b51a73   Dominik Brodowski   pcmcia: move cist...
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
  
  
  #define to_socket(_dev) container_of(_dev, struct pcmcia_socket, dev)
  
  static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf,
  				  loff_t off, size_t count)
  {
  	tuple_t tuple;
  	int status, i;
  	loff_t pointer = 0;
  	ssize_t ret = 0;
  	u_char *tuplebuffer;
  	u_char *tempbuffer;
  
  	tuplebuffer = kmalloc(sizeof(u_char) * 256, GFP_KERNEL);
  	if (!tuplebuffer)
  		return -ENOMEM;
  
  	tempbuffer = kmalloc(sizeof(u_char) * 258, GFP_KERNEL);
  	if (!tempbuffer) {
  		ret = -ENOMEM;
  		goto free_tuple;
  	}
  
  	memset(&tuple, 0, sizeof(tuple_t));
  
  	tuple.Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON;
  	tuple.DesiredTuple = RETURN_FIRST_TUPLE;
  	tuple.TupleOffset = 0;
  
  	status = pccard_get_first_tuple(s, BIND_FN_ALL, &tuple);
  	while (!status) {
  		tuple.TupleData = tuplebuffer;
  		tuple.TupleDataMax = 255;
  		memset(tuplebuffer, 0, sizeof(u_char) * 255);
  
  		status = pccard_get_tuple_data(s, &tuple);
  		if (status)
  			break;
  
  		if (off < (pointer + 2 + tuple.TupleDataLen)) {
  			tempbuffer[0] = tuple.TupleCode & 0xff;
  			tempbuffer[1] = tuple.TupleLink & 0xff;
  			for (i = 0; i < tuple.TupleDataLen; i++)
  				tempbuffer[i + 2] = tuplebuffer[i] & 0xff;
  
  			for (i = 0; i < (2 + tuple.TupleDataLen); i++) {
  				if (((i + pointer) >= off) &&
  				    (i + pointer) < (off + count)) {
  					buf[ret] = tempbuffer[i];
  					ret++;
  				}
  			}
  		}
  
  		pointer += 2 + tuple.TupleDataLen;
  
  		if (pointer >= (off + count))
  			break;
  
  		if (tuple.TupleCode == CISTPL_END)
  			break;
  		status = pccard_get_next_tuple(s, BIND_FN_ALL, &tuple);
  	}
  
  	kfree(tempbuffer);
   free_tuple:
  	kfree(tuplebuffer);
  
  	return ret;
  }
2c3c8bea6   Chris Wright   sysfs: add struct...
1519
  static ssize_t pccard_show_cis(struct file *filp, struct kobject *kobj,
6e7b51a73   Dominik Brodowski   pcmcia: move cist...
1520
1521
1522
1523
1524
1525
1526
1527
1528
  			       struct bin_attribute *bin_attr,
  			       char *buf, loff_t off, size_t count)
  {
  	unsigned int size = 0x200;
  
  	if (off >= size)
  		count = 0;
  	else {
  		struct pcmcia_socket *s;
a8408c17d   Dominik Brodowski   pcmcia: avoid lat...
1529
  		unsigned int chains = 1;
6e7b51a73   Dominik Brodowski   pcmcia: move cist...
1530
1531
1532
1533
1534
1535
1536
1537
  
  		if (off + count > size)
  			count = size - off;
  
  		s = to_socket(container_of(kobj, struct device, kobj));
  
  		if (!(s->state & SOCKET_PRESENT))
  			return -ENODEV;
a8408c17d   Dominik Brodowski   pcmcia: avoid lat...
1538
  		if (!s->functions && pccard_validate_cis(s, &chains))
6e7b51a73   Dominik Brodowski   pcmcia: move cist...
1539
1540
1541
1542
1543
1544
1545
1546
1547
  			return -EIO;
  		if (!chains)
  			return -ENODATA;
  
  		count = pccard_extract_cis(s, buf, off, count);
  	}
  
  	return count;
  }
2c3c8bea6   Chris Wright   sysfs: add struct...
1548
  static ssize_t pccard_store_cis(struct file *filp, struct kobject *kobj,
6e7b51a73   Dominik Brodowski   pcmcia: move cist...
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
  				struct bin_attribute *bin_attr,
  				char *buf, loff_t off, size_t count)
  {
  	struct pcmcia_socket *s;
  	int error;
  
  	s = to_socket(container_of(kobj, struct device, kobj));
  
  	if (off)
  		return -EINVAL;
  
  	if (count >= CISTPL_MAX_CIS_SIZE)
  		return -EINVAL;
  
  	if (!(s->state & SOCKET_PRESENT))
  		return -ENODEV;
  
  	error = pcmcia_replace_cis(s, buf, count);
  	if (error)
  		return -EIO;
af461fc18   Dominik Brodowski   pcmcia: delay re-...
1569
  	pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
6e7b51a73   Dominik Brodowski   pcmcia: move cist...
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
  
  	return count;
  }
  
  
  struct bin_attribute pccard_cis_attr = {
  	.attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR },
  	.size = 0x200,
  	.read = pccard_show_cis,
  	.write = pccard_store_cis,
  };