1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * cistpl.c -- 16-bit PCMCIA Card Information Structure parser
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * The initial developer of the original code is David A. Hinds
61da177e4SLinus Torvalds * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
71da177e4SLinus Torvalds * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
81da177e4SLinus Torvalds *
91da177e4SLinus Torvalds * (C) 1999 David A. Hinds
101da177e4SLinus Torvalds */
111da177e4SLinus Torvalds
121da177e4SLinus Torvalds #include <linux/module.h>
131da177e4SLinus Torvalds #include <linux/moduleparam.h>
141da177e4SLinus Torvalds #include <linux/kernel.h>
151da177e4SLinus Torvalds #include <linux/string.h>
161da177e4SLinus Torvalds #include <linux/major.h>
171da177e4SLinus Torvalds #include <linux/errno.h>
181da177e4SLinus Torvalds #include <linux/timer.h>
191da177e4SLinus Torvalds #include <linux/slab.h>
201da177e4SLinus Torvalds #include <linux/mm.h>
211da177e4SLinus Torvalds #include <linux/pci.h>
221da177e4SLinus Torvalds #include <linux/ioport.h>
239fea84f4SDominik Brodowski #include <linux/io.h>
243f19cad3SDavid Howells #include <linux/security.h>
251da177e4SLinus Torvalds #include <asm/byteorder.h>
26dc0cf6a2SDaniel Ritz #include <asm/unaligned.h>
271da177e4SLinus Torvalds
281da177e4SLinus Torvalds #include <pcmcia/ss.h>
291da177e4SLinus Torvalds #include <pcmcia/cisreg.h>
301da177e4SLinus Torvalds #include <pcmcia/cistpl.h>
31e469edbbSBen Dooks (Codethink) #include <pcmcia/ds.h>
321da177e4SLinus Torvalds #include "cs_internal.h"
331da177e4SLinus Torvalds
341da177e4SLinus Torvalds static const u_char mantissa[] = {
351da177e4SLinus Torvalds 10, 12, 13, 15, 20, 25, 30, 35,
361da177e4SLinus Torvalds 40, 45, 50, 55, 60, 70, 80, 90
371da177e4SLinus Torvalds };
381da177e4SLinus Torvalds
391da177e4SLinus Torvalds static const u_int exponent[] = {
401da177e4SLinus Torvalds 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
411da177e4SLinus Torvalds };
421da177e4SLinus Torvalds
431da177e4SLinus Torvalds /* Convert an extended speed byte to a time in nanoseconds */
441da177e4SLinus Torvalds #define SPEED_CVT(v) \
451da177e4SLinus Torvalds (mantissa[(((v)>>3)&15)-1] * exponent[(v)&7] / 10)
461da177e4SLinus Torvalds /* Convert a power byte to a current in 0.1 microamps */
471da177e4SLinus Torvalds #define POWER_CVT(v) \
481da177e4SLinus Torvalds (mantissa[((v)>>3)&15] * exponent[(v)&7] / 10)
491da177e4SLinus Torvalds #define POWER_SCALE(v) (exponent[(v)&7])
501da177e4SLinus Torvalds
511da177e4SLinus Torvalds /* Upper limit on reasonable # of tuples */
521da177e4SLinus Torvalds #define MAX_TUPLES 200
531da177e4SLinus Torvalds
54a3d0d4d8SDominik Brodowski /* Bits in IRQInfo1 field */
55a3d0d4d8SDominik Brodowski #define IRQ_INFO2_VALID 0x10
56a3d0d4d8SDominik Brodowski
5737f77955SPavel Machek /* 16-bit CIS? */
5837f77955SPavel Machek static int cis_width;
5937f77955SPavel Machek module_param(cis_width, int, 0444);
601da177e4SLinus Torvalds
release_cis_mem(struct pcmcia_socket * s)611da177e4SLinus Torvalds void release_cis_mem(struct pcmcia_socket *s)
621da177e4SLinus Torvalds {
636b8e087bSDominik Brodowski mutex_lock(&s->ops_mutex);
641da177e4SLinus Torvalds if (s->cis_mem.flags & MAP_ACTIVE) {
651da177e4SLinus Torvalds s->cis_mem.flags &= ~MAP_ACTIVE;
661da177e4SLinus Torvalds s->ops->set_mem_map(s, &s->cis_mem);
671da177e4SLinus Torvalds if (s->cis_mem.res) {
681da177e4SLinus Torvalds release_resource(s->cis_mem.res);
691da177e4SLinus Torvalds kfree(s->cis_mem.res);
701da177e4SLinus Torvalds s->cis_mem.res = NULL;
711da177e4SLinus Torvalds }
721da177e4SLinus Torvalds iounmap(s->cis_virt);
731da177e4SLinus Torvalds s->cis_virt = NULL;
741da177e4SLinus Torvalds }
756b8e087bSDominik Brodowski mutex_unlock(&s->ops_mutex);
761da177e4SLinus Torvalds }
771da177e4SLinus Torvalds
78*cc448bafSLee Jones /*
796e83ee07SDominik Brodowski * set_cis_map() - map the card memory at "card_offset" into virtual space.
806e83ee07SDominik Brodowski *
811da177e4SLinus Torvalds * If flags & MAP_ATTRIB, map the attribute space, otherwise
821da177e4SLinus Torvalds * map the memory space.
837ab24855SDominik Brodowski *
847ab24855SDominik Brodowski * Must be called with ops_mutex held.
851da177e4SLinus Torvalds */
set_cis_map(struct pcmcia_socket * s,unsigned int card_offset,unsigned int flags)866e83ee07SDominik Brodowski static void __iomem *set_cis_map(struct pcmcia_socket *s,
876e83ee07SDominik Brodowski unsigned int card_offset, unsigned int flags)
881da177e4SLinus Torvalds {
891da177e4SLinus Torvalds pccard_mem_map *mem = &s->cis_mem;
902ad0a0a7SDominik Brodowski int ret;
912ad0a0a7SDominik Brodowski
922e5a3e79SDominik Brodowski if (!(s->features & SS_CAP_STATIC_MAP) && (mem->res == NULL)) {
936e83ee07SDominik Brodowski mem->res = pcmcia_find_mem_region(0, s->map_size,
946e83ee07SDominik Brodowski s->map_size, 0, s);
951da177e4SLinus Torvalds if (mem->res == NULL) {
96f2e6cf76SJoe Perches dev_notice(&s->dev, "cs: unable to map card memory!\n");
971da177e4SLinus Torvalds return NULL;
981da177e4SLinus Torvalds }
992e5a3e79SDominik Brodowski s->cis_virt = NULL;
1001da177e4SLinus Torvalds }
1012e5a3e79SDominik Brodowski
1022e5a3e79SDominik Brodowski if (!(s->features & SS_CAP_STATIC_MAP) && (!s->cis_virt))
1032e5a3e79SDominik Brodowski s->cis_virt = ioremap(mem->res->start, s->map_size);
1042e5a3e79SDominik Brodowski
1051da177e4SLinus Torvalds mem->card_start = card_offset;
1061da177e4SLinus Torvalds mem->flags = flags;
1072e5a3e79SDominik Brodowski
1082ad0a0a7SDominik Brodowski ret = s->ops->set_mem_map(s, mem);
1092ad0a0a7SDominik Brodowski if (ret) {
1102ad0a0a7SDominik Brodowski iounmap(s->cis_virt);
1112e5a3e79SDominik Brodowski s->cis_virt = NULL;
1122ad0a0a7SDominik Brodowski return NULL;
1132ad0a0a7SDominik Brodowski }
1142ad0a0a7SDominik Brodowski
1151da177e4SLinus Torvalds if (s->features & SS_CAP_STATIC_MAP) {
1161da177e4SLinus Torvalds if (s->cis_virt)
1171da177e4SLinus Torvalds iounmap(s->cis_virt);
1181da177e4SLinus Torvalds s->cis_virt = ioremap(mem->static_start, s->map_size);
1191da177e4SLinus Torvalds }
1202e5a3e79SDominik Brodowski
1211da177e4SLinus Torvalds return s->cis_virt;
1221da177e4SLinus Torvalds }
1231da177e4SLinus Torvalds
1241da177e4SLinus Torvalds
1251da177e4SLinus Torvalds /* Bits in attr field */
1261da177e4SLinus Torvalds #define IS_ATTR 1
1271da177e4SLinus Torvalds #define IS_INDIRECT 8
1281da177e4SLinus Torvalds
129*cc448bafSLee Jones /*
1306e83ee07SDominik Brodowski * pcmcia_read_cis_mem() - low-level function to read CIS memory
131059f667dSDominik Brodowski *
132059f667dSDominik Brodowski * must be called with ops_mutex held
1336e83ee07SDominik Brodowski */
pcmcia_read_cis_mem(struct pcmcia_socket * s,int attr,u_int addr,u_int len,void * ptr)134e6ea0b9eSDominik Brodowski int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
1351da177e4SLinus Torvalds u_int len, void *ptr)
1361da177e4SLinus Torvalds {
1371da177e4SLinus Torvalds void __iomem *sys, *end;
1381da177e4SLinus Torvalds unsigned char *buf = ptr;
1391da177e4SLinus Torvalds
140d50dbec3SDominik Brodowski dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
1411da177e4SLinus Torvalds
1421da177e4SLinus Torvalds if (attr & IS_INDIRECT) {
1431da177e4SLinus Torvalds /* Indirect accesses use a bunch of special registers at fixed
1441da177e4SLinus Torvalds locations in common memory */
1451da177e4SLinus Torvalds u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
1461da177e4SLinus Torvalds if (attr & IS_ATTR) {
1471da177e4SLinus Torvalds addr *= 2;
1481da177e4SLinus Torvalds flags = ICTRL0_AUTOINC;
1491da177e4SLinus Torvalds }
1501da177e4SLinus Torvalds
1516e83ee07SDominik Brodowski sys = set_cis_map(s, 0, MAP_ACTIVE |
1526e83ee07SDominik Brodowski ((cis_width) ? MAP_16BIT : 0));
1531da177e4SLinus Torvalds if (!sys) {
1547ab24855SDominik Brodowski dev_dbg(&s->dev, "could not map memory\n");
1551da177e4SLinus Torvalds memset(ptr, 0xff, len);
1561da177e4SLinus Torvalds return -1;
1571da177e4SLinus Torvalds }
1581da177e4SLinus Torvalds
1591da177e4SLinus Torvalds writeb(flags, sys+CISREG_ICTRL0);
1601da177e4SLinus Torvalds writeb(addr & 0xff, sys+CISREG_IADDR0);
1611da177e4SLinus Torvalds writeb((addr>>8) & 0xff, sys+CISREG_IADDR1);
1621da177e4SLinus Torvalds writeb((addr>>16) & 0xff, sys+CISREG_IADDR2);
1631da177e4SLinus Torvalds writeb((addr>>24) & 0xff, sys+CISREG_IADDR3);
1641da177e4SLinus Torvalds for ( ; len > 0; len--, buf++)
1651da177e4SLinus Torvalds *buf = readb(sys+CISREG_IDATA0);
1661da177e4SLinus Torvalds } else {
1671da177e4SLinus Torvalds u_int inc = 1, card_offset, flags;
1681da177e4SLinus Torvalds
169b38a4bd3SAlan Cox if (addr > CISTPL_MAX_CIS_SIZE) {
1706e83ee07SDominik Brodowski dev_dbg(&s->dev,
1716e83ee07SDominik Brodowski "attempt to read CIS mem at addr %#x", addr);
172b38a4bd3SAlan Cox memset(ptr, 0xff, len);
173b38a4bd3SAlan Cox return -1;
174b38a4bd3SAlan Cox }
1757ab24855SDominik Brodowski
1761da177e4SLinus Torvalds flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
1771da177e4SLinus Torvalds if (attr) {
1781da177e4SLinus Torvalds flags |= MAP_ATTRIB;
1791da177e4SLinus Torvalds inc++;
1801da177e4SLinus Torvalds addr *= 2;
1811da177e4SLinus Torvalds }
1821da177e4SLinus Torvalds
1831da177e4SLinus Torvalds card_offset = addr & ~(s->map_size-1);
1841da177e4SLinus Torvalds while (len) {
1851da177e4SLinus Torvalds sys = set_cis_map(s, card_offset, flags);
1861da177e4SLinus Torvalds if (!sys) {
1877ab24855SDominik Brodowski dev_dbg(&s->dev, "could not map memory\n");
1881da177e4SLinus Torvalds memset(ptr, 0xff, len);
1891da177e4SLinus Torvalds return -1;
1901da177e4SLinus Torvalds }
1911da177e4SLinus Torvalds end = sys + s->map_size;
1921da177e4SLinus Torvalds sys = sys + (addr & (s->map_size-1));
1931da177e4SLinus Torvalds for ( ; len > 0; len--, buf++, sys += inc) {
1941da177e4SLinus Torvalds if (sys == end)
1951da177e4SLinus Torvalds break;
1961da177e4SLinus Torvalds *buf = readb(sys);
1971da177e4SLinus Torvalds }
1981da177e4SLinus Torvalds card_offset += s->map_size;
1991da177e4SLinus Torvalds addr = 0;
2001da177e4SLinus Torvalds }
2011da177e4SLinus Torvalds }
202d50dbec3SDominik Brodowski dev_dbg(&s->dev, " %#2.2x %#2.2x %#2.2x %#2.2x ...\n",
2031da177e4SLinus Torvalds *(u_char *)(ptr+0), *(u_char *)(ptr+1),
2041da177e4SLinus Torvalds *(u_char *)(ptr+2), *(u_char *)(ptr+3));
2051da177e4SLinus Torvalds return 0;
2061da177e4SLinus Torvalds }
2071a8d4663SDominik Brodowski
2081da177e4SLinus Torvalds
209*cc448bafSLee Jones /*
2106e83ee07SDominik Brodowski * pcmcia_write_cis_mem() - low-level function to write CIS memory
2116e83ee07SDominik Brodowski *
212059f667dSDominik Brodowski * Probably only useful for writing one-byte registers. Must be called
213059f667dSDominik Brodowski * with ops_mutex held.
2146e83ee07SDominik Brodowski */
pcmcia_write_cis_mem(struct pcmcia_socket * s,int attr,u_int addr,u_int len,void * ptr)2151d5cc192SDominik Brodowski int pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
2161da177e4SLinus Torvalds u_int len, void *ptr)
2171da177e4SLinus Torvalds {
2181da177e4SLinus Torvalds void __iomem *sys, *end;
2191da177e4SLinus Torvalds unsigned char *buf = ptr;
2201da177e4SLinus Torvalds
2216e83ee07SDominik Brodowski dev_dbg(&s->dev,
2226e83ee07SDominik Brodowski "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
2231da177e4SLinus Torvalds
2241da177e4SLinus Torvalds if (attr & IS_INDIRECT) {
2251da177e4SLinus Torvalds /* Indirect accesses use a bunch of special registers at fixed
2261da177e4SLinus Torvalds locations in common memory */
2271da177e4SLinus Torvalds u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
2281da177e4SLinus Torvalds if (attr & IS_ATTR) {
2291da177e4SLinus Torvalds addr *= 2;
2301da177e4SLinus Torvalds flags = ICTRL0_AUTOINC;
2311da177e4SLinus Torvalds }
2321da177e4SLinus Torvalds
2336e83ee07SDominik Brodowski sys = set_cis_map(s, 0, MAP_ACTIVE |
2346e83ee07SDominik Brodowski ((cis_width) ? MAP_16BIT : 0));
2357ab24855SDominik Brodowski if (!sys) {
2367ab24855SDominik Brodowski dev_dbg(&s->dev, "could not map memory\n");
2371d5cc192SDominik Brodowski return -EINVAL;
2387ab24855SDominik Brodowski }
2391da177e4SLinus Torvalds
2401da177e4SLinus Torvalds writeb(flags, sys+CISREG_ICTRL0);
2411da177e4SLinus Torvalds writeb(addr & 0xff, sys+CISREG_IADDR0);
2421da177e4SLinus Torvalds writeb((addr>>8) & 0xff, sys+CISREG_IADDR1);
2431da177e4SLinus Torvalds writeb((addr>>16) & 0xff, sys+CISREG_IADDR2);
2441da177e4SLinus Torvalds writeb((addr>>24) & 0xff, sys+CISREG_IADDR3);
2451da177e4SLinus Torvalds for ( ; len > 0; len--, buf++)
2461da177e4SLinus Torvalds writeb(*buf, sys+CISREG_IDATA0);
2471da177e4SLinus Torvalds } else {
2481da177e4SLinus Torvalds u_int inc = 1, card_offset, flags;
2491da177e4SLinus Torvalds
2501da177e4SLinus Torvalds flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
2511da177e4SLinus Torvalds if (attr & IS_ATTR) {
2521da177e4SLinus Torvalds flags |= MAP_ATTRIB;
2531da177e4SLinus Torvalds inc++;
2541da177e4SLinus Torvalds addr *= 2;
2551da177e4SLinus Torvalds }
2561da177e4SLinus Torvalds
2571da177e4SLinus Torvalds card_offset = addr & ~(s->map_size-1);
2581da177e4SLinus Torvalds while (len) {
2591da177e4SLinus Torvalds sys = set_cis_map(s, card_offset, flags);
2607ab24855SDominik Brodowski if (!sys) {
2617ab24855SDominik Brodowski dev_dbg(&s->dev, "could not map memory\n");
2621d5cc192SDominik Brodowski return -EINVAL;
2637ab24855SDominik Brodowski }
2641da177e4SLinus Torvalds
2651da177e4SLinus Torvalds end = sys + s->map_size;
2661da177e4SLinus Torvalds sys = sys + (addr & (s->map_size-1));
2671da177e4SLinus Torvalds for ( ; len > 0; len--, buf++, sys += inc) {
2681da177e4SLinus Torvalds if (sys == end)
2691da177e4SLinus Torvalds break;
2701da177e4SLinus Torvalds writeb(*buf, sys);
2711da177e4SLinus Torvalds }
2721da177e4SLinus Torvalds card_offset += s->map_size;
2731da177e4SLinus Torvalds addr = 0;
2741da177e4SLinus Torvalds }
2751da177e4SLinus Torvalds }
2761d5cc192SDominik Brodowski return 0;
2771da177e4SLinus Torvalds }
2781a8d4663SDominik Brodowski
2791da177e4SLinus Torvalds
280*cc448bafSLee Jones /*
2816e83ee07SDominik Brodowski * read_cis_cache() - read CIS memory or its associated cache
2826e83ee07SDominik Brodowski *
2836e83ee07SDominik Brodowski * This is a wrapper around read_cis_mem, with the same interface,
2846e83ee07SDominik Brodowski * but which caches information, for cards whose CIS may not be
2856e83ee07SDominik Brodowski * readable all the time.
2866e83ee07SDominik Brodowski */
read_cis_cache(struct pcmcia_socket * s,int attr,u_int addr,size_t len,void * ptr)287d700518aSDominik Brodowski static int read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
28853efec95SDominik Brodowski size_t len, void *ptr)
2891da177e4SLinus Torvalds {
2901da177e4SLinus Torvalds struct cis_cache_entry *cis;
291d700518aSDominik Brodowski int ret = 0;
2921da177e4SLinus Torvalds
29357197b9bSDominik Brodowski if (s->state & SOCKET_CARDBUS)
294d700518aSDominik Brodowski return -EINVAL;
29557197b9bSDominik Brodowski
2968680c4b3SDominik Brodowski mutex_lock(&s->ops_mutex);
2971da177e4SLinus Torvalds if (s->fake_cis) {
29853efec95SDominik Brodowski if (s->fake_cis_len >= addr+len)
2991da177e4SLinus Torvalds memcpy(ptr, s->fake_cis+addr, len);
300d700518aSDominik Brodowski else {
3011da177e4SLinus Torvalds memset(ptr, 0xff, len);
302d700518aSDominik Brodowski ret = -EINVAL;
303d700518aSDominik Brodowski }
3048680c4b3SDominik Brodowski mutex_unlock(&s->ops_mutex);
305d700518aSDominik Brodowski return ret;
3061da177e4SLinus Torvalds }
3071da177e4SLinus Torvalds
3081da177e4SLinus Torvalds list_for_each_entry(cis, &s->cis_cache, node) {
3091da177e4SLinus Torvalds if (cis->addr == addr && cis->len == len && cis->attr == attr) {
3101da177e4SLinus Torvalds memcpy(ptr, cis->cache, len);
3118680c4b3SDominik Brodowski mutex_unlock(&s->ops_mutex);
312d700518aSDominik Brodowski return 0;
3131da177e4SLinus Torvalds }
3141da177e4SLinus Torvalds }
3151da177e4SLinus Torvalds
316e6ea0b9eSDominik Brodowski ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr);
3171da177e4SLinus Torvalds
3181da177e4SLinus Torvalds if (ret == 0) {
3191da177e4SLinus Torvalds /* Copy data into the cache */
3201da177e4SLinus Torvalds cis = kmalloc(sizeof(struct cis_cache_entry) + len, GFP_KERNEL);
3211da177e4SLinus Torvalds if (cis) {
3221da177e4SLinus Torvalds cis->addr = addr;
3231da177e4SLinus Torvalds cis->len = len;
3241da177e4SLinus Torvalds cis->attr = attr;
3251da177e4SLinus Torvalds memcpy(cis->cache, ptr, len);
3261da177e4SLinus Torvalds list_add(&cis->node, &s->cis_cache);
327059f667dSDominik Brodowski }
328059f667dSDominik Brodowski }
3298680c4b3SDominik Brodowski mutex_unlock(&s->ops_mutex);
330059f667dSDominik Brodowski
331d700518aSDominik Brodowski return ret;
3321da177e4SLinus Torvalds }
3331da177e4SLinus Torvalds
3341da177e4SLinus Torvalds static void
remove_cis_cache(struct pcmcia_socket * s,int attr,u_int addr,u_int len)3351da177e4SLinus Torvalds remove_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len)
3361da177e4SLinus Torvalds {
3371da177e4SLinus Torvalds struct cis_cache_entry *cis;
3381da177e4SLinus Torvalds
3398680c4b3SDominik Brodowski mutex_lock(&s->ops_mutex);
3401da177e4SLinus Torvalds list_for_each_entry(cis, &s->cis_cache, node)
3411da177e4SLinus Torvalds if (cis->addr == addr && cis->len == len && cis->attr == attr) {
3421da177e4SLinus Torvalds list_del(&cis->node);
3431da177e4SLinus Torvalds kfree(cis);
3441da177e4SLinus Torvalds break;
3451da177e4SLinus Torvalds }
3468680c4b3SDominik Brodowski mutex_unlock(&s->ops_mutex);
3471da177e4SLinus Torvalds }
3481da177e4SLinus Torvalds
349904e3777SDominik Brodowski /**
350904e3777SDominik Brodowski * destroy_cis_cache() - destroy the CIS cache
351904e3777SDominik Brodowski * @s: pcmcia_socket for which CIS cache shall be destroyed
352904e3777SDominik Brodowski *
3538680c4b3SDominik Brodowski * This destroys the CIS cache but keeps any fake CIS alive. Must be
3548680c4b3SDominik Brodowski * called with ops_mutex held.
355904e3777SDominik Brodowski */
destroy_cis_cache(struct pcmcia_socket * s)3561da177e4SLinus Torvalds void destroy_cis_cache(struct pcmcia_socket *s)
3571da177e4SLinus Torvalds {
3581da177e4SLinus Torvalds struct list_head *l, *n;
359904e3777SDominik Brodowski struct cis_cache_entry *cis;
3601da177e4SLinus Torvalds
3611da177e4SLinus Torvalds list_for_each_safe(l, n, &s->cis_cache) {
362904e3777SDominik Brodowski cis = list_entry(l, struct cis_cache_entry, node);
3631da177e4SLinus Torvalds list_del(&cis->node);
3641da177e4SLinus Torvalds kfree(cis);
3651da177e4SLinus Torvalds }
3661da177e4SLinus Torvalds }
3671da177e4SLinus Torvalds
368*cc448bafSLee Jones /*
3696e83ee07SDominik Brodowski * verify_cis_cache() - does the CIS match what is in the CIS cache?
3706e83ee07SDominik Brodowski */
verify_cis_cache(struct pcmcia_socket * s)3711da177e4SLinus Torvalds int verify_cis_cache(struct pcmcia_socket *s)
3721da177e4SLinus Torvalds {
3731da177e4SLinus Torvalds struct cis_cache_entry *cis;
3741da177e4SLinus Torvalds char *buf;
375d700518aSDominik Brodowski int ret;
3761da177e4SLinus Torvalds
37757197b9bSDominik Brodowski if (s->state & SOCKET_CARDBUS)
37857197b9bSDominik Brodowski return -EINVAL;
37957197b9bSDominik Brodowski
3801da177e4SLinus Torvalds buf = kmalloc(256, GFP_KERNEL);
381e689597fSDominik Brodowski if (buf == NULL) {
382f2e6cf76SJoe Perches dev_warn(&s->dev, "no memory for verifying CIS\n");
3831168386aSDominik Brodowski return -ENOMEM;
384e689597fSDominik Brodowski }
385059f667dSDominik Brodowski mutex_lock(&s->ops_mutex);
3861da177e4SLinus Torvalds list_for_each_entry(cis, &s->cis_cache, node) {
3871da177e4SLinus Torvalds int len = cis->len;
3881da177e4SLinus Torvalds
3891da177e4SLinus Torvalds if (len > 256)
3901da177e4SLinus Torvalds len = 256;
39157197b9bSDominik Brodowski
392d700518aSDominik Brodowski ret = pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf);
393d700518aSDominik Brodowski if (ret || memcmp(buf, cis->cache, len) != 0) {
3941da177e4SLinus Torvalds kfree(buf);
395059f667dSDominik Brodowski mutex_unlock(&s->ops_mutex);
3961da177e4SLinus Torvalds return -1;
3971da177e4SLinus Torvalds }
3981da177e4SLinus Torvalds }
3991da177e4SLinus Torvalds kfree(buf);
400059f667dSDominik Brodowski mutex_unlock(&s->ops_mutex);
4011da177e4SLinus Torvalds return 0;
4021da177e4SLinus Torvalds }
4031da177e4SLinus Torvalds
404*cc448bafSLee Jones /*
4056e83ee07SDominik Brodowski * pcmcia_replace_cis() - use a replacement CIS instead of the card's CIS
4066e83ee07SDominik Brodowski *
4076e83ee07SDominik Brodowski * For really bad cards, we provide a facility for uploading a
4086e83ee07SDominik Brodowski * replacement CIS.
4096e83ee07SDominik Brodowski */
pcmcia_replace_cis(struct pcmcia_socket * s,const u8 * data,const size_t len)41053efec95SDominik Brodowski int pcmcia_replace_cis(struct pcmcia_socket *s,
41153efec95SDominik Brodowski const u8 *data, const size_t len)
4121da177e4SLinus Torvalds {
4131168386aSDominik Brodowski if (len > CISTPL_MAX_CIS_SIZE) {
414f2e6cf76SJoe Perches dev_warn(&s->dev, "replacement CIS too big\n");
4151168386aSDominik Brodowski return -EINVAL;
4161168386aSDominik Brodowski }
4178680c4b3SDominik Brodowski mutex_lock(&s->ops_mutex);
41853efec95SDominik Brodowski kfree(s->fake_cis);
41953efec95SDominik Brodowski s->fake_cis = kmalloc(len, GFP_KERNEL);
4201168386aSDominik Brodowski if (s->fake_cis == NULL) {
421f2e6cf76SJoe Perches dev_warn(&s->dev, "no memory to replace CIS\n");
4228680c4b3SDominik Brodowski mutex_unlock(&s->ops_mutex);
4231168386aSDominik Brodowski return -ENOMEM;
4241168386aSDominik Brodowski }
42553efec95SDominik Brodowski s->fake_cis_len = len;
42653efec95SDominik Brodowski memcpy(s->fake_cis, data, len);
427d700518aSDominik Brodowski dev_info(&s->dev, "Using replacement CIS\n");
4288680c4b3SDominik Brodowski mutex_unlock(&s->ops_mutex);
4294c89e88bSDominik Brodowski return 0;
4301da177e4SLinus Torvalds }
4311da177e4SLinus Torvalds
4326e83ee07SDominik Brodowski /* The high-level CIS tuple services */
4331da177e4SLinus Torvalds
434820dc846SHimangi Saraogi struct tuple_flags {
4351da177e4SLinus Torvalds u_int link_space:4;
4361da177e4SLinus Torvalds u_int has_link:1;
4371da177e4SLinus Torvalds u_int mfc_fn:3;
4381da177e4SLinus Torvalds u_int space:4;
439820dc846SHimangi Saraogi };
4401da177e4SLinus Torvalds
441820dc846SHimangi Saraogi #define LINK_SPACE(f) (((struct tuple_flags *)(&(f)))->link_space)
442820dc846SHimangi Saraogi #define HAS_LINK(f) (((struct tuple_flags *)(&(f)))->has_link)
443820dc846SHimangi Saraogi #define MFC_FN(f) (((struct tuple_flags *)(&(f)))->mfc_fn)
444820dc846SHimangi Saraogi #define SPACE(f) (((struct tuple_flags *)(&(f)))->space)
4451da177e4SLinus Torvalds
pccard_get_first_tuple(struct pcmcia_socket * s,unsigned int function,tuple_t * tuple)4466e83ee07SDominik Brodowski int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function,
4476e83ee07SDominik Brodowski tuple_t *tuple)
4481da177e4SLinus Torvalds {
4491da177e4SLinus Torvalds if (!s)
450ffb8da20SDominik Brodowski return -EINVAL;
45157197b9bSDominik Brodowski
45257197b9bSDominik Brodowski if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS))
4533939c1efSDominik Brodowski return -ENODEV;
4541da177e4SLinus Torvalds tuple->TupleLink = tuple->Flags = 0;
45557197b9bSDominik Brodowski
4561da177e4SLinus Torvalds /* Assume presence of a LONGLINK_C to address 0 */
4571da177e4SLinus Torvalds tuple->CISOffset = tuple->LinkOffset = 0;
4581da177e4SLinus Torvalds SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1;
45957197b9bSDominik Brodowski
46057197b9bSDominik Brodowski if ((s->functions > 1) && !(tuple->Attributes & TUPLE_RETURN_COMMON)) {
4611da177e4SLinus Torvalds cisdata_t req = tuple->DesiredTuple;
4621da177e4SLinus Torvalds tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
4634c89e88bSDominik Brodowski if (pccard_get_next_tuple(s, function, tuple) == 0) {
4641da177e4SLinus Torvalds tuple->DesiredTuple = CISTPL_LINKTARGET;
4654c89e88bSDominik Brodowski if (pccard_get_next_tuple(s, function, tuple) != 0)
466635d19beSDominik Brodowski return -ENOSPC;
4671da177e4SLinus Torvalds } else
4681da177e4SLinus Torvalds tuple->CISOffset = tuple->TupleLink = 0;
4691da177e4SLinus Torvalds tuple->DesiredTuple = req;
4701da177e4SLinus Torvalds }
4711da177e4SLinus Torvalds return pccard_get_next_tuple(s, function, tuple);
4721da177e4SLinus Torvalds }
4731da177e4SLinus Torvalds
follow_link(struct pcmcia_socket * s,tuple_t * tuple)4741da177e4SLinus Torvalds static int follow_link(struct pcmcia_socket *s, tuple_t *tuple)
4751da177e4SLinus Torvalds {
4761da177e4SLinus Torvalds u_char link[5];
4771da177e4SLinus Torvalds u_int ofs;
478d700518aSDominik Brodowski int ret;
4791da177e4SLinus Torvalds
4801da177e4SLinus Torvalds if (MFC_FN(tuple->Flags)) {
4811da177e4SLinus Torvalds /* Get indirect link from the MFC tuple */
482d700518aSDominik Brodowski ret = read_cis_cache(s, LINK_SPACE(tuple->Flags),
4831da177e4SLinus Torvalds tuple->LinkOffset, 5, link);
484d700518aSDominik Brodowski if (ret)
485d700518aSDominik Brodowski return -1;
4866b1e6f63SHarvey Harrison ofs = get_unaligned_le32(link + 1);
4871da177e4SLinus Torvalds SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR);
4881da177e4SLinus Torvalds /* Move to the next indirect link */
4891da177e4SLinus Torvalds tuple->LinkOffset += 5;
4901da177e4SLinus Torvalds MFC_FN(tuple->Flags)--;
4911da177e4SLinus Torvalds } else if (HAS_LINK(tuple->Flags)) {
4921da177e4SLinus Torvalds ofs = tuple->LinkOffset;
4931da177e4SLinus Torvalds SPACE(tuple->Flags) = LINK_SPACE(tuple->Flags);
4941da177e4SLinus Torvalds HAS_LINK(tuple->Flags) = 0;
4956e83ee07SDominik Brodowski } else
4961da177e4SLinus Torvalds return -1;
4976e83ee07SDominik Brodowski
49857197b9bSDominik Brodowski if (SPACE(tuple->Flags)) {
4991da177e4SLinus Torvalds /* This is ugly, but a common CIS error is to code the long
5001da177e4SLinus Torvalds link offset incorrectly, so we check the right spot... */
501d700518aSDominik Brodowski ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
502d700518aSDominik Brodowski if (ret)
503d700518aSDominik Brodowski return -1;
5041da177e4SLinus Torvalds if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
5051da177e4SLinus Torvalds (strncmp(link+2, "CIS", 3) == 0))
5061da177e4SLinus Torvalds return ofs;
5071da177e4SLinus Torvalds remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5);
5081da177e4SLinus Torvalds /* Then, we try the wrong spot... */
5091da177e4SLinus Torvalds ofs = ofs >> 1;
5101da177e4SLinus Torvalds }
511d700518aSDominik Brodowski ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
512d700518aSDominik Brodowski if (ret)
513d700518aSDominik Brodowski return -1;
5141da177e4SLinus Torvalds if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
5151da177e4SLinus Torvalds (strncmp(link+2, "CIS", 3) == 0))
5161da177e4SLinus Torvalds return ofs;
5171da177e4SLinus Torvalds remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5);
5181da177e4SLinus Torvalds return -1;
5191da177e4SLinus Torvalds }
5201da177e4SLinus Torvalds
pccard_get_next_tuple(struct pcmcia_socket * s,unsigned int function,tuple_t * tuple)5216e83ee07SDominik Brodowski int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function,
5226e83ee07SDominik Brodowski tuple_t *tuple)
5231da177e4SLinus Torvalds {
5241da177e4SLinus Torvalds u_char link[2], tmp;
5251da177e4SLinus Torvalds int ofs, i, attr;
526d700518aSDominik Brodowski int ret;
5271da177e4SLinus Torvalds
5281da177e4SLinus Torvalds if (!s)
529ffb8da20SDominik Brodowski return -EINVAL;
53057197b9bSDominik Brodowski if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS))
5313939c1efSDominik Brodowski return -ENODEV;
5321da177e4SLinus Torvalds
5331da177e4SLinus Torvalds link[1] = tuple->TupleLink;
5341da177e4SLinus Torvalds ofs = tuple->CISOffset + tuple->TupleLink;
5351da177e4SLinus Torvalds attr = SPACE(tuple->Flags);
5361da177e4SLinus Torvalds
5371da177e4SLinus Torvalds for (i = 0; i < MAX_TUPLES; i++) {
5386e83ee07SDominik Brodowski if (link[1] == 0xff)
5391da177e4SLinus Torvalds link[0] = CISTPL_END;
5406e83ee07SDominik Brodowski else {
541d700518aSDominik Brodowski ret = read_cis_cache(s, attr, ofs, 2, link);
542d700518aSDominik Brodowski if (ret)
543d700518aSDominik Brodowski return -1;
5441da177e4SLinus Torvalds if (link[0] == CISTPL_NULL) {
5456e83ee07SDominik Brodowski ofs++;
5466e83ee07SDominik Brodowski continue;
5471da177e4SLinus Torvalds }
5481da177e4SLinus Torvalds }
5491da177e4SLinus Torvalds
5501da177e4SLinus Torvalds /* End of chain? Follow long link if possible */
5511da177e4SLinus Torvalds if (link[0] == CISTPL_END) {
5529fea84f4SDominik Brodowski ofs = follow_link(s, tuple);
5539fea84f4SDominik Brodowski if (ofs < 0)
554635d19beSDominik Brodowski return -ENOSPC;
5551da177e4SLinus Torvalds attr = SPACE(tuple->Flags);
556d700518aSDominik Brodowski ret = read_cis_cache(s, attr, ofs, 2, link);
557d700518aSDominik Brodowski if (ret)
558d700518aSDominik Brodowski return -1;
5591da177e4SLinus Torvalds }
5601da177e4SLinus Torvalds
5611da177e4SLinus Torvalds /* Is this a link tuple? Make a note of it */
5621da177e4SLinus Torvalds if ((link[0] == CISTPL_LONGLINK_A) ||
5631da177e4SLinus Torvalds (link[0] == CISTPL_LONGLINK_C) ||
5641da177e4SLinus Torvalds (link[0] == CISTPL_LONGLINK_MFC) ||
5651da177e4SLinus Torvalds (link[0] == CISTPL_LINKTARGET) ||
5661da177e4SLinus Torvalds (link[0] == CISTPL_INDIRECT) ||
5671da177e4SLinus Torvalds (link[0] == CISTPL_NO_LINK)) {
5681da177e4SLinus Torvalds switch (link[0]) {
5691da177e4SLinus Torvalds case CISTPL_LONGLINK_A:
5701da177e4SLinus Torvalds HAS_LINK(tuple->Flags) = 1;
5711da177e4SLinus Torvalds LINK_SPACE(tuple->Flags) = attr | IS_ATTR;
5726e83ee07SDominik Brodowski ret = read_cis_cache(s, attr, ofs+2, 4,
5736e83ee07SDominik Brodowski &tuple->LinkOffset);
574d700518aSDominik Brodowski if (ret)
575d700518aSDominik Brodowski return -1;
5761da177e4SLinus Torvalds break;
5771da177e4SLinus Torvalds case CISTPL_LONGLINK_C:
5781da177e4SLinus Torvalds HAS_LINK(tuple->Flags) = 1;
5791da177e4SLinus Torvalds LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR;
5806e83ee07SDominik Brodowski ret = read_cis_cache(s, attr, ofs+2, 4,
5816e83ee07SDominik Brodowski &tuple->LinkOffset);
582d700518aSDominik Brodowski if (ret)
583d700518aSDominik Brodowski return -1;
5841da177e4SLinus Torvalds break;
5851da177e4SLinus Torvalds case CISTPL_INDIRECT:
5861da177e4SLinus Torvalds HAS_LINK(tuple->Flags) = 1;
5876e83ee07SDominik Brodowski LINK_SPACE(tuple->Flags) = IS_ATTR |
5886e83ee07SDominik Brodowski IS_INDIRECT;
5891da177e4SLinus Torvalds tuple->LinkOffset = 0;
5901da177e4SLinus Torvalds break;
5911da177e4SLinus Torvalds case CISTPL_LONGLINK_MFC:
5921da177e4SLinus Torvalds tuple->LinkOffset = ofs + 3;
5931da177e4SLinus Torvalds LINK_SPACE(tuple->Flags) = attr;
5941da177e4SLinus Torvalds if (function == BIND_FN_ALL) {
5951da177e4SLinus Torvalds /* Follow all the MFC links */
5966e83ee07SDominik Brodowski ret = read_cis_cache(s, attr, ofs+2,
5976e83ee07SDominik Brodowski 1, &tmp);
598d700518aSDominik Brodowski if (ret)
599d700518aSDominik Brodowski return -1;
6001da177e4SLinus Torvalds MFC_FN(tuple->Flags) = tmp;
6011da177e4SLinus Torvalds } else {
6021da177e4SLinus Torvalds /* Follow exactly one of the links */
6031da177e4SLinus Torvalds MFC_FN(tuple->Flags) = 1;
6041da177e4SLinus Torvalds tuple->LinkOffset += function * 5;
6051da177e4SLinus Torvalds }
6061da177e4SLinus Torvalds break;
6071da177e4SLinus Torvalds case CISTPL_NO_LINK:
6081da177e4SLinus Torvalds HAS_LINK(tuple->Flags) = 0;
6091da177e4SLinus Torvalds break;
6101da177e4SLinus Torvalds }
6111da177e4SLinus Torvalds if ((tuple->Attributes & TUPLE_RETURN_LINK) &&
6121da177e4SLinus Torvalds (tuple->DesiredTuple == RETURN_FIRST_TUPLE))
6131da177e4SLinus Torvalds break;
6141da177e4SLinus Torvalds } else
6151da177e4SLinus Torvalds if (tuple->DesiredTuple == RETURN_FIRST_TUPLE)
6161da177e4SLinus Torvalds break;
6171da177e4SLinus Torvalds
6181da177e4SLinus Torvalds if (link[0] == tuple->DesiredTuple)
6191da177e4SLinus Torvalds break;
6201da177e4SLinus Torvalds ofs += link[1] + 2;
6211da177e4SLinus Torvalds }
6221da177e4SLinus Torvalds if (i == MAX_TUPLES) {
623d50dbec3SDominik Brodowski dev_dbg(&s->dev, "cs: overrun in pcmcia_get_next_tuple\n");
624635d19beSDominik Brodowski return -ENOSPC;
6251da177e4SLinus Torvalds }
6261da177e4SLinus Torvalds
6271da177e4SLinus Torvalds tuple->TupleCode = link[0];
6281da177e4SLinus Torvalds tuple->TupleLink = link[1];
6291da177e4SLinus Torvalds tuple->CISOffset = ofs + 2;
6304c89e88bSDominik Brodowski return 0;
6311da177e4SLinus Torvalds }
6321da177e4SLinus Torvalds
pccard_get_tuple_data(struct pcmcia_socket * s,tuple_t * tuple)6331da177e4SLinus Torvalds int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple)
6341da177e4SLinus Torvalds {
6351da177e4SLinus Torvalds u_int len;
636d700518aSDominik Brodowski int ret;
6371da177e4SLinus Torvalds
6381da177e4SLinus Torvalds if (!s)
639ffb8da20SDominik Brodowski return -EINVAL;
6401da177e4SLinus Torvalds
6411da177e4SLinus Torvalds if (tuple->TupleLink < tuple->TupleOffset)
642635d19beSDominik Brodowski return -ENOSPC;
6431da177e4SLinus Torvalds len = tuple->TupleLink - tuple->TupleOffset;
6441da177e4SLinus Torvalds tuple->TupleDataLen = tuple->TupleLink;
6451da177e4SLinus Torvalds if (len == 0)
6464c89e88bSDominik Brodowski return 0;
647d700518aSDominik Brodowski ret = read_cis_cache(s, SPACE(tuple->Flags),
6481da177e4SLinus Torvalds tuple->CISOffset + tuple->TupleOffset,
6496e83ee07SDominik Brodowski min(len, (u_int) tuple->TupleDataMax),
6506e83ee07SDominik Brodowski tuple->TupleData);
651d700518aSDominik Brodowski if (ret)
652d700518aSDominik Brodowski return -1;
6534c89e88bSDominik Brodowski return 0;
6541da177e4SLinus Torvalds }
6551da177e4SLinus Torvalds
6561da177e4SLinus Torvalds
6576e83ee07SDominik Brodowski /* Parsing routines for individual tuples */
6581da177e4SLinus Torvalds
parse_device(tuple_t * tuple,cistpl_device_t * device)6591da177e4SLinus Torvalds static int parse_device(tuple_t *tuple, cistpl_device_t *device)
6601da177e4SLinus Torvalds {
6611da177e4SLinus Torvalds int i;
6621da177e4SLinus Torvalds u_char scale;
6631da177e4SLinus Torvalds u_char *p, *q;
6641da177e4SLinus Torvalds
6651da177e4SLinus Torvalds p = (u_char *)tuple->TupleData;
6661da177e4SLinus Torvalds q = p + tuple->TupleDataLen;
6671da177e4SLinus Torvalds
6681da177e4SLinus Torvalds device->ndev = 0;
6691da177e4SLinus Torvalds for (i = 0; i < CISTPL_MAX_DEVICES; i++) {
6701da177e4SLinus Torvalds
6719fea84f4SDominik Brodowski if (*p == 0xff)
6729fea84f4SDominik Brodowski break;
6731da177e4SLinus Torvalds device->dev[i].type = (*p >> 4);
6741da177e4SLinus Torvalds device->dev[i].wp = (*p & 0x08) ? 1 : 0;
6751da177e4SLinus Torvalds switch (*p & 0x07) {
6769fea84f4SDominik Brodowski case 0:
6779fea84f4SDominik Brodowski device->dev[i].speed = 0;
6789fea84f4SDominik Brodowski break;
6799fea84f4SDominik Brodowski case 1:
6809fea84f4SDominik Brodowski device->dev[i].speed = 250;
6819fea84f4SDominik Brodowski break;
6829fea84f4SDominik Brodowski case 2:
6839fea84f4SDominik Brodowski device->dev[i].speed = 200;
6849fea84f4SDominik Brodowski break;
6859fea84f4SDominik Brodowski case 3:
6869fea84f4SDominik Brodowski device->dev[i].speed = 150;
6879fea84f4SDominik Brodowski break;
6889fea84f4SDominik Brodowski case 4:
6899fea84f4SDominik Brodowski device->dev[i].speed = 100;
6909fea84f4SDominik Brodowski break;
6911da177e4SLinus Torvalds case 7:
6923f9c5f4cSDominik Brodowski if (++p == q)
6933f9c5f4cSDominik Brodowski return -EINVAL;
6941da177e4SLinus Torvalds device->dev[i].speed = SPEED_CVT(*p);
6951da177e4SLinus Torvalds while (*p & 0x80)
6963f9c5f4cSDominik Brodowski if (++p == q)
6973f9c5f4cSDominik Brodowski return -EINVAL;
6981da177e4SLinus Torvalds break;
6991da177e4SLinus Torvalds default:
7003f9c5f4cSDominik Brodowski return -EINVAL;
7011da177e4SLinus Torvalds }
7021da177e4SLinus Torvalds
7033f9c5f4cSDominik Brodowski if (++p == q)
7043f9c5f4cSDominik Brodowski return -EINVAL;
7053f9c5f4cSDominik Brodowski if (*p == 0xff)
7063f9c5f4cSDominik Brodowski break;
7071da177e4SLinus Torvalds scale = *p & 7;
7083f9c5f4cSDominik Brodowski if (scale == 7)
7093f9c5f4cSDominik Brodowski return -EINVAL;
7101da177e4SLinus Torvalds device->dev[i].size = ((*p >> 3) + 1) * (512 << (scale*2));
7111da177e4SLinus Torvalds device->ndev++;
7123f9c5f4cSDominik Brodowski if (++p == q)
7133f9c5f4cSDominik Brodowski break;
7141da177e4SLinus Torvalds }
7151da177e4SLinus Torvalds
7164c89e88bSDominik Brodowski return 0;
7171da177e4SLinus Torvalds }
7181da177e4SLinus Torvalds
7191da177e4SLinus Torvalds
parse_checksum(tuple_t * tuple,cistpl_checksum_t * csum)7201da177e4SLinus Torvalds static int parse_checksum(tuple_t *tuple, cistpl_checksum_t *csum)
7211da177e4SLinus Torvalds {
7221da177e4SLinus Torvalds u_char *p;
7231da177e4SLinus Torvalds if (tuple->TupleDataLen < 5)
7243f9c5f4cSDominik Brodowski return -EINVAL;
7251da177e4SLinus Torvalds p = (u_char *) tuple->TupleData;
7266b1e6f63SHarvey Harrison csum->addr = tuple->CISOffset + get_unaligned_le16(p) - 2;
7276b1e6f63SHarvey Harrison csum->len = get_unaligned_le16(p + 2);
7281da177e4SLinus Torvalds csum->sum = *(p + 4);
7294c89e88bSDominik Brodowski return 0;
7301da177e4SLinus Torvalds }
7311da177e4SLinus Torvalds
7321da177e4SLinus Torvalds
parse_longlink(tuple_t * tuple,cistpl_longlink_t * link)7331da177e4SLinus Torvalds static int parse_longlink(tuple_t *tuple, cistpl_longlink_t *link)
7341da177e4SLinus Torvalds {
7351da177e4SLinus Torvalds if (tuple->TupleDataLen < 4)
7363f9c5f4cSDominik Brodowski return -EINVAL;
7376b1e6f63SHarvey Harrison link->addr = get_unaligned_le32(tuple->TupleData);
7384c89e88bSDominik Brodowski return 0;
7391da177e4SLinus Torvalds }
7401da177e4SLinus Torvalds
7411da177e4SLinus Torvalds
parse_longlink_mfc(tuple_t * tuple,cistpl_longlink_mfc_t * link)7426e83ee07SDominik Brodowski static int parse_longlink_mfc(tuple_t *tuple, cistpl_longlink_mfc_t *link)
7431da177e4SLinus Torvalds {
7441da177e4SLinus Torvalds u_char *p;
7451da177e4SLinus Torvalds int i;
7461da177e4SLinus Torvalds
7471da177e4SLinus Torvalds p = (u_char *)tuple->TupleData;
7481da177e4SLinus Torvalds
7491da177e4SLinus Torvalds link->nfn = *p; p++;
7501da177e4SLinus Torvalds if (tuple->TupleDataLen <= link->nfn*5)
7513f9c5f4cSDominik Brodowski return -EINVAL;
7521da177e4SLinus Torvalds for (i = 0; i < link->nfn; i++) {
7531da177e4SLinus Torvalds link->fn[i].space = *p; p++;
7546b1e6f63SHarvey Harrison link->fn[i].addr = get_unaligned_le32(p);
755dc0cf6a2SDaniel Ritz p += 4;
7561da177e4SLinus Torvalds }
7574c89e88bSDominik Brodowski return 0;
7581da177e4SLinus Torvalds }
7591da177e4SLinus Torvalds
7601da177e4SLinus Torvalds
parse_strings(u_char * p,u_char * q,int max,char * s,u_char * ofs,u_char * found)7611da177e4SLinus Torvalds static int parse_strings(u_char *p, u_char *q, int max,
7621da177e4SLinus Torvalds char *s, u_char *ofs, u_char *found)
7631da177e4SLinus Torvalds {
7641da177e4SLinus Torvalds int i, j, ns;
7651da177e4SLinus Torvalds
7663f9c5f4cSDominik Brodowski if (p == q)
7673f9c5f4cSDominik Brodowski return -EINVAL;
7681da177e4SLinus Torvalds ns = 0; j = 0;
7691da177e4SLinus Torvalds for (i = 0; i < max; i++) {
7703f9c5f4cSDominik Brodowski if (*p == 0xff)
7713f9c5f4cSDominik Brodowski break;
7721da177e4SLinus Torvalds ofs[i] = j;
7731da177e4SLinus Torvalds ns++;
7741da177e4SLinus Torvalds for (;;) {
7751da177e4SLinus Torvalds s[j++] = (*p == 0xff) ? '\0' : *p;
7769fea84f4SDominik Brodowski if ((*p == '\0') || (*p == 0xff))
7779fea84f4SDominik Brodowski break;
7783f9c5f4cSDominik Brodowski if (++p == q)
7793f9c5f4cSDominik Brodowski return -EINVAL;
7801da177e4SLinus Torvalds }
7819fea84f4SDominik Brodowski if ((*p == 0xff) || (++p == q))
7829fea84f4SDominik Brodowski break;
7831da177e4SLinus Torvalds }
7841da177e4SLinus Torvalds if (found) {
7851da177e4SLinus Torvalds *found = ns;
7864c89e88bSDominik Brodowski return 0;
7871da177e4SLinus Torvalds }
7881da177e4SLinus Torvalds
7896e83ee07SDominik Brodowski return (ns == max) ? 0 : -EINVAL;
7906e83ee07SDominik Brodowski }
7916e83ee07SDominik Brodowski
7921da177e4SLinus Torvalds
parse_vers_1(tuple_t * tuple,cistpl_vers_1_t * vers_1)7931da177e4SLinus Torvalds static int parse_vers_1(tuple_t *tuple, cistpl_vers_1_t *vers_1)
7941da177e4SLinus Torvalds {
7951da177e4SLinus Torvalds u_char *p, *q;
7961da177e4SLinus Torvalds
7971da177e4SLinus Torvalds p = (u_char *)tuple->TupleData;
7981da177e4SLinus Torvalds q = p + tuple->TupleDataLen;
7991da177e4SLinus Torvalds
8001da177e4SLinus Torvalds vers_1->major = *p; p++;
8011da177e4SLinus Torvalds vers_1->minor = *p; p++;
8023f9c5f4cSDominik Brodowski if (p >= q)
8033f9c5f4cSDominik Brodowski return -EINVAL;
8041da177e4SLinus Torvalds
8051da177e4SLinus Torvalds return parse_strings(p, q, CISTPL_VERS_1_MAX_PROD_STRINGS,
8061da177e4SLinus Torvalds vers_1->str, vers_1->ofs, &vers_1->ns);
8071da177e4SLinus Torvalds }
8081da177e4SLinus Torvalds
8091da177e4SLinus Torvalds
parse_altstr(tuple_t * tuple,cistpl_altstr_t * altstr)8101da177e4SLinus Torvalds static int parse_altstr(tuple_t *tuple, cistpl_altstr_t *altstr)
8111da177e4SLinus Torvalds {
8121da177e4SLinus Torvalds u_char *p, *q;
8131da177e4SLinus Torvalds
8141da177e4SLinus Torvalds p = (u_char *)tuple->TupleData;
8151da177e4SLinus Torvalds q = p + tuple->TupleDataLen;
8161da177e4SLinus Torvalds
8171da177e4SLinus Torvalds return parse_strings(p, q, CISTPL_MAX_ALTSTR_STRINGS,
8181da177e4SLinus Torvalds altstr->str, altstr->ofs, &altstr->ns);
8191da177e4SLinus Torvalds }
8201da177e4SLinus Torvalds
8211da177e4SLinus Torvalds
parse_jedec(tuple_t * tuple,cistpl_jedec_t * jedec)8221da177e4SLinus Torvalds static int parse_jedec(tuple_t *tuple, cistpl_jedec_t *jedec)
8231da177e4SLinus Torvalds {
8241da177e4SLinus Torvalds u_char *p, *q;
8251da177e4SLinus Torvalds int nid;
8261da177e4SLinus Torvalds
8271da177e4SLinus Torvalds p = (u_char *)tuple->TupleData;
8281da177e4SLinus Torvalds q = p + tuple->TupleDataLen;
8291da177e4SLinus Torvalds
8301da177e4SLinus Torvalds for (nid = 0; nid < CISTPL_MAX_DEVICES; nid++) {
8319fea84f4SDominik Brodowski if (p > q-2)
8329fea84f4SDominik Brodowski break;
8331da177e4SLinus Torvalds jedec->id[nid].mfr = p[0];
8341da177e4SLinus Torvalds jedec->id[nid].info = p[1];
8351da177e4SLinus Torvalds p += 2;
8361da177e4SLinus Torvalds }
8371da177e4SLinus Torvalds jedec->nid = nid;
8384c89e88bSDominik Brodowski return 0;
8391da177e4SLinus Torvalds }
8401da177e4SLinus Torvalds
8411da177e4SLinus Torvalds
parse_manfid(tuple_t * tuple,cistpl_manfid_t * m)8421da177e4SLinus Torvalds static int parse_manfid(tuple_t *tuple, cistpl_manfid_t *m)
8431da177e4SLinus Torvalds {
8441da177e4SLinus Torvalds if (tuple->TupleDataLen < 4)
8453f9c5f4cSDominik Brodowski return -EINVAL;
8466b1e6f63SHarvey Harrison m->manf = get_unaligned_le16(tuple->TupleData);
8476b1e6f63SHarvey Harrison m->card = get_unaligned_le16(tuple->TupleData + 2);
8484c89e88bSDominik Brodowski return 0;
8491da177e4SLinus Torvalds }
8501da177e4SLinus Torvalds
8511da177e4SLinus Torvalds
parse_funcid(tuple_t * tuple,cistpl_funcid_t * f)8521da177e4SLinus Torvalds static int parse_funcid(tuple_t *tuple, cistpl_funcid_t *f)
8531da177e4SLinus Torvalds {
8541da177e4SLinus Torvalds u_char *p;
8551da177e4SLinus Torvalds if (tuple->TupleDataLen < 2)
8563f9c5f4cSDominik Brodowski return -EINVAL;
8571da177e4SLinus Torvalds p = (u_char *)tuple->TupleData;
8581da177e4SLinus Torvalds f->func = p[0];
8591da177e4SLinus Torvalds f->sysinit = p[1];
8604c89e88bSDominik Brodowski return 0;
8611da177e4SLinus Torvalds }
8621da177e4SLinus Torvalds
8631da177e4SLinus Torvalds
parse_funce(tuple_t * tuple,cistpl_funce_t * f)8641da177e4SLinus Torvalds static int parse_funce(tuple_t *tuple, cistpl_funce_t *f)
8651da177e4SLinus Torvalds {
8661da177e4SLinus Torvalds u_char *p;
8671da177e4SLinus Torvalds int i;
8681da177e4SLinus Torvalds if (tuple->TupleDataLen < 1)
8693f9c5f4cSDominik Brodowski return -EINVAL;
8701da177e4SLinus Torvalds p = (u_char *)tuple->TupleData;
8711da177e4SLinus Torvalds f->type = p[0];
8721da177e4SLinus Torvalds for (i = 1; i < tuple->TupleDataLen; i++)
8731da177e4SLinus Torvalds f->data[i-1] = p[i];
8744c89e88bSDominik Brodowski return 0;
8751da177e4SLinus Torvalds }
8761da177e4SLinus Torvalds
8771da177e4SLinus Torvalds
parse_config(tuple_t * tuple,cistpl_config_t * config)8781da177e4SLinus Torvalds static int parse_config(tuple_t *tuple, cistpl_config_t *config)
8791da177e4SLinus Torvalds {
8801da177e4SLinus Torvalds int rasz, rmsz, i;
8811da177e4SLinus Torvalds u_char *p;
8821da177e4SLinus Torvalds
8831da177e4SLinus Torvalds p = (u_char *)tuple->TupleData;
8841da177e4SLinus Torvalds rasz = *p & 0x03;
8851da177e4SLinus Torvalds rmsz = (*p & 0x3c) >> 2;
8861da177e4SLinus Torvalds if (tuple->TupleDataLen < rasz+rmsz+4)
8873f9c5f4cSDominik Brodowski return -EINVAL;
8881da177e4SLinus Torvalds config->last_idx = *(++p);
8891da177e4SLinus Torvalds p++;
8901da177e4SLinus Torvalds config->base = 0;
8911da177e4SLinus Torvalds for (i = 0; i <= rasz; i++)
8921da177e4SLinus Torvalds config->base += p[i] << (8*i);
8931da177e4SLinus Torvalds p += rasz+1;
8941da177e4SLinus Torvalds for (i = 0; i < 4; i++)
8951da177e4SLinus Torvalds config->rmask[i] = 0;
8961da177e4SLinus Torvalds for (i = 0; i <= rmsz; i++)
8971da177e4SLinus Torvalds config->rmask[i>>2] += p[i] << (8*(i%4));
8981da177e4SLinus Torvalds config->subtuples = tuple->TupleDataLen - (rasz+rmsz+4);
8994c89e88bSDominik Brodowski return 0;
9001da177e4SLinus Torvalds }
9011da177e4SLinus Torvalds
9026e83ee07SDominik Brodowski /* The following routines are all used to parse the nightmarish
9036e83ee07SDominik Brodowski * config table entries.
9046e83ee07SDominik Brodowski */
9051da177e4SLinus Torvalds
parse_power(u_char * p,u_char * q,cistpl_power_t * pwr)9066e83ee07SDominik Brodowski static u_char *parse_power(u_char *p, u_char *q, cistpl_power_t *pwr)
9071da177e4SLinus Torvalds {
9081da177e4SLinus Torvalds int i;
9091da177e4SLinus Torvalds u_int scale;
9101da177e4SLinus Torvalds
9119fea84f4SDominik Brodowski if (p == q)
9129fea84f4SDominik Brodowski return NULL;
9131da177e4SLinus Torvalds pwr->present = *p;
9141da177e4SLinus Torvalds pwr->flags = 0;
9151da177e4SLinus Torvalds p++;
9161da177e4SLinus Torvalds for (i = 0; i < 7; i++)
9171da177e4SLinus Torvalds if (pwr->present & (1<<i)) {
9189fea84f4SDominik Brodowski if (p == q)
9199fea84f4SDominik Brodowski return NULL;
9201da177e4SLinus Torvalds pwr->param[i] = POWER_CVT(*p);
9211da177e4SLinus Torvalds scale = POWER_SCALE(*p);
9221da177e4SLinus Torvalds while (*p & 0x80) {
9239fea84f4SDominik Brodowski if (++p == q)
9249fea84f4SDominik Brodowski return NULL;
9251da177e4SLinus Torvalds if ((*p & 0x7f) < 100)
9266e83ee07SDominik Brodowski pwr->param[i] +=
9276e83ee07SDominik Brodowski (*p & 0x7f) * scale / 100;
9281da177e4SLinus Torvalds else if (*p == 0x7d)
9291da177e4SLinus Torvalds pwr->flags |= CISTPL_POWER_HIGHZ_OK;
9301da177e4SLinus Torvalds else if (*p == 0x7e)
9311da177e4SLinus Torvalds pwr->param[i] = 0;
9321da177e4SLinus Torvalds else if (*p == 0x7f)
9331da177e4SLinus Torvalds pwr->flags |= CISTPL_POWER_HIGHZ_REQ;
9341da177e4SLinus Torvalds else
9351da177e4SLinus Torvalds return NULL;
9361da177e4SLinus Torvalds }
9371da177e4SLinus Torvalds p++;
9381da177e4SLinus Torvalds }
9391da177e4SLinus Torvalds return p;
9401da177e4SLinus Torvalds }
9411da177e4SLinus Torvalds
9421da177e4SLinus Torvalds
parse_timing(u_char * p,u_char * q,cistpl_timing_t * timing)9436e83ee07SDominik Brodowski static u_char *parse_timing(u_char *p, u_char *q, cistpl_timing_t *timing)
9441da177e4SLinus Torvalds {
9451da177e4SLinus Torvalds u_char scale;
9461da177e4SLinus Torvalds
9479fea84f4SDominik Brodowski if (p == q)
9489fea84f4SDominik Brodowski return NULL;
9491da177e4SLinus Torvalds scale = *p;
9501da177e4SLinus Torvalds if ((scale & 3) != 3) {
9519fea84f4SDominik Brodowski if (++p == q)
9529fea84f4SDominik Brodowski return NULL;
9531da177e4SLinus Torvalds timing->wait = SPEED_CVT(*p);
9541da177e4SLinus Torvalds timing->waitscale = exponent[scale & 3];
9551da177e4SLinus Torvalds } else
9561da177e4SLinus Torvalds timing->wait = 0;
9571da177e4SLinus Torvalds scale >>= 2;
9581da177e4SLinus Torvalds if ((scale & 7) != 7) {
9599fea84f4SDominik Brodowski if (++p == q)
9609fea84f4SDominik Brodowski return NULL;
9611da177e4SLinus Torvalds timing->ready = SPEED_CVT(*p);
9621da177e4SLinus Torvalds timing->rdyscale = exponent[scale & 7];
9631da177e4SLinus Torvalds } else
9641da177e4SLinus Torvalds timing->ready = 0;
9651da177e4SLinus Torvalds scale >>= 3;
9661da177e4SLinus Torvalds if (scale != 7) {
9679fea84f4SDominik Brodowski if (++p == q)
9689fea84f4SDominik Brodowski return NULL;
9691da177e4SLinus Torvalds timing->reserved = SPEED_CVT(*p);
9701da177e4SLinus Torvalds timing->rsvscale = exponent[scale];
9711da177e4SLinus Torvalds } else
9721da177e4SLinus Torvalds timing->reserved = 0;
9731da177e4SLinus Torvalds p++;
9741da177e4SLinus Torvalds return p;
9751da177e4SLinus Torvalds }
9761da177e4SLinus Torvalds
9771da177e4SLinus Torvalds
parse_io(u_char * p,u_char * q,cistpl_io_t * io)9781da177e4SLinus Torvalds static u_char *parse_io(u_char *p, u_char *q, cistpl_io_t *io)
9791da177e4SLinus Torvalds {
9801da177e4SLinus Torvalds int i, j, bsz, lsz;
9811da177e4SLinus Torvalds
9829fea84f4SDominik Brodowski if (p == q)
9839fea84f4SDominik Brodowski return NULL;
9841da177e4SLinus Torvalds io->flags = *p;
9851da177e4SLinus Torvalds
9861da177e4SLinus Torvalds if (!(*p & 0x80)) {
9871da177e4SLinus Torvalds io->nwin = 1;
9881da177e4SLinus Torvalds io->win[0].base = 0;
9891da177e4SLinus Torvalds io->win[0].len = (1 << (io->flags & CISTPL_IO_LINES_MASK));
9901da177e4SLinus Torvalds return p+1;
9911da177e4SLinus Torvalds }
9921da177e4SLinus Torvalds
9939fea84f4SDominik Brodowski if (++p == q)
9949fea84f4SDominik Brodowski return NULL;
9951da177e4SLinus Torvalds io->nwin = (*p & 0x0f) + 1;
9961da177e4SLinus Torvalds bsz = (*p & 0x30) >> 4;
9979fea84f4SDominik Brodowski if (bsz == 3)
9989fea84f4SDominik Brodowski bsz++;
9991da177e4SLinus Torvalds lsz = (*p & 0xc0) >> 6;
10009fea84f4SDominik Brodowski if (lsz == 3)
10019fea84f4SDominik Brodowski lsz++;
10021da177e4SLinus Torvalds p++;
10031da177e4SLinus Torvalds
10041da177e4SLinus Torvalds for (i = 0; i < io->nwin; i++) {
10051da177e4SLinus Torvalds io->win[i].base = 0;
10061da177e4SLinus Torvalds io->win[i].len = 1;
10071da177e4SLinus Torvalds for (j = 0; j < bsz; j++, p++) {
10089fea84f4SDominik Brodowski if (p == q)
10099fea84f4SDominik Brodowski return NULL;
10101da177e4SLinus Torvalds io->win[i].base += *p << (j*8);
10111da177e4SLinus Torvalds }
10121da177e4SLinus Torvalds for (j = 0; j < lsz; j++, p++) {
10139fea84f4SDominik Brodowski if (p == q)
10149fea84f4SDominik Brodowski return NULL;
10151da177e4SLinus Torvalds io->win[i].len += *p << (j*8);
10161da177e4SLinus Torvalds }
10171da177e4SLinus Torvalds }
10181da177e4SLinus Torvalds return p;
10191da177e4SLinus Torvalds }
10201da177e4SLinus Torvalds
10211da177e4SLinus Torvalds
parse_mem(u_char * p,u_char * q,cistpl_mem_t * mem)10221da177e4SLinus Torvalds static u_char *parse_mem(u_char *p, u_char *q, cistpl_mem_t *mem)
10231da177e4SLinus Torvalds {
10241da177e4SLinus Torvalds int i, j, asz, lsz, has_ha;
10251da177e4SLinus Torvalds u_int len, ca, ha;
10261da177e4SLinus Torvalds
10279fea84f4SDominik Brodowski if (p == q)
10289fea84f4SDominik Brodowski return NULL;
10291da177e4SLinus Torvalds
10301da177e4SLinus Torvalds mem->nwin = (*p & 0x07) + 1;
10311da177e4SLinus Torvalds lsz = (*p & 0x18) >> 3;
10321da177e4SLinus Torvalds asz = (*p & 0x60) >> 5;
10331da177e4SLinus Torvalds has_ha = (*p & 0x80);
10349fea84f4SDominik Brodowski if (++p == q)
10359fea84f4SDominik Brodowski return NULL;
10361da177e4SLinus Torvalds
10371da177e4SLinus Torvalds for (i = 0; i < mem->nwin; i++) {
10381da177e4SLinus Torvalds len = ca = ha = 0;
10391da177e4SLinus Torvalds for (j = 0; j < lsz; j++, p++) {
10409fea84f4SDominik Brodowski if (p == q)
10419fea84f4SDominik Brodowski return NULL;
10421da177e4SLinus Torvalds len += *p << (j*8);
10431da177e4SLinus Torvalds }
10441da177e4SLinus Torvalds for (j = 0; j < asz; j++, p++) {
10459fea84f4SDominik Brodowski if (p == q)
10469fea84f4SDominik Brodowski return NULL;
10471da177e4SLinus Torvalds ca += *p << (j*8);
10481da177e4SLinus Torvalds }
10491da177e4SLinus Torvalds if (has_ha)
10501da177e4SLinus Torvalds for (j = 0; j < asz; j++, p++) {
10519fea84f4SDominik Brodowski if (p == q)
10529fea84f4SDominik Brodowski return NULL;
10531da177e4SLinus Torvalds ha += *p << (j*8);
10541da177e4SLinus Torvalds }
10551da177e4SLinus Torvalds mem->win[i].len = len << 8;
10561da177e4SLinus Torvalds mem->win[i].card_addr = ca << 8;
10571da177e4SLinus Torvalds mem->win[i].host_addr = ha << 8;
10581da177e4SLinus Torvalds }
10591da177e4SLinus Torvalds return p;
10601da177e4SLinus Torvalds }
10611da177e4SLinus Torvalds
10621da177e4SLinus Torvalds
parse_irq(u_char * p,u_char * q,cistpl_irq_t * irq)10631da177e4SLinus Torvalds static u_char *parse_irq(u_char *p, u_char *q, cistpl_irq_t *irq)
10641da177e4SLinus Torvalds {
10653f9c5f4cSDominik Brodowski if (p == q)
10663f9c5f4cSDominik Brodowski return NULL;
10671da177e4SLinus Torvalds irq->IRQInfo1 = *p; p++;
10681da177e4SLinus Torvalds if (irq->IRQInfo1 & IRQ_INFO2_VALID) {
10693f9c5f4cSDominik Brodowski if (p+2 > q)
10703f9c5f4cSDominik Brodowski return NULL;
10711da177e4SLinus Torvalds irq->IRQInfo2 = (p[1]<<8) + p[0];
10721da177e4SLinus Torvalds p += 2;
10731da177e4SLinus Torvalds }
10741da177e4SLinus Torvalds return p;
10751da177e4SLinus Torvalds }
10761da177e4SLinus Torvalds
10771da177e4SLinus Torvalds
parse_cftable_entry(tuple_t * tuple,cistpl_cftable_entry_t * entry)10781da177e4SLinus Torvalds static int parse_cftable_entry(tuple_t *tuple,
10791da177e4SLinus Torvalds cistpl_cftable_entry_t *entry)
10801da177e4SLinus Torvalds {
10811da177e4SLinus Torvalds u_char *p, *q, features;
10821da177e4SLinus Torvalds
10831da177e4SLinus Torvalds p = tuple->TupleData;
10841da177e4SLinus Torvalds q = p + tuple->TupleDataLen;
10851da177e4SLinus Torvalds entry->index = *p & 0x3f;
10861da177e4SLinus Torvalds entry->flags = 0;
10871da177e4SLinus Torvalds if (*p & 0x40)
10881da177e4SLinus Torvalds entry->flags |= CISTPL_CFTABLE_DEFAULT;
10891da177e4SLinus Torvalds if (*p & 0x80) {
10903f9c5f4cSDominik Brodowski if (++p == q)
10913f9c5f4cSDominik Brodowski return -EINVAL;
10921da177e4SLinus Torvalds if (*p & 0x10)
10931da177e4SLinus Torvalds entry->flags |= CISTPL_CFTABLE_BVDS;
10941da177e4SLinus Torvalds if (*p & 0x20)
10951da177e4SLinus Torvalds entry->flags |= CISTPL_CFTABLE_WP;
10961da177e4SLinus Torvalds if (*p & 0x40)
10971da177e4SLinus Torvalds entry->flags |= CISTPL_CFTABLE_RDYBSY;
10981da177e4SLinus Torvalds if (*p & 0x80)
10991da177e4SLinus Torvalds entry->flags |= CISTPL_CFTABLE_MWAIT;
11001da177e4SLinus Torvalds entry->interface = *p & 0x0f;
11011da177e4SLinus Torvalds } else
11021da177e4SLinus Torvalds entry->interface = 0;
11031da177e4SLinus Torvalds
11041da177e4SLinus Torvalds /* Process optional features */
11053f9c5f4cSDominik Brodowski if (++p == q)
11063f9c5f4cSDominik Brodowski return -EINVAL;
11071da177e4SLinus Torvalds features = *p; p++;
11081da177e4SLinus Torvalds
11091da177e4SLinus Torvalds /* Power options */
11101da177e4SLinus Torvalds if ((features & 3) > 0) {
11111da177e4SLinus Torvalds p = parse_power(p, q, &entry->vcc);
11123f9c5f4cSDominik Brodowski if (p == NULL)
11133f9c5f4cSDominik Brodowski return -EINVAL;
11141da177e4SLinus Torvalds } else
11151da177e4SLinus Torvalds entry->vcc.present = 0;
11161da177e4SLinus Torvalds if ((features & 3) > 1) {
11171da177e4SLinus Torvalds p = parse_power(p, q, &entry->vpp1);
11183f9c5f4cSDominik Brodowski if (p == NULL)
11193f9c5f4cSDominik Brodowski return -EINVAL;
11201da177e4SLinus Torvalds } else
11211da177e4SLinus Torvalds entry->vpp1.present = 0;
11221da177e4SLinus Torvalds if ((features & 3) > 2) {
11231da177e4SLinus Torvalds p = parse_power(p, q, &entry->vpp2);
11243f9c5f4cSDominik Brodowski if (p == NULL)
11253f9c5f4cSDominik Brodowski return -EINVAL;
11261da177e4SLinus Torvalds } else
11271da177e4SLinus Torvalds entry->vpp2.present = 0;
11281da177e4SLinus Torvalds
11291da177e4SLinus Torvalds /* Timing options */
11301da177e4SLinus Torvalds if (features & 0x04) {
11311da177e4SLinus Torvalds p = parse_timing(p, q, &entry->timing);
11323f9c5f4cSDominik Brodowski if (p == NULL)
11333f9c5f4cSDominik Brodowski return -EINVAL;
11341da177e4SLinus Torvalds } else {
11351da177e4SLinus Torvalds entry->timing.wait = 0;
11361da177e4SLinus Torvalds entry->timing.ready = 0;
11371da177e4SLinus Torvalds entry->timing.reserved = 0;
11381da177e4SLinus Torvalds }
11391da177e4SLinus Torvalds
11401da177e4SLinus Torvalds /* I/O window options */
11411da177e4SLinus Torvalds if (features & 0x08) {
11421da177e4SLinus Torvalds p = parse_io(p, q, &entry->io);
11433f9c5f4cSDominik Brodowski if (p == NULL)
11443f9c5f4cSDominik Brodowski return -EINVAL;
11451da177e4SLinus Torvalds } else
11461da177e4SLinus Torvalds entry->io.nwin = 0;
11471da177e4SLinus Torvalds
11481da177e4SLinus Torvalds /* Interrupt options */
11491da177e4SLinus Torvalds if (features & 0x10) {
11501da177e4SLinus Torvalds p = parse_irq(p, q, &entry->irq);
11513f9c5f4cSDominik Brodowski if (p == NULL)
11523f9c5f4cSDominik Brodowski return -EINVAL;
11531da177e4SLinus Torvalds } else
11541da177e4SLinus Torvalds entry->irq.IRQInfo1 = 0;
11551da177e4SLinus Torvalds
11561da177e4SLinus Torvalds switch (features & 0x60) {
11571da177e4SLinus Torvalds case 0x00:
11581da177e4SLinus Torvalds entry->mem.nwin = 0;
11591da177e4SLinus Torvalds break;
11601da177e4SLinus Torvalds case 0x20:
11611da177e4SLinus Torvalds entry->mem.nwin = 1;
11626b1e6f63SHarvey Harrison entry->mem.win[0].len = get_unaligned_le16(p) << 8;
11631da177e4SLinus Torvalds entry->mem.win[0].card_addr = 0;
11641da177e4SLinus Torvalds entry->mem.win[0].host_addr = 0;
11651da177e4SLinus Torvalds p += 2;
11663f9c5f4cSDominik Brodowski if (p > q)
11673f9c5f4cSDominik Brodowski return -EINVAL;
11681da177e4SLinus Torvalds break;
11691da177e4SLinus Torvalds case 0x40:
11701da177e4SLinus Torvalds entry->mem.nwin = 1;
11716b1e6f63SHarvey Harrison entry->mem.win[0].len = get_unaligned_le16(p) << 8;
11726b1e6f63SHarvey Harrison entry->mem.win[0].card_addr = get_unaligned_le16(p + 2) << 8;
11731da177e4SLinus Torvalds entry->mem.win[0].host_addr = 0;
11741da177e4SLinus Torvalds p += 4;
11753f9c5f4cSDominik Brodowski if (p > q)
11763f9c5f4cSDominik Brodowski return -EINVAL;
11771da177e4SLinus Torvalds break;
11781da177e4SLinus Torvalds case 0x60:
11791da177e4SLinus Torvalds p = parse_mem(p, q, &entry->mem);
11803f9c5f4cSDominik Brodowski if (p == NULL)
11813f9c5f4cSDominik Brodowski return -EINVAL;
11821da177e4SLinus Torvalds break;
11831da177e4SLinus Torvalds }
11841da177e4SLinus Torvalds
11851da177e4SLinus Torvalds /* Misc features */
11861da177e4SLinus Torvalds if (features & 0x80) {
11873f9c5f4cSDominik Brodowski if (p == q)
11883f9c5f4cSDominik Brodowski return -EINVAL;
11891da177e4SLinus Torvalds entry->flags |= (*p << 8);
11901da177e4SLinus Torvalds while (*p & 0x80)
11913f9c5f4cSDominik Brodowski if (++p == q)
11923f9c5f4cSDominik Brodowski return -EINVAL;
11931da177e4SLinus Torvalds p++;
11941da177e4SLinus Torvalds }
11951da177e4SLinus Torvalds
11961da177e4SLinus Torvalds entry->subtuples = q-p;
11971da177e4SLinus Torvalds
11984c89e88bSDominik Brodowski return 0;
11991da177e4SLinus Torvalds }
12001da177e4SLinus Torvalds
12011da177e4SLinus Torvalds
parse_device_geo(tuple_t * tuple,cistpl_device_geo_t * geo)12021da177e4SLinus Torvalds static int parse_device_geo(tuple_t *tuple, cistpl_device_geo_t *geo)
12031da177e4SLinus Torvalds {
12041da177e4SLinus Torvalds u_char *p, *q;
12051da177e4SLinus Torvalds int n;
12061da177e4SLinus Torvalds
12071da177e4SLinus Torvalds p = (u_char *)tuple->TupleData;
12081da177e4SLinus Torvalds q = p + tuple->TupleDataLen;
12091da177e4SLinus Torvalds
12101da177e4SLinus Torvalds for (n = 0; n < CISTPL_MAX_DEVICES; n++) {
12119fea84f4SDominik Brodowski if (p > q-6)
12129fea84f4SDominik Brodowski break;
12131da177e4SLinus Torvalds geo->geo[n].buswidth = p[0];
12141da177e4SLinus Torvalds geo->geo[n].erase_block = 1 << (p[1]-1);
12151da177e4SLinus Torvalds geo->geo[n].read_block = 1 << (p[2]-1);
12161da177e4SLinus Torvalds geo->geo[n].write_block = 1 << (p[3]-1);
12171da177e4SLinus Torvalds geo->geo[n].partition = 1 << (p[4]-1);
12181da177e4SLinus Torvalds geo->geo[n].interleave = 1 << (p[5]-1);
12191da177e4SLinus Torvalds p += 6;
12201da177e4SLinus Torvalds }
12211da177e4SLinus Torvalds geo->ngeo = n;
12224c89e88bSDominik Brodowski return 0;
12231da177e4SLinus Torvalds }
12241da177e4SLinus Torvalds
12251da177e4SLinus Torvalds
parse_vers_2(tuple_t * tuple,cistpl_vers_2_t * v2)12261da177e4SLinus Torvalds static int parse_vers_2(tuple_t *tuple, cistpl_vers_2_t *v2)
12271da177e4SLinus Torvalds {
12281da177e4SLinus Torvalds u_char *p, *q;
12291da177e4SLinus Torvalds
12301da177e4SLinus Torvalds if (tuple->TupleDataLen < 10)
12313f9c5f4cSDominik Brodowski return -EINVAL;
12321da177e4SLinus Torvalds
12331da177e4SLinus Torvalds p = tuple->TupleData;
12341da177e4SLinus Torvalds q = p + tuple->TupleDataLen;
12351da177e4SLinus Torvalds
12361da177e4SLinus Torvalds v2->vers = p[0];
12371da177e4SLinus Torvalds v2->comply = p[1];
12386b1e6f63SHarvey Harrison v2->dindex = get_unaligned_le16(p + 2);
12391da177e4SLinus Torvalds v2->vspec8 = p[6];
12401da177e4SLinus Torvalds v2->vspec9 = p[7];
12411da177e4SLinus Torvalds v2->nhdr = p[8];
12421da177e4SLinus Torvalds p += 9;
12431da177e4SLinus Torvalds return parse_strings(p, q, 2, v2->str, &v2->vendor, NULL);
12441da177e4SLinus Torvalds }
12451da177e4SLinus Torvalds
12461da177e4SLinus Torvalds
parse_org(tuple_t * tuple,cistpl_org_t * org)12471da177e4SLinus Torvalds static int parse_org(tuple_t *tuple, cistpl_org_t *org)
12481da177e4SLinus Torvalds {
12491da177e4SLinus Torvalds u_char *p, *q;
12501da177e4SLinus Torvalds int i;
12511da177e4SLinus Torvalds
12521da177e4SLinus Torvalds p = tuple->TupleData;
12531da177e4SLinus Torvalds q = p + tuple->TupleDataLen;
12543f9c5f4cSDominik Brodowski if (p == q)
12553f9c5f4cSDominik Brodowski return -EINVAL;
12561da177e4SLinus Torvalds org->data_org = *p;
12573f9c5f4cSDominik Brodowski if (++p == q)
12583f9c5f4cSDominik Brodowski return -EINVAL;
12591da177e4SLinus Torvalds for (i = 0; i < 30; i++) {
12601da177e4SLinus Torvalds org->desc[i] = *p;
12619fea84f4SDominik Brodowski if (*p == '\0')
12629fea84f4SDominik Brodowski break;
12633f9c5f4cSDominik Brodowski if (++p == q)
12643f9c5f4cSDominik Brodowski return -EINVAL;
12651da177e4SLinus Torvalds }
12664c89e88bSDominik Brodowski return 0;
12671da177e4SLinus Torvalds }
12681da177e4SLinus Torvalds
12691da177e4SLinus Torvalds
parse_format(tuple_t * tuple,cistpl_format_t * fmt)12701da177e4SLinus Torvalds static int parse_format(tuple_t *tuple, cistpl_format_t *fmt)
12711da177e4SLinus Torvalds {
12721da177e4SLinus Torvalds u_char *p;
12731da177e4SLinus Torvalds
12741da177e4SLinus Torvalds if (tuple->TupleDataLen < 10)
12753f9c5f4cSDominik Brodowski return -EINVAL;
12761da177e4SLinus Torvalds
12771da177e4SLinus Torvalds p = tuple->TupleData;
12781da177e4SLinus Torvalds
12791da177e4SLinus Torvalds fmt->type = p[0];
12801da177e4SLinus Torvalds fmt->edc = p[1];
12816b1e6f63SHarvey Harrison fmt->offset = get_unaligned_le32(p + 2);
12826b1e6f63SHarvey Harrison fmt->length = get_unaligned_le32(p + 6);
12831da177e4SLinus Torvalds
12844c89e88bSDominik Brodowski return 0;
12851da177e4SLinus Torvalds }
12861da177e4SLinus Torvalds
12871da177e4SLinus Torvalds
pcmcia_parse_tuple(tuple_t * tuple,cisparse_t * parse)12882f3061ebSDominik Brodowski int pcmcia_parse_tuple(tuple_t *tuple, cisparse_t *parse)
12891da177e4SLinus Torvalds {
12904c89e88bSDominik Brodowski int ret = 0;
12911da177e4SLinus Torvalds
12921da177e4SLinus Torvalds if (tuple->TupleDataLen > tuple->TupleDataMax)
12933f9c5f4cSDominik Brodowski return -EINVAL;
12941da177e4SLinus Torvalds switch (tuple->TupleCode) {
12951da177e4SLinus Torvalds case CISTPL_DEVICE:
12961da177e4SLinus Torvalds case CISTPL_DEVICE_A:
12971da177e4SLinus Torvalds ret = parse_device(tuple, &parse->device);
12981da177e4SLinus Torvalds break;
12991da177e4SLinus Torvalds case CISTPL_CHECKSUM:
13001da177e4SLinus Torvalds ret = parse_checksum(tuple, &parse->checksum);
13011da177e4SLinus Torvalds break;
13021da177e4SLinus Torvalds case CISTPL_LONGLINK_A:
13031da177e4SLinus Torvalds case CISTPL_LONGLINK_C:
13041da177e4SLinus Torvalds ret = parse_longlink(tuple, &parse->longlink);
13051da177e4SLinus Torvalds break;
13061da177e4SLinus Torvalds case CISTPL_LONGLINK_MFC:
13071da177e4SLinus Torvalds ret = parse_longlink_mfc(tuple, &parse->longlink_mfc);
13081da177e4SLinus Torvalds break;
13091da177e4SLinus Torvalds case CISTPL_VERS_1:
13101da177e4SLinus Torvalds ret = parse_vers_1(tuple, &parse->version_1);
13111da177e4SLinus Torvalds break;
13121da177e4SLinus Torvalds case CISTPL_ALTSTR:
13131da177e4SLinus Torvalds ret = parse_altstr(tuple, &parse->altstr);
13141da177e4SLinus Torvalds break;
13151da177e4SLinus Torvalds case CISTPL_JEDEC_A:
13161da177e4SLinus Torvalds case CISTPL_JEDEC_C:
13171da177e4SLinus Torvalds ret = parse_jedec(tuple, &parse->jedec);
13181da177e4SLinus Torvalds break;
13191da177e4SLinus Torvalds case CISTPL_MANFID:
13201da177e4SLinus Torvalds ret = parse_manfid(tuple, &parse->manfid);
13211da177e4SLinus Torvalds break;
13221da177e4SLinus Torvalds case CISTPL_FUNCID:
13231da177e4SLinus Torvalds ret = parse_funcid(tuple, &parse->funcid);
13241da177e4SLinus Torvalds break;
13251da177e4SLinus Torvalds case CISTPL_FUNCE:
13261da177e4SLinus Torvalds ret = parse_funce(tuple, &parse->funce);
13271da177e4SLinus Torvalds break;
13281da177e4SLinus Torvalds case CISTPL_CONFIG:
13291da177e4SLinus Torvalds ret = parse_config(tuple, &parse->config);
13301da177e4SLinus Torvalds break;
13311da177e4SLinus Torvalds case CISTPL_CFTABLE_ENTRY:
13321da177e4SLinus Torvalds ret = parse_cftable_entry(tuple, &parse->cftable_entry);
13331da177e4SLinus Torvalds break;
13341da177e4SLinus Torvalds case CISTPL_DEVICE_GEO:
13351da177e4SLinus Torvalds case CISTPL_DEVICE_GEO_A:
13361da177e4SLinus Torvalds ret = parse_device_geo(tuple, &parse->device_geo);
13371da177e4SLinus Torvalds break;
13381da177e4SLinus Torvalds case CISTPL_VERS_2:
13391da177e4SLinus Torvalds ret = parse_vers_2(tuple, &parse->vers_2);
13401da177e4SLinus Torvalds break;
13411da177e4SLinus Torvalds case CISTPL_ORG:
13421da177e4SLinus Torvalds ret = parse_org(tuple, &parse->org);
13431da177e4SLinus Torvalds break;
13441da177e4SLinus Torvalds case CISTPL_FORMAT:
13451da177e4SLinus Torvalds case CISTPL_FORMAT_A:
13461da177e4SLinus Torvalds ret = parse_format(tuple, &parse->format);
13471da177e4SLinus Torvalds break;
13481da177e4SLinus Torvalds case CISTPL_NO_LINK:
13491da177e4SLinus Torvalds case CISTPL_LINKTARGET:
13504c89e88bSDominik Brodowski ret = 0;
13511da177e4SLinus Torvalds break;
13521da177e4SLinus Torvalds default:
1353de6405e9SDominik Brodowski ret = -EINVAL;
13541da177e4SLinus Torvalds break;
13551da177e4SLinus Torvalds }
13563f9c5f4cSDominik Brodowski if (ret)
1357d50dbec3SDominik Brodowski pr_debug("parse_tuple failed %d\n", ret);
13581da177e4SLinus Torvalds return ret;
13591da177e4SLinus Torvalds }
13602f3061ebSDominik Brodowski EXPORT_SYMBOL(pcmcia_parse_tuple);
13611da177e4SLinus Torvalds
13621da177e4SLinus Torvalds
13636e83ee07SDominik Brodowski /**
1364f131ddc4SDominik Brodowski * pccard_validate_cis() - check whether card has a sensible CIS
1365f131ddc4SDominik Brodowski * @s: the struct pcmcia_socket we are to check
1366f131ddc4SDominik Brodowski * @info: returns the number of tuples in the (valid) CIS, or 0
1367f131ddc4SDominik Brodowski *
1368f131ddc4SDominik Brodowski * This tries to determine if a card has a sensible CIS. In @info, it
1369f131ddc4SDominik Brodowski * returns the number of tuples in the CIS, or 0 if the CIS looks bad. The
1370f131ddc4SDominik Brodowski * checks include making sure several critical tuples are present and
1371f131ddc4SDominik Brodowski * valid; seeing if the total number of tuples is reasonable; and
1372f131ddc4SDominik Brodowski * looking for tuples that use reserved codes.
1373f131ddc4SDominik Brodowski *
1374f131ddc4SDominik Brodowski * The function returns 0 on success.
1375f131ddc4SDominik Brodowski */
pccard_validate_cis(struct pcmcia_socket * s,unsigned int * info)137684897fc0SDominik Brodowski int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info)
13771da177e4SLinus Torvalds {
13781da177e4SLinus Torvalds tuple_t *tuple;
13791da177e4SLinus Torvalds cisparse_t *p;
1380c5081d5fSDominik Brodowski unsigned int count = 0;
13811da177e4SLinus Torvalds int ret, reserved, dev_ok = 0, ident_ok = 0;
13821da177e4SLinus Torvalds
13831da177e4SLinus Torvalds if (!s)
1384ffb8da20SDominik Brodowski return -EINVAL;
13851da177e4SLinus Torvalds
13868402641bSAlan Cox if (s->functions || !(s->state & SOCKET_PRESENT)) {
1387a8408c17SDominik Brodowski WARN_ON(1);
1388a8408c17SDominik Brodowski return -EINVAL;
1389a8408c17SDominik Brodowski }
1390a8408c17SDominik Brodowski
1391904e3777SDominik Brodowski /* We do not want to validate the CIS cache... */
13928680c4b3SDominik Brodowski mutex_lock(&s->ops_mutex);
1393904e3777SDominik Brodowski destroy_cis_cache(s);
13948680c4b3SDominik Brodowski mutex_unlock(&s->ops_mutex);
1395904e3777SDominik Brodowski
13961da177e4SLinus Torvalds tuple = kmalloc(sizeof(*tuple), GFP_KERNEL);
13971168386aSDominik Brodowski if (tuple == NULL) {
1398f131ddc4SDominik Brodowski dev_warn(&s->dev, "no memory to validate CIS\n");
13991168386aSDominik Brodowski return -ENOMEM;
14001168386aSDominik Brodowski }
14011da177e4SLinus Torvalds p = kmalloc(sizeof(*p), GFP_KERNEL);
14021da177e4SLinus Torvalds if (p == NULL) {
14031da177e4SLinus Torvalds kfree(tuple);
1404f131ddc4SDominik Brodowski dev_warn(&s->dev, "no memory to validate CIS\n");
14051168386aSDominik Brodowski return -ENOMEM;
14061da177e4SLinus Torvalds }
14071da177e4SLinus Torvalds
1408c5081d5fSDominik Brodowski count = reserved = 0;
14091da177e4SLinus Torvalds tuple->DesiredTuple = RETURN_FIRST_TUPLE;
14101da177e4SLinus Torvalds tuple->Attributes = TUPLE_RETURN_COMMON;
141184897fc0SDominik Brodowski ret = pccard_get_first_tuple(s, BIND_FN_ALL, tuple);
14124c89e88bSDominik Brodowski if (ret != 0)
14131da177e4SLinus Torvalds goto done;
14141da177e4SLinus Torvalds
14151da177e4SLinus Torvalds /* First tuple should be DEVICE; we should really have either that
14161da177e4SLinus Torvalds or a CFTABLE_ENTRY of some sort */
14171da177e4SLinus Torvalds if ((tuple->TupleCode == CISTPL_DEVICE) ||
1418f131ddc4SDominik Brodowski (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY, p)) ||
1419f131ddc4SDominik Brodowski (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY_CB, p)))
14201da177e4SLinus Torvalds dev_ok++;
14211da177e4SLinus Torvalds
14221da177e4SLinus Torvalds /* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2
14231da177e4SLinus Torvalds tuple, for card identification. Certain old D-Link and Linksys
14241da177e4SLinus Torvalds cards have only a broken VERS_2 tuple; hence the bogus test. */
142584897fc0SDominik Brodowski if ((pccard_read_tuple(s, BIND_FN_ALL, CISTPL_MANFID, p) == 0) ||
142684897fc0SDominik Brodowski (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_1, p) == 0) ||
142784897fc0SDominik Brodowski (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_2, p) != -ENOSPC))
14281da177e4SLinus Torvalds ident_ok++;
14291da177e4SLinus Torvalds
14301da177e4SLinus Torvalds if (!dev_ok && !ident_ok)
14311da177e4SLinus Torvalds goto done;
14321da177e4SLinus Torvalds
1433c5081d5fSDominik Brodowski for (count = 1; count < MAX_TUPLES; count++) {
143484897fc0SDominik Brodowski ret = pccard_get_next_tuple(s, BIND_FN_ALL, tuple);
14354c89e88bSDominik Brodowski if (ret != 0)
14364c89e88bSDominik Brodowski break;
14371da177e4SLinus Torvalds if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) ||
14381da177e4SLinus Torvalds ((tuple->TupleCode > 0x47) && (tuple->TupleCode < 0x80)) ||
14391da177e4SLinus Torvalds ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff)))
14401da177e4SLinus Torvalds reserved++;
14411da177e4SLinus Torvalds }
1442002b90a1SMarc Zyngier if ((count == MAX_TUPLES) || (reserved > 5) ||
1443c5081d5fSDominik Brodowski ((!dev_ok || !ident_ok) && (count > 10)))
1444c5081d5fSDominik Brodowski count = 0;
14451da177e4SLinus Torvalds
1446f131ddc4SDominik Brodowski ret = 0;
1447f131ddc4SDominik Brodowski
14481da177e4SLinus Torvalds done:
1449904e3777SDominik Brodowski /* invalidate CIS cache on failure */
1450f131ddc4SDominik Brodowski if (!dev_ok || !ident_ok || !count) {
14518680c4b3SDominik Brodowski mutex_lock(&s->ops_mutex);
1452904e3777SDominik Brodowski destroy_cis_cache(s);
14538680c4b3SDominik Brodowski mutex_unlock(&s->ops_mutex);
1454e8e68fd8SDominik Brodowski /* We differentiate between dev_ok, ident_ok and count
1455e8e68fd8SDominik Brodowski failures to allow for an override for anonymous cards
1456e8e68fd8SDominik Brodowski in ds.c */
1457e8e68fd8SDominik Brodowski if (!dev_ok || !ident_ok)
1458f131ddc4SDominik Brodowski ret = -EIO;
1459e8e68fd8SDominik Brodowski else
1460e8e68fd8SDominik Brodowski ret = -EFAULT;
14611c6c9b1dSAlan Cox }
1462904e3777SDominik Brodowski
1463c5081d5fSDominik Brodowski if (info)
1464c5081d5fSDominik Brodowski *info = count;
14651da177e4SLinus Torvalds kfree(tuple);
14661da177e4SLinus Torvalds kfree(p);
1467f131ddc4SDominik Brodowski return ret;
14681da177e4SLinus Torvalds }
14696e7b51a7SDominik Brodowski
14706e7b51a7SDominik Brodowski
14716e7b51a7SDominik Brodowski #define to_socket(_dev) container_of(_dev, struct pcmcia_socket, dev)
14726e7b51a7SDominik Brodowski
pccard_extract_cis(struct pcmcia_socket * s,char * buf,loff_t off,size_t count)14736e7b51a7SDominik Brodowski static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf,
14746e7b51a7SDominik Brodowski loff_t off, size_t count)
14756e7b51a7SDominik Brodowski {
14766e7b51a7SDominik Brodowski tuple_t tuple;
14776e7b51a7SDominik Brodowski int status, i;
14786e7b51a7SDominik Brodowski loff_t pointer = 0;
14796e7b51a7SDominik Brodowski ssize_t ret = 0;
14806e7b51a7SDominik Brodowski u_char *tuplebuffer;
14816e7b51a7SDominik Brodowski u_char *tempbuffer;
14826e7b51a7SDominik Brodowski
14836da2ec56SKees Cook tuplebuffer = kmalloc_array(256, sizeof(u_char), GFP_KERNEL);
14846e7b51a7SDominik Brodowski if (!tuplebuffer)
14856e7b51a7SDominik Brodowski return -ENOMEM;
14866e7b51a7SDominik Brodowski
14876da2ec56SKees Cook tempbuffer = kmalloc_array(258, sizeof(u_char), GFP_KERNEL);
14886e7b51a7SDominik Brodowski if (!tempbuffer) {
14896e7b51a7SDominik Brodowski ret = -ENOMEM;
14906e7b51a7SDominik Brodowski goto free_tuple;
14916e7b51a7SDominik Brodowski }
14926e7b51a7SDominik Brodowski
14936e7b51a7SDominik Brodowski memset(&tuple, 0, sizeof(tuple_t));
14946e7b51a7SDominik Brodowski
14956e7b51a7SDominik Brodowski tuple.Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON;
14966e7b51a7SDominik Brodowski tuple.DesiredTuple = RETURN_FIRST_TUPLE;
14976e7b51a7SDominik Brodowski tuple.TupleOffset = 0;
14986e7b51a7SDominik Brodowski
14996e7b51a7SDominik Brodowski status = pccard_get_first_tuple(s, BIND_FN_ALL, &tuple);
15006e7b51a7SDominik Brodowski while (!status) {
15016e7b51a7SDominik Brodowski tuple.TupleData = tuplebuffer;
15026e7b51a7SDominik Brodowski tuple.TupleDataMax = 255;
15036e7b51a7SDominik Brodowski memset(tuplebuffer, 0, sizeof(u_char) * 255);
15046e7b51a7SDominik Brodowski
15056e7b51a7SDominik Brodowski status = pccard_get_tuple_data(s, &tuple);
15066e7b51a7SDominik Brodowski if (status)
15076e7b51a7SDominik Brodowski break;
15086e7b51a7SDominik Brodowski
15096e7b51a7SDominik Brodowski if (off < (pointer + 2 + tuple.TupleDataLen)) {
15106e7b51a7SDominik Brodowski tempbuffer[0] = tuple.TupleCode & 0xff;
15116e7b51a7SDominik Brodowski tempbuffer[1] = tuple.TupleLink & 0xff;
15126e7b51a7SDominik Brodowski for (i = 0; i < tuple.TupleDataLen; i++)
15136e7b51a7SDominik Brodowski tempbuffer[i + 2] = tuplebuffer[i] & 0xff;
15146e7b51a7SDominik Brodowski
15156e7b51a7SDominik Brodowski for (i = 0; i < (2 + tuple.TupleDataLen); i++) {
15166e7b51a7SDominik Brodowski if (((i + pointer) >= off) &&
15176e7b51a7SDominik Brodowski (i + pointer) < (off + count)) {
15186e7b51a7SDominik Brodowski buf[ret] = tempbuffer[i];
15196e7b51a7SDominik Brodowski ret++;
15206e7b51a7SDominik Brodowski }
15216e7b51a7SDominik Brodowski }
15226e7b51a7SDominik Brodowski }
15236e7b51a7SDominik Brodowski
15246e7b51a7SDominik Brodowski pointer += 2 + tuple.TupleDataLen;
15256e7b51a7SDominik Brodowski
15266e7b51a7SDominik Brodowski if (pointer >= (off + count))
15276e7b51a7SDominik Brodowski break;
15286e7b51a7SDominik Brodowski
15296e7b51a7SDominik Brodowski if (tuple.TupleCode == CISTPL_END)
15306e7b51a7SDominik Brodowski break;
15316e7b51a7SDominik Brodowski status = pccard_get_next_tuple(s, BIND_FN_ALL, &tuple);
15326e7b51a7SDominik Brodowski }
15336e7b51a7SDominik Brodowski
15346e7b51a7SDominik Brodowski kfree(tempbuffer);
15356e7b51a7SDominik Brodowski free_tuple:
15366e7b51a7SDominik Brodowski kfree(tuplebuffer);
15376e7b51a7SDominik Brodowski
15386e7b51a7SDominik Brodowski return ret;
15396e7b51a7SDominik Brodowski }
15406e7b51a7SDominik Brodowski
15416e7b51a7SDominik Brodowski
pccard_show_cis(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)15422c3c8beaSChris Wright static ssize_t pccard_show_cis(struct file *filp, struct kobject *kobj,
15436e7b51a7SDominik Brodowski struct bin_attribute *bin_attr,
15446e7b51a7SDominik Brodowski char *buf, loff_t off, size_t count)
15456e7b51a7SDominik Brodowski {
15466e7b51a7SDominik Brodowski unsigned int size = 0x200;
15476e7b51a7SDominik Brodowski
15486e7b51a7SDominik Brodowski if (off >= size)
15496e7b51a7SDominik Brodowski count = 0;
15506e7b51a7SDominik Brodowski else {
15516e7b51a7SDominik Brodowski struct pcmcia_socket *s;
1552a8408c17SDominik Brodowski unsigned int chains = 1;
15536e7b51a7SDominik Brodowski
15546e7b51a7SDominik Brodowski if (off + count > size)
15556e7b51a7SDominik Brodowski count = size - off;
15566e7b51a7SDominik Brodowski
15574ce6b242STian Tao s = to_socket(kobj_to_dev(kobj));
15586e7b51a7SDominik Brodowski
15596e7b51a7SDominik Brodowski if (!(s->state & SOCKET_PRESENT))
15606e7b51a7SDominik Brodowski return -ENODEV;
1561a8408c17SDominik Brodowski if (!s->functions && pccard_validate_cis(s, &chains))
15626e7b51a7SDominik Brodowski return -EIO;
15636e7b51a7SDominik Brodowski if (!chains)
15646e7b51a7SDominik Brodowski return -ENODATA;
15656e7b51a7SDominik Brodowski
15666e7b51a7SDominik Brodowski count = pccard_extract_cis(s, buf, off, count);
15676e7b51a7SDominik Brodowski }
15686e7b51a7SDominik Brodowski
15696e7b51a7SDominik Brodowski return count;
15706e7b51a7SDominik Brodowski }
15716e7b51a7SDominik Brodowski
15726e7b51a7SDominik Brodowski
pccard_store_cis(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)15732c3c8beaSChris Wright static ssize_t pccard_store_cis(struct file *filp, struct kobject *kobj,
15746e7b51a7SDominik Brodowski struct bin_attribute *bin_attr,
15756e7b51a7SDominik Brodowski char *buf, loff_t off, size_t count)
15766e7b51a7SDominik Brodowski {
15776e7b51a7SDominik Brodowski struct pcmcia_socket *s;
15786e7b51a7SDominik Brodowski int error;
15796e7b51a7SDominik Brodowski
15803f19cad3SDavid Howells error = security_locked_down(LOCKDOWN_PCMCIA_CIS);
15813f19cad3SDavid Howells if (error)
15823f19cad3SDavid Howells return error;
15833f19cad3SDavid Howells
15844ce6b242STian Tao s = to_socket(kobj_to_dev(kobj));
15856e7b51a7SDominik Brodowski
15866e7b51a7SDominik Brodowski if (off)
15876e7b51a7SDominik Brodowski return -EINVAL;
15886e7b51a7SDominik Brodowski
15896e7b51a7SDominik Brodowski if (count >= CISTPL_MAX_CIS_SIZE)
15906e7b51a7SDominik Brodowski return -EINVAL;
15916e7b51a7SDominik Brodowski
15926e7b51a7SDominik Brodowski if (!(s->state & SOCKET_PRESENT))
15936e7b51a7SDominik Brodowski return -ENODEV;
15946e7b51a7SDominik Brodowski
15956e7b51a7SDominik Brodowski error = pcmcia_replace_cis(s, buf, count);
15966e7b51a7SDominik Brodowski if (error)
15976e7b51a7SDominik Brodowski return -EIO;
15986e7b51a7SDominik Brodowski
1599af461fc1SDominik Brodowski pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
16006e7b51a7SDominik Brodowski
16016e7b51a7SDominik Brodowski return count;
16026e7b51a7SDominik Brodowski }
16036e7b51a7SDominik Brodowski
16046e7b51a7SDominik Brodowski
1605c151206bSBhumika Goyal const struct bin_attribute pccard_cis_attr = {
16066e7b51a7SDominik Brodowski .attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR },
16076e7b51a7SDominik Brodowski .size = 0x200,
16086e7b51a7SDominik Brodowski .read = pccard_show_cis,
16096e7b51a7SDominik Brodowski .write = pccard_store_cis,
16106e7b51a7SDominik Brodowski };
1611