18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * cistpl.c -- 16-bit PCMCIA Card Information Structure parser
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * The initial developer of the original code is David A. Hinds
68c2ecf20Sopenharmony_ci * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
78c2ecf20Sopenharmony_ci * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * (C) 1999		David A. Hinds
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/string.h>
168c2ecf20Sopenharmony_ci#include <linux/major.h>
178c2ecf20Sopenharmony_ci#include <linux/errno.h>
188c2ecf20Sopenharmony_ci#include <linux/timer.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/mm.h>
218c2ecf20Sopenharmony_ci#include <linux/pci.h>
228c2ecf20Sopenharmony_ci#include <linux/ioport.h>
238c2ecf20Sopenharmony_ci#include <linux/io.h>
248c2ecf20Sopenharmony_ci#include <linux/security.h>
258c2ecf20Sopenharmony_ci#include <asm/byteorder.h>
268c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <pcmcia/ss.h>
298c2ecf20Sopenharmony_ci#include <pcmcia/cisreg.h>
308c2ecf20Sopenharmony_ci#include <pcmcia/cistpl.h>
318c2ecf20Sopenharmony_ci#include <pcmcia/ds.h>
328c2ecf20Sopenharmony_ci#include "cs_internal.h"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic const u_char mantissa[] = {
358c2ecf20Sopenharmony_ci    10, 12, 13, 15, 20, 25, 30, 35,
368c2ecf20Sopenharmony_ci    40, 45, 50, 55, 60, 70, 80, 90
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic const u_int exponent[] = {
408c2ecf20Sopenharmony_ci    1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* Convert an extended speed byte to a time in nanoseconds */
448c2ecf20Sopenharmony_ci#define SPEED_CVT(v) \
458c2ecf20Sopenharmony_ci    (mantissa[(((v)>>3)&15)-1] * exponent[(v)&7] / 10)
468c2ecf20Sopenharmony_ci/* Convert a power byte to a current in 0.1 microamps */
478c2ecf20Sopenharmony_ci#define POWER_CVT(v) \
488c2ecf20Sopenharmony_ci    (mantissa[((v)>>3)&15] * exponent[(v)&7] / 10)
498c2ecf20Sopenharmony_ci#define POWER_SCALE(v)		(exponent[(v)&7])
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* Upper limit on reasonable # of tuples */
528c2ecf20Sopenharmony_ci#define MAX_TUPLES		200
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/* Bits in IRQInfo1 field */
558c2ecf20Sopenharmony_ci#define IRQ_INFO2_VALID		0x10
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* 16-bit CIS? */
588c2ecf20Sopenharmony_cistatic int cis_width;
598c2ecf20Sopenharmony_cimodule_param(cis_width, int, 0444);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_civoid release_cis_mem(struct pcmcia_socket *s)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
648c2ecf20Sopenharmony_ci	if (s->cis_mem.flags & MAP_ACTIVE) {
658c2ecf20Sopenharmony_ci		s->cis_mem.flags &= ~MAP_ACTIVE;
668c2ecf20Sopenharmony_ci		s->ops->set_mem_map(s, &s->cis_mem);
678c2ecf20Sopenharmony_ci		if (s->cis_mem.res) {
688c2ecf20Sopenharmony_ci			release_resource(s->cis_mem.res);
698c2ecf20Sopenharmony_ci			kfree(s->cis_mem.res);
708c2ecf20Sopenharmony_ci			s->cis_mem.res = NULL;
718c2ecf20Sopenharmony_ci		}
728c2ecf20Sopenharmony_ci		iounmap(s->cis_virt);
738c2ecf20Sopenharmony_ci		s->cis_virt = NULL;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/**
798c2ecf20Sopenharmony_ci * set_cis_map() - map the card memory at "card_offset" into virtual space.
808c2ecf20Sopenharmony_ci *
818c2ecf20Sopenharmony_ci * If flags & MAP_ATTRIB, map the attribute space, otherwise
828c2ecf20Sopenharmony_ci * map the memory space.
838c2ecf20Sopenharmony_ci *
848c2ecf20Sopenharmony_ci * Must be called with ops_mutex held.
858c2ecf20Sopenharmony_ci */
868c2ecf20Sopenharmony_cistatic void __iomem *set_cis_map(struct pcmcia_socket *s,
878c2ecf20Sopenharmony_ci				unsigned int card_offset, unsigned int flags)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	pccard_mem_map *mem = &s->cis_mem;
908c2ecf20Sopenharmony_ci	int ret;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (!(s->features & SS_CAP_STATIC_MAP) && (mem->res == NULL)) {
938c2ecf20Sopenharmony_ci		mem->res = pcmcia_find_mem_region(0, s->map_size,
948c2ecf20Sopenharmony_ci						s->map_size, 0, s);
958c2ecf20Sopenharmony_ci		if (mem->res == NULL) {
968c2ecf20Sopenharmony_ci			dev_notice(&s->dev, "cs: unable to map card memory!\n");
978c2ecf20Sopenharmony_ci			return NULL;
988c2ecf20Sopenharmony_ci		}
998c2ecf20Sopenharmony_ci		s->cis_virt = NULL;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (!(s->features & SS_CAP_STATIC_MAP) && (!s->cis_virt))
1038c2ecf20Sopenharmony_ci		s->cis_virt = ioremap(mem->res->start, s->map_size);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	mem->card_start = card_offset;
1068c2ecf20Sopenharmony_ci	mem->flags = flags;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	ret = s->ops->set_mem_map(s, mem);
1098c2ecf20Sopenharmony_ci	if (ret) {
1108c2ecf20Sopenharmony_ci		iounmap(s->cis_virt);
1118c2ecf20Sopenharmony_ci		s->cis_virt = NULL;
1128c2ecf20Sopenharmony_ci		return NULL;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (s->features & SS_CAP_STATIC_MAP) {
1168c2ecf20Sopenharmony_ci		if (s->cis_virt)
1178c2ecf20Sopenharmony_ci			iounmap(s->cis_virt);
1188c2ecf20Sopenharmony_ci		s->cis_virt = ioremap(mem->static_start, s->map_size);
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return s->cis_virt;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/* Bits in attr field */
1268c2ecf20Sopenharmony_ci#define IS_ATTR		1
1278c2ecf20Sopenharmony_ci#define IS_INDIRECT	8
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/**
1308c2ecf20Sopenharmony_ci * pcmcia_read_cis_mem() - low-level function to read CIS memory
1318c2ecf20Sopenharmony_ci *
1328c2ecf20Sopenharmony_ci * must be called with ops_mutex held
1338c2ecf20Sopenharmony_ci */
1348c2ecf20Sopenharmony_ciint pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
1358c2ecf20Sopenharmony_ci		 u_int len, void *ptr)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	void __iomem *sys, *end;
1388c2ecf20Sopenharmony_ci	unsigned char *buf = ptr;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (attr & IS_INDIRECT) {
1438c2ecf20Sopenharmony_ci		/* Indirect accesses use a bunch of special registers at fixed
1448c2ecf20Sopenharmony_ci		   locations in common memory */
1458c2ecf20Sopenharmony_ci		u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
1468c2ecf20Sopenharmony_ci		if (attr & IS_ATTR) {
1478c2ecf20Sopenharmony_ci			addr *= 2;
1488c2ecf20Sopenharmony_ci			flags = ICTRL0_AUTOINC;
1498c2ecf20Sopenharmony_ci		}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci		sys = set_cis_map(s, 0, MAP_ACTIVE |
1528c2ecf20Sopenharmony_ci				((cis_width) ? MAP_16BIT : 0));
1538c2ecf20Sopenharmony_ci		if (!sys) {
1548c2ecf20Sopenharmony_ci			dev_dbg(&s->dev, "could not map memory\n");
1558c2ecf20Sopenharmony_ci			memset(ptr, 0xff, len);
1568c2ecf20Sopenharmony_ci			return -1;
1578c2ecf20Sopenharmony_ci		}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		writeb(flags, sys+CISREG_ICTRL0);
1608c2ecf20Sopenharmony_ci		writeb(addr & 0xff, sys+CISREG_IADDR0);
1618c2ecf20Sopenharmony_ci		writeb((addr>>8) & 0xff, sys+CISREG_IADDR1);
1628c2ecf20Sopenharmony_ci		writeb((addr>>16) & 0xff, sys+CISREG_IADDR2);
1638c2ecf20Sopenharmony_ci		writeb((addr>>24) & 0xff, sys+CISREG_IADDR3);
1648c2ecf20Sopenharmony_ci		for ( ; len > 0; len--, buf++)
1658c2ecf20Sopenharmony_ci			*buf = readb(sys+CISREG_IDATA0);
1668c2ecf20Sopenharmony_ci	} else {
1678c2ecf20Sopenharmony_ci		u_int inc = 1, card_offset, flags;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		if (addr > CISTPL_MAX_CIS_SIZE) {
1708c2ecf20Sopenharmony_ci			dev_dbg(&s->dev,
1718c2ecf20Sopenharmony_ci				"attempt to read CIS mem at addr %#x", addr);
1728c2ecf20Sopenharmony_ci			memset(ptr, 0xff, len);
1738c2ecf20Sopenharmony_ci			return -1;
1748c2ecf20Sopenharmony_ci		}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci		flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
1778c2ecf20Sopenharmony_ci		if (attr) {
1788c2ecf20Sopenharmony_ci			flags |= MAP_ATTRIB;
1798c2ecf20Sopenharmony_ci			inc++;
1808c2ecf20Sopenharmony_ci			addr *= 2;
1818c2ecf20Sopenharmony_ci		}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci		card_offset = addr & ~(s->map_size-1);
1848c2ecf20Sopenharmony_ci		while (len) {
1858c2ecf20Sopenharmony_ci			sys = set_cis_map(s, card_offset, flags);
1868c2ecf20Sopenharmony_ci			if (!sys) {
1878c2ecf20Sopenharmony_ci				dev_dbg(&s->dev, "could not map memory\n");
1888c2ecf20Sopenharmony_ci				memset(ptr, 0xff, len);
1898c2ecf20Sopenharmony_ci				return -1;
1908c2ecf20Sopenharmony_ci			}
1918c2ecf20Sopenharmony_ci			end = sys + s->map_size;
1928c2ecf20Sopenharmony_ci			sys = sys + (addr & (s->map_size-1));
1938c2ecf20Sopenharmony_ci			for ( ; len > 0; len--, buf++, sys += inc) {
1948c2ecf20Sopenharmony_ci				if (sys == end)
1958c2ecf20Sopenharmony_ci					break;
1968c2ecf20Sopenharmony_ci				*buf = readb(sys);
1978c2ecf20Sopenharmony_ci			}
1988c2ecf20Sopenharmony_ci			card_offset += s->map_size;
1998c2ecf20Sopenharmony_ci			addr = 0;
2008c2ecf20Sopenharmony_ci		}
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci	dev_dbg(&s->dev, "  %#2.2x %#2.2x %#2.2x %#2.2x ...\n",
2038c2ecf20Sopenharmony_ci		*(u_char *)(ptr+0), *(u_char *)(ptr+1),
2048c2ecf20Sopenharmony_ci		*(u_char *)(ptr+2), *(u_char *)(ptr+3));
2058c2ecf20Sopenharmony_ci	return 0;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci/**
2108c2ecf20Sopenharmony_ci * pcmcia_write_cis_mem() - low-level function to write CIS memory
2118c2ecf20Sopenharmony_ci *
2128c2ecf20Sopenharmony_ci * Probably only useful for writing one-byte registers. Must be called
2138c2ecf20Sopenharmony_ci * with ops_mutex held.
2148c2ecf20Sopenharmony_ci */
2158c2ecf20Sopenharmony_ciint pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
2168c2ecf20Sopenharmony_ci		   u_int len, void *ptr)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	void __iomem *sys, *end;
2198c2ecf20Sopenharmony_ci	unsigned char *buf = ptr;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	dev_dbg(&s->dev,
2228c2ecf20Sopenharmony_ci		"pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	if (attr & IS_INDIRECT) {
2258c2ecf20Sopenharmony_ci		/* Indirect accesses use a bunch of special registers at fixed
2268c2ecf20Sopenharmony_ci		   locations in common memory */
2278c2ecf20Sopenharmony_ci		u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
2288c2ecf20Sopenharmony_ci		if (attr & IS_ATTR) {
2298c2ecf20Sopenharmony_ci			addr *= 2;
2308c2ecf20Sopenharmony_ci			flags = ICTRL0_AUTOINC;
2318c2ecf20Sopenharmony_ci		}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci		sys = set_cis_map(s, 0, MAP_ACTIVE |
2348c2ecf20Sopenharmony_ci				((cis_width) ? MAP_16BIT : 0));
2358c2ecf20Sopenharmony_ci		if (!sys) {
2368c2ecf20Sopenharmony_ci			dev_dbg(&s->dev, "could not map memory\n");
2378c2ecf20Sopenharmony_ci			return -EINVAL;
2388c2ecf20Sopenharmony_ci		}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci		writeb(flags, sys+CISREG_ICTRL0);
2418c2ecf20Sopenharmony_ci		writeb(addr & 0xff, sys+CISREG_IADDR0);
2428c2ecf20Sopenharmony_ci		writeb((addr>>8) & 0xff, sys+CISREG_IADDR1);
2438c2ecf20Sopenharmony_ci		writeb((addr>>16) & 0xff, sys+CISREG_IADDR2);
2448c2ecf20Sopenharmony_ci		writeb((addr>>24) & 0xff, sys+CISREG_IADDR3);
2458c2ecf20Sopenharmony_ci		for ( ; len > 0; len--, buf++)
2468c2ecf20Sopenharmony_ci			writeb(*buf, sys+CISREG_IDATA0);
2478c2ecf20Sopenharmony_ci	} else {
2488c2ecf20Sopenharmony_ci		u_int inc = 1, card_offset, flags;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci		flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
2518c2ecf20Sopenharmony_ci		if (attr & IS_ATTR) {
2528c2ecf20Sopenharmony_ci			flags |= MAP_ATTRIB;
2538c2ecf20Sopenharmony_ci			inc++;
2548c2ecf20Sopenharmony_ci			addr *= 2;
2558c2ecf20Sopenharmony_ci		}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci		card_offset = addr & ~(s->map_size-1);
2588c2ecf20Sopenharmony_ci		while (len) {
2598c2ecf20Sopenharmony_ci			sys = set_cis_map(s, card_offset, flags);
2608c2ecf20Sopenharmony_ci			if (!sys) {
2618c2ecf20Sopenharmony_ci				dev_dbg(&s->dev, "could not map memory\n");
2628c2ecf20Sopenharmony_ci				return -EINVAL;
2638c2ecf20Sopenharmony_ci			}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci			end = sys + s->map_size;
2668c2ecf20Sopenharmony_ci			sys = sys + (addr & (s->map_size-1));
2678c2ecf20Sopenharmony_ci			for ( ; len > 0; len--, buf++, sys += inc) {
2688c2ecf20Sopenharmony_ci				if (sys == end)
2698c2ecf20Sopenharmony_ci					break;
2708c2ecf20Sopenharmony_ci				writeb(*buf, sys);
2718c2ecf20Sopenharmony_ci			}
2728c2ecf20Sopenharmony_ci			card_offset += s->map_size;
2738c2ecf20Sopenharmony_ci			addr = 0;
2748c2ecf20Sopenharmony_ci		}
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci	return 0;
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci/**
2818c2ecf20Sopenharmony_ci * read_cis_cache() - read CIS memory or its associated cache
2828c2ecf20Sopenharmony_ci *
2838c2ecf20Sopenharmony_ci * This is a wrapper around read_cis_mem, with the same interface,
2848c2ecf20Sopenharmony_ci * but which caches information, for cards whose CIS may not be
2858c2ecf20Sopenharmony_ci * readable all the time.
2868c2ecf20Sopenharmony_ci */
2878c2ecf20Sopenharmony_cistatic int read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
2888c2ecf20Sopenharmony_ci			size_t len, void *ptr)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	struct cis_cache_entry *cis;
2918c2ecf20Sopenharmony_ci	int ret = 0;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	if (s->state & SOCKET_CARDBUS)
2948c2ecf20Sopenharmony_ci		return -EINVAL;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
2978c2ecf20Sopenharmony_ci	if (s->fake_cis) {
2988c2ecf20Sopenharmony_ci		if (s->fake_cis_len >= addr+len)
2998c2ecf20Sopenharmony_ci			memcpy(ptr, s->fake_cis+addr, len);
3008c2ecf20Sopenharmony_ci		else {
3018c2ecf20Sopenharmony_ci			memset(ptr, 0xff, len);
3028c2ecf20Sopenharmony_ci			ret = -EINVAL;
3038c2ecf20Sopenharmony_ci		}
3048c2ecf20Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
3058c2ecf20Sopenharmony_ci		return ret;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	list_for_each_entry(cis, &s->cis_cache, node) {
3098c2ecf20Sopenharmony_ci		if (cis->addr == addr && cis->len == len && cis->attr == attr) {
3108c2ecf20Sopenharmony_ci			memcpy(ptr, cis->cache, len);
3118c2ecf20Sopenharmony_ci			mutex_unlock(&s->ops_mutex);
3128c2ecf20Sopenharmony_ci			return 0;
3138c2ecf20Sopenharmony_ci		}
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (ret == 0) {
3198c2ecf20Sopenharmony_ci		/* Copy data into the cache */
3208c2ecf20Sopenharmony_ci		cis = kmalloc(sizeof(struct cis_cache_entry) + len, GFP_KERNEL);
3218c2ecf20Sopenharmony_ci		if (cis) {
3228c2ecf20Sopenharmony_ci			cis->addr = addr;
3238c2ecf20Sopenharmony_ci			cis->len = len;
3248c2ecf20Sopenharmony_ci			cis->attr = attr;
3258c2ecf20Sopenharmony_ci			memcpy(cis->cache, ptr, len);
3268c2ecf20Sopenharmony_ci			list_add(&cis->node, &s->cis_cache);
3278c2ecf20Sopenharmony_ci		}
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	return ret;
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic void
3358c2ecf20Sopenharmony_ciremove_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	struct cis_cache_entry *cis;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
3408c2ecf20Sopenharmony_ci	list_for_each_entry(cis, &s->cis_cache, node)
3418c2ecf20Sopenharmony_ci		if (cis->addr == addr && cis->len == len && cis->attr == attr) {
3428c2ecf20Sopenharmony_ci			list_del(&cis->node);
3438c2ecf20Sopenharmony_ci			kfree(cis);
3448c2ecf20Sopenharmony_ci			break;
3458c2ecf20Sopenharmony_ci		}
3468c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci/**
3508c2ecf20Sopenharmony_ci * destroy_cis_cache() - destroy the CIS cache
3518c2ecf20Sopenharmony_ci * @s:		pcmcia_socket for which CIS cache shall be destroyed
3528c2ecf20Sopenharmony_ci *
3538c2ecf20Sopenharmony_ci * This destroys the CIS cache but keeps any fake CIS alive. Must be
3548c2ecf20Sopenharmony_ci * called with ops_mutex held.
3558c2ecf20Sopenharmony_ci */
3568c2ecf20Sopenharmony_civoid destroy_cis_cache(struct pcmcia_socket *s)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	struct list_head *l, *n;
3598c2ecf20Sopenharmony_ci	struct cis_cache_entry *cis;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	list_for_each_safe(l, n, &s->cis_cache) {
3628c2ecf20Sopenharmony_ci		cis = list_entry(l, struct cis_cache_entry, node);
3638c2ecf20Sopenharmony_ci		list_del(&cis->node);
3648c2ecf20Sopenharmony_ci		kfree(cis);
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci/**
3698c2ecf20Sopenharmony_ci * verify_cis_cache() - does the CIS match what is in the CIS cache?
3708c2ecf20Sopenharmony_ci */
3718c2ecf20Sopenharmony_ciint verify_cis_cache(struct pcmcia_socket *s)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct cis_cache_entry *cis;
3748c2ecf20Sopenharmony_ci	char *buf;
3758c2ecf20Sopenharmony_ci	int ret;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	if (s->state & SOCKET_CARDBUS)
3788c2ecf20Sopenharmony_ci		return -EINVAL;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	buf = kmalloc(256, GFP_KERNEL);
3818c2ecf20Sopenharmony_ci	if (buf == NULL) {
3828c2ecf20Sopenharmony_ci		dev_warn(&s->dev, "no memory for verifying CIS\n");
3838c2ecf20Sopenharmony_ci		return -ENOMEM;
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
3868c2ecf20Sopenharmony_ci	list_for_each_entry(cis, &s->cis_cache, node) {
3878c2ecf20Sopenharmony_ci		int len = cis->len;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci		if (len > 256)
3908c2ecf20Sopenharmony_ci			len = 256;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		ret = pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf);
3938c2ecf20Sopenharmony_ci		if (ret || memcmp(buf, cis->cache, len) != 0) {
3948c2ecf20Sopenharmony_ci			kfree(buf);
3958c2ecf20Sopenharmony_ci			mutex_unlock(&s->ops_mutex);
3968c2ecf20Sopenharmony_ci			return -1;
3978c2ecf20Sopenharmony_ci		}
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci	kfree(buf);
4008c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
4018c2ecf20Sopenharmony_ci	return 0;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci/**
4058c2ecf20Sopenharmony_ci * pcmcia_replace_cis() - use a replacement CIS instead of the card's CIS
4068c2ecf20Sopenharmony_ci *
4078c2ecf20Sopenharmony_ci * For really bad cards, we provide a facility for uploading a
4088c2ecf20Sopenharmony_ci * replacement CIS.
4098c2ecf20Sopenharmony_ci */
4108c2ecf20Sopenharmony_ciint pcmcia_replace_cis(struct pcmcia_socket *s,
4118c2ecf20Sopenharmony_ci		       const u8 *data, const size_t len)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	if (len > CISTPL_MAX_CIS_SIZE) {
4148c2ecf20Sopenharmony_ci		dev_warn(&s->dev, "replacement CIS too big\n");
4158c2ecf20Sopenharmony_ci		return -EINVAL;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
4188c2ecf20Sopenharmony_ci	kfree(s->fake_cis);
4198c2ecf20Sopenharmony_ci	s->fake_cis = kmalloc(len, GFP_KERNEL);
4208c2ecf20Sopenharmony_ci	if (s->fake_cis == NULL) {
4218c2ecf20Sopenharmony_ci		dev_warn(&s->dev, "no memory to replace CIS\n");
4228c2ecf20Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
4238c2ecf20Sopenharmony_ci		return -ENOMEM;
4248c2ecf20Sopenharmony_ci	}
4258c2ecf20Sopenharmony_ci	s->fake_cis_len = len;
4268c2ecf20Sopenharmony_ci	memcpy(s->fake_cis, data, len);
4278c2ecf20Sopenharmony_ci	dev_info(&s->dev, "Using replacement CIS\n");
4288c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
4298c2ecf20Sopenharmony_ci	return 0;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci/* The high-level CIS tuple services */
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistruct tuple_flags {
4358c2ecf20Sopenharmony_ci	u_int		link_space:4;
4368c2ecf20Sopenharmony_ci	u_int		has_link:1;
4378c2ecf20Sopenharmony_ci	u_int		mfc_fn:3;
4388c2ecf20Sopenharmony_ci	u_int		space:4;
4398c2ecf20Sopenharmony_ci};
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci#define LINK_SPACE(f)	(((struct tuple_flags *)(&(f)))->link_space)
4428c2ecf20Sopenharmony_ci#define HAS_LINK(f)	(((struct tuple_flags *)(&(f)))->has_link)
4438c2ecf20Sopenharmony_ci#define MFC_FN(f)	(((struct tuple_flags *)(&(f)))->mfc_fn)
4448c2ecf20Sopenharmony_ci#define SPACE(f)	(((struct tuple_flags *)(&(f)))->space)
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ciint pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function,
4478c2ecf20Sopenharmony_ci			tuple_t *tuple)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	if (!s)
4508c2ecf20Sopenharmony_ci		return -EINVAL;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS))
4538c2ecf20Sopenharmony_ci		return -ENODEV;
4548c2ecf20Sopenharmony_ci	tuple->TupleLink = tuple->Flags = 0;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	/* Assume presence of a LONGLINK_C to address 0 */
4578c2ecf20Sopenharmony_ci	tuple->CISOffset = tuple->LinkOffset = 0;
4588c2ecf20Sopenharmony_ci	SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	if ((s->functions > 1) && !(tuple->Attributes & TUPLE_RETURN_COMMON)) {
4618c2ecf20Sopenharmony_ci		cisdata_t req = tuple->DesiredTuple;
4628c2ecf20Sopenharmony_ci		tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
4638c2ecf20Sopenharmony_ci		if (pccard_get_next_tuple(s, function, tuple) == 0) {
4648c2ecf20Sopenharmony_ci			tuple->DesiredTuple = CISTPL_LINKTARGET;
4658c2ecf20Sopenharmony_ci			if (pccard_get_next_tuple(s, function, tuple) != 0)
4668c2ecf20Sopenharmony_ci				return -ENOSPC;
4678c2ecf20Sopenharmony_ci		} else
4688c2ecf20Sopenharmony_ci			tuple->CISOffset = tuple->TupleLink = 0;
4698c2ecf20Sopenharmony_ci		tuple->DesiredTuple = req;
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci	return pccard_get_next_tuple(s, function, tuple);
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistatic int follow_link(struct pcmcia_socket *s, tuple_t *tuple)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	u_char link[5];
4778c2ecf20Sopenharmony_ci	u_int ofs;
4788c2ecf20Sopenharmony_ci	int ret;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (MFC_FN(tuple->Flags)) {
4818c2ecf20Sopenharmony_ci		/* Get indirect link from the MFC tuple */
4828c2ecf20Sopenharmony_ci		ret = read_cis_cache(s, LINK_SPACE(tuple->Flags),
4838c2ecf20Sopenharmony_ci				tuple->LinkOffset, 5, link);
4848c2ecf20Sopenharmony_ci		if (ret)
4858c2ecf20Sopenharmony_ci			return -1;
4868c2ecf20Sopenharmony_ci		ofs = get_unaligned_le32(link + 1);
4878c2ecf20Sopenharmony_ci		SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR);
4888c2ecf20Sopenharmony_ci		/* Move to the next indirect link */
4898c2ecf20Sopenharmony_ci		tuple->LinkOffset += 5;
4908c2ecf20Sopenharmony_ci		MFC_FN(tuple->Flags)--;
4918c2ecf20Sopenharmony_ci	} else if (HAS_LINK(tuple->Flags)) {
4928c2ecf20Sopenharmony_ci		ofs = tuple->LinkOffset;
4938c2ecf20Sopenharmony_ci		SPACE(tuple->Flags) = LINK_SPACE(tuple->Flags);
4948c2ecf20Sopenharmony_ci		HAS_LINK(tuple->Flags) = 0;
4958c2ecf20Sopenharmony_ci	} else
4968c2ecf20Sopenharmony_ci		return -1;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	if (SPACE(tuple->Flags)) {
4998c2ecf20Sopenharmony_ci		/* This is ugly, but a common CIS error is to code the long
5008c2ecf20Sopenharmony_ci		   link offset incorrectly, so we check the right spot... */
5018c2ecf20Sopenharmony_ci		ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
5028c2ecf20Sopenharmony_ci		if (ret)
5038c2ecf20Sopenharmony_ci			return -1;
5048c2ecf20Sopenharmony_ci		if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
5058c2ecf20Sopenharmony_ci			(strncmp(link+2, "CIS", 3) == 0))
5068c2ecf20Sopenharmony_ci			return ofs;
5078c2ecf20Sopenharmony_ci		remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5);
5088c2ecf20Sopenharmony_ci		/* Then, we try the wrong spot... */
5098c2ecf20Sopenharmony_ci		ofs = ofs >> 1;
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci	ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
5128c2ecf20Sopenharmony_ci	if (ret)
5138c2ecf20Sopenharmony_ci		return -1;
5148c2ecf20Sopenharmony_ci	if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
5158c2ecf20Sopenharmony_ci		(strncmp(link+2, "CIS", 3) == 0))
5168c2ecf20Sopenharmony_ci		return ofs;
5178c2ecf20Sopenharmony_ci	remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5);
5188c2ecf20Sopenharmony_ci	return -1;
5198c2ecf20Sopenharmony_ci}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ciint pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function,
5228c2ecf20Sopenharmony_ci			tuple_t *tuple)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	u_char link[2], tmp;
5258c2ecf20Sopenharmony_ci	int ofs, i, attr;
5268c2ecf20Sopenharmony_ci	int ret;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	if (!s)
5298c2ecf20Sopenharmony_ci		return -EINVAL;
5308c2ecf20Sopenharmony_ci	if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS))
5318c2ecf20Sopenharmony_ci		return -ENODEV;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	link[1] = tuple->TupleLink;
5348c2ecf20Sopenharmony_ci	ofs = tuple->CISOffset + tuple->TupleLink;
5358c2ecf20Sopenharmony_ci	attr = SPACE(tuple->Flags);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_TUPLES; i++) {
5388c2ecf20Sopenharmony_ci		if (link[1] == 0xff)
5398c2ecf20Sopenharmony_ci			link[0] = CISTPL_END;
5408c2ecf20Sopenharmony_ci		else {
5418c2ecf20Sopenharmony_ci			ret = read_cis_cache(s, attr, ofs, 2, link);
5428c2ecf20Sopenharmony_ci			if (ret)
5438c2ecf20Sopenharmony_ci				return -1;
5448c2ecf20Sopenharmony_ci			if (link[0] == CISTPL_NULL) {
5458c2ecf20Sopenharmony_ci				ofs++;
5468c2ecf20Sopenharmony_ci				continue;
5478c2ecf20Sopenharmony_ci			}
5488c2ecf20Sopenharmony_ci		}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci		/* End of chain?  Follow long link if possible */
5518c2ecf20Sopenharmony_ci		if (link[0] == CISTPL_END) {
5528c2ecf20Sopenharmony_ci			ofs = follow_link(s, tuple);
5538c2ecf20Sopenharmony_ci			if (ofs < 0)
5548c2ecf20Sopenharmony_ci				return -ENOSPC;
5558c2ecf20Sopenharmony_ci			attr = SPACE(tuple->Flags);
5568c2ecf20Sopenharmony_ci			ret = read_cis_cache(s, attr, ofs, 2, link);
5578c2ecf20Sopenharmony_ci			if (ret)
5588c2ecf20Sopenharmony_ci				return -1;
5598c2ecf20Sopenharmony_ci		}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci		/* Is this a link tuple?  Make a note of it */
5628c2ecf20Sopenharmony_ci		if ((link[0] == CISTPL_LONGLINK_A) ||
5638c2ecf20Sopenharmony_ci			(link[0] == CISTPL_LONGLINK_C) ||
5648c2ecf20Sopenharmony_ci			(link[0] == CISTPL_LONGLINK_MFC) ||
5658c2ecf20Sopenharmony_ci			(link[0] == CISTPL_LINKTARGET) ||
5668c2ecf20Sopenharmony_ci			(link[0] == CISTPL_INDIRECT) ||
5678c2ecf20Sopenharmony_ci			(link[0] == CISTPL_NO_LINK)) {
5688c2ecf20Sopenharmony_ci			switch (link[0]) {
5698c2ecf20Sopenharmony_ci			case CISTPL_LONGLINK_A:
5708c2ecf20Sopenharmony_ci				HAS_LINK(tuple->Flags) = 1;
5718c2ecf20Sopenharmony_ci				LINK_SPACE(tuple->Flags) = attr | IS_ATTR;
5728c2ecf20Sopenharmony_ci				ret = read_cis_cache(s, attr, ofs+2, 4,
5738c2ecf20Sopenharmony_ci						&tuple->LinkOffset);
5748c2ecf20Sopenharmony_ci				if (ret)
5758c2ecf20Sopenharmony_ci					return -1;
5768c2ecf20Sopenharmony_ci				break;
5778c2ecf20Sopenharmony_ci			case CISTPL_LONGLINK_C:
5788c2ecf20Sopenharmony_ci				HAS_LINK(tuple->Flags) = 1;
5798c2ecf20Sopenharmony_ci				LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR;
5808c2ecf20Sopenharmony_ci				ret = read_cis_cache(s, attr, ofs+2, 4,
5818c2ecf20Sopenharmony_ci						&tuple->LinkOffset);
5828c2ecf20Sopenharmony_ci				if (ret)
5838c2ecf20Sopenharmony_ci					return -1;
5848c2ecf20Sopenharmony_ci				break;
5858c2ecf20Sopenharmony_ci			case CISTPL_INDIRECT:
5868c2ecf20Sopenharmony_ci				HAS_LINK(tuple->Flags) = 1;
5878c2ecf20Sopenharmony_ci				LINK_SPACE(tuple->Flags) = IS_ATTR |
5888c2ecf20Sopenharmony_ci					IS_INDIRECT;
5898c2ecf20Sopenharmony_ci				tuple->LinkOffset = 0;
5908c2ecf20Sopenharmony_ci				break;
5918c2ecf20Sopenharmony_ci			case CISTPL_LONGLINK_MFC:
5928c2ecf20Sopenharmony_ci				tuple->LinkOffset = ofs + 3;
5938c2ecf20Sopenharmony_ci				LINK_SPACE(tuple->Flags) = attr;
5948c2ecf20Sopenharmony_ci				if (function == BIND_FN_ALL) {
5958c2ecf20Sopenharmony_ci					/* Follow all the MFC links */
5968c2ecf20Sopenharmony_ci					ret = read_cis_cache(s, attr, ofs+2,
5978c2ecf20Sopenharmony_ci							1, &tmp);
5988c2ecf20Sopenharmony_ci					if (ret)
5998c2ecf20Sopenharmony_ci						return -1;
6008c2ecf20Sopenharmony_ci					MFC_FN(tuple->Flags) = tmp;
6018c2ecf20Sopenharmony_ci				} else {
6028c2ecf20Sopenharmony_ci					/* Follow exactly one of the links */
6038c2ecf20Sopenharmony_ci					MFC_FN(tuple->Flags) = 1;
6048c2ecf20Sopenharmony_ci					tuple->LinkOffset += function * 5;
6058c2ecf20Sopenharmony_ci				}
6068c2ecf20Sopenharmony_ci				break;
6078c2ecf20Sopenharmony_ci			case CISTPL_NO_LINK:
6088c2ecf20Sopenharmony_ci				HAS_LINK(tuple->Flags) = 0;
6098c2ecf20Sopenharmony_ci				break;
6108c2ecf20Sopenharmony_ci			}
6118c2ecf20Sopenharmony_ci			if ((tuple->Attributes & TUPLE_RETURN_LINK) &&
6128c2ecf20Sopenharmony_ci				(tuple->DesiredTuple == RETURN_FIRST_TUPLE))
6138c2ecf20Sopenharmony_ci				break;
6148c2ecf20Sopenharmony_ci		} else
6158c2ecf20Sopenharmony_ci			if (tuple->DesiredTuple == RETURN_FIRST_TUPLE)
6168c2ecf20Sopenharmony_ci				break;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci		if (link[0] == tuple->DesiredTuple)
6198c2ecf20Sopenharmony_ci			break;
6208c2ecf20Sopenharmony_ci		ofs += link[1] + 2;
6218c2ecf20Sopenharmony_ci	}
6228c2ecf20Sopenharmony_ci	if (i == MAX_TUPLES) {
6238c2ecf20Sopenharmony_ci		dev_dbg(&s->dev, "cs: overrun in pcmcia_get_next_tuple\n");
6248c2ecf20Sopenharmony_ci		return -ENOSPC;
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	tuple->TupleCode = link[0];
6288c2ecf20Sopenharmony_ci	tuple->TupleLink = link[1];
6298c2ecf20Sopenharmony_ci	tuple->CISOffset = ofs + 2;
6308c2ecf20Sopenharmony_ci	return 0;
6318c2ecf20Sopenharmony_ci}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ciint pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple)
6348c2ecf20Sopenharmony_ci{
6358c2ecf20Sopenharmony_ci	u_int len;
6368c2ecf20Sopenharmony_ci	int ret;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	if (!s)
6398c2ecf20Sopenharmony_ci		return -EINVAL;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	if (tuple->TupleLink < tuple->TupleOffset)
6428c2ecf20Sopenharmony_ci		return -ENOSPC;
6438c2ecf20Sopenharmony_ci	len = tuple->TupleLink - tuple->TupleOffset;
6448c2ecf20Sopenharmony_ci	tuple->TupleDataLen = tuple->TupleLink;
6458c2ecf20Sopenharmony_ci	if (len == 0)
6468c2ecf20Sopenharmony_ci		return 0;
6478c2ecf20Sopenharmony_ci	ret = read_cis_cache(s, SPACE(tuple->Flags),
6488c2ecf20Sopenharmony_ci			tuple->CISOffset + tuple->TupleOffset,
6498c2ecf20Sopenharmony_ci			min(len, (u_int) tuple->TupleDataMax),
6508c2ecf20Sopenharmony_ci			tuple->TupleData);
6518c2ecf20Sopenharmony_ci	if (ret)
6528c2ecf20Sopenharmony_ci		return -1;
6538c2ecf20Sopenharmony_ci	return 0;
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci/* Parsing routines for individual tuples */
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_cistatic int parse_device(tuple_t *tuple, cistpl_device_t *device)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	int i;
6628c2ecf20Sopenharmony_ci	u_char scale;
6638c2ecf20Sopenharmony_ci	u_char *p, *q;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	p = (u_char *)tuple->TupleData;
6668c2ecf20Sopenharmony_ci	q = p + tuple->TupleDataLen;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	device->ndev = 0;
6698c2ecf20Sopenharmony_ci	for (i = 0; i < CISTPL_MAX_DEVICES; i++) {
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci		if (*p == 0xff)
6728c2ecf20Sopenharmony_ci			break;
6738c2ecf20Sopenharmony_ci		device->dev[i].type = (*p >> 4);
6748c2ecf20Sopenharmony_ci		device->dev[i].wp = (*p & 0x08) ? 1 : 0;
6758c2ecf20Sopenharmony_ci		switch (*p & 0x07) {
6768c2ecf20Sopenharmony_ci		case 0:
6778c2ecf20Sopenharmony_ci			device->dev[i].speed = 0;
6788c2ecf20Sopenharmony_ci			break;
6798c2ecf20Sopenharmony_ci		case 1:
6808c2ecf20Sopenharmony_ci			device->dev[i].speed = 250;
6818c2ecf20Sopenharmony_ci			break;
6828c2ecf20Sopenharmony_ci		case 2:
6838c2ecf20Sopenharmony_ci			device->dev[i].speed = 200;
6848c2ecf20Sopenharmony_ci			break;
6858c2ecf20Sopenharmony_ci		case 3:
6868c2ecf20Sopenharmony_ci			device->dev[i].speed = 150;
6878c2ecf20Sopenharmony_ci			break;
6888c2ecf20Sopenharmony_ci		case 4:
6898c2ecf20Sopenharmony_ci			device->dev[i].speed = 100;
6908c2ecf20Sopenharmony_ci			break;
6918c2ecf20Sopenharmony_ci		case 7:
6928c2ecf20Sopenharmony_ci			if (++p == q)
6938c2ecf20Sopenharmony_ci				return -EINVAL;
6948c2ecf20Sopenharmony_ci			device->dev[i].speed = SPEED_CVT(*p);
6958c2ecf20Sopenharmony_ci			while (*p & 0x80)
6968c2ecf20Sopenharmony_ci				if (++p == q)
6978c2ecf20Sopenharmony_ci					return -EINVAL;
6988c2ecf20Sopenharmony_ci			break;
6998c2ecf20Sopenharmony_ci		default:
7008c2ecf20Sopenharmony_ci			return -EINVAL;
7018c2ecf20Sopenharmony_ci		}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci		if (++p == q)
7048c2ecf20Sopenharmony_ci			return -EINVAL;
7058c2ecf20Sopenharmony_ci		if (*p == 0xff)
7068c2ecf20Sopenharmony_ci			break;
7078c2ecf20Sopenharmony_ci		scale = *p & 7;
7088c2ecf20Sopenharmony_ci		if (scale == 7)
7098c2ecf20Sopenharmony_ci			return -EINVAL;
7108c2ecf20Sopenharmony_ci		device->dev[i].size = ((*p >> 3) + 1) * (512 << (scale*2));
7118c2ecf20Sopenharmony_ci		device->ndev++;
7128c2ecf20Sopenharmony_ci		if (++p == q)
7138c2ecf20Sopenharmony_ci			break;
7148c2ecf20Sopenharmony_ci	}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	return 0;
7178c2ecf20Sopenharmony_ci}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_cistatic int parse_checksum(tuple_t *tuple, cistpl_checksum_t *csum)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	u_char *p;
7238c2ecf20Sopenharmony_ci	if (tuple->TupleDataLen < 5)
7248c2ecf20Sopenharmony_ci		return -EINVAL;
7258c2ecf20Sopenharmony_ci	p = (u_char *) tuple->TupleData;
7268c2ecf20Sopenharmony_ci	csum->addr = tuple->CISOffset + get_unaligned_le16(p) - 2;
7278c2ecf20Sopenharmony_ci	csum->len = get_unaligned_le16(p + 2);
7288c2ecf20Sopenharmony_ci	csum->sum = *(p + 4);
7298c2ecf20Sopenharmony_ci	return 0;
7308c2ecf20Sopenharmony_ci}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_cistatic int parse_longlink(tuple_t *tuple, cistpl_longlink_t *link)
7348c2ecf20Sopenharmony_ci{
7358c2ecf20Sopenharmony_ci	if (tuple->TupleDataLen < 4)
7368c2ecf20Sopenharmony_ci		return -EINVAL;
7378c2ecf20Sopenharmony_ci	link->addr = get_unaligned_le32(tuple->TupleData);
7388c2ecf20Sopenharmony_ci	return 0;
7398c2ecf20Sopenharmony_ci}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_cistatic int parse_longlink_mfc(tuple_t *tuple, cistpl_longlink_mfc_t *link)
7438c2ecf20Sopenharmony_ci{
7448c2ecf20Sopenharmony_ci	u_char *p;
7458c2ecf20Sopenharmony_ci	int i;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	p = (u_char *)tuple->TupleData;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	link->nfn = *p; p++;
7508c2ecf20Sopenharmony_ci	if (tuple->TupleDataLen <= link->nfn*5)
7518c2ecf20Sopenharmony_ci		return -EINVAL;
7528c2ecf20Sopenharmony_ci	for (i = 0; i < link->nfn; i++) {
7538c2ecf20Sopenharmony_ci		link->fn[i].space = *p; p++;
7548c2ecf20Sopenharmony_ci		link->fn[i].addr = get_unaligned_le32(p);
7558c2ecf20Sopenharmony_ci		p += 4;
7568c2ecf20Sopenharmony_ci	}
7578c2ecf20Sopenharmony_ci	return 0;
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cistatic int parse_strings(u_char *p, u_char *q, int max,
7628c2ecf20Sopenharmony_ci			 char *s, u_char *ofs, u_char *found)
7638c2ecf20Sopenharmony_ci{
7648c2ecf20Sopenharmony_ci	int i, j, ns;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	if (p == q)
7678c2ecf20Sopenharmony_ci		return -EINVAL;
7688c2ecf20Sopenharmony_ci	ns = 0; j = 0;
7698c2ecf20Sopenharmony_ci	for (i = 0; i < max; i++) {
7708c2ecf20Sopenharmony_ci		if (*p == 0xff)
7718c2ecf20Sopenharmony_ci			break;
7728c2ecf20Sopenharmony_ci		ofs[i] = j;
7738c2ecf20Sopenharmony_ci		ns++;
7748c2ecf20Sopenharmony_ci		for (;;) {
7758c2ecf20Sopenharmony_ci			s[j++] = (*p == 0xff) ? '\0' : *p;
7768c2ecf20Sopenharmony_ci			if ((*p == '\0') || (*p == 0xff))
7778c2ecf20Sopenharmony_ci				break;
7788c2ecf20Sopenharmony_ci			if (++p == q)
7798c2ecf20Sopenharmony_ci				return -EINVAL;
7808c2ecf20Sopenharmony_ci		}
7818c2ecf20Sopenharmony_ci		if ((*p == 0xff) || (++p == q))
7828c2ecf20Sopenharmony_ci			break;
7838c2ecf20Sopenharmony_ci	}
7848c2ecf20Sopenharmony_ci	if (found) {
7858c2ecf20Sopenharmony_ci		*found = ns;
7868c2ecf20Sopenharmony_ci		return 0;
7878c2ecf20Sopenharmony_ci	}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	return (ns == max) ? 0 : -EINVAL;
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_cistatic int parse_vers_1(tuple_t *tuple, cistpl_vers_1_t *vers_1)
7948c2ecf20Sopenharmony_ci{
7958c2ecf20Sopenharmony_ci	u_char *p, *q;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	p = (u_char *)tuple->TupleData;
7988c2ecf20Sopenharmony_ci	q = p + tuple->TupleDataLen;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	vers_1->major = *p; p++;
8018c2ecf20Sopenharmony_ci	vers_1->minor = *p; p++;
8028c2ecf20Sopenharmony_ci	if (p >= q)
8038c2ecf20Sopenharmony_ci		return -EINVAL;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	return parse_strings(p, q, CISTPL_VERS_1_MAX_PROD_STRINGS,
8068c2ecf20Sopenharmony_ci			vers_1->str, vers_1->ofs, &vers_1->ns);
8078c2ecf20Sopenharmony_ci}
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_cistatic int parse_altstr(tuple_t *tuple, cistpl_altstr_t *altstr)
8118c2ecf20Sopenharmony_ci{
8128c2ecf20Sopenharmony_ci	u_char *p, *q;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	p = (u_char *)tuple->TupleData;
8158c2ecf20Sopenharmony_ci	q = p + tuple->TupleDataLen;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	return parse_strings(p, q, CISTPL_MAX_ALTSTR_STRINGS,
8188c2ecf20Sopenharmony_ci			altstr->str, altstr->ofs, &altstr->ns);
8198c2ecf20Sopenharmony_ci}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_cistatic int parse_jedec(tuple_t *tuple, cistpl_jedec_t *jedec)
8238c2ecf20Sopenharmony_ci{
8248c2ecf20Sopenharmony_ci	u_char *p, *q;
8258c2ecf20Sopenharmony_ci	int nid;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	p = (u_char *)tuple->TupleData;
8288c2ecf20Sopenharmony_ci	q = p + tuple->TupleDataLen;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	for (nid = 0; nid < CISTPL_MAX_DEVICES; nid++) {
8318c2ecf20Sopenharmony_ci		if (p > q-2)
8328c2ecf20Sopenharmony_ci			break;
8338c2ecf20Sopenharmony_ci		jedec->id[nid].mfr = p[0];
8348c2ecf20Sopenharmony_ci		jedec->id[nid].info = p[1];
8358c2ecf20Sopenharmony_ci		p += 2;
8368c2ecf20Sopenharmony_ci	}
8378c2ecf20Sopenharmony_ci	jedec->nid = nid;
8388c2ecf20Sopenharmony_ci	return 0;
8398c2ecf20Sopenharmony_ci}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_cistatic int parse_manfid(tuple_t *tuple, cistpl_manfid_t *m)
8438c2ecf20Sopenharmony_ci{
8448c2ecf20Sopenharmony_ci	if (tuple->TupleDataLen < 4)
8458c2ecf20Sopenharmony_ci		return -EINVAL;
8468c2ecf20Sopenharmony_ci	m->manf = get_unaligned_le16(tuple->TupleData);
8478c2ecf20Sopenharmony_ci	m->card = get_unaligned_le16(tuple->TupleData + 2);
8488c2ecf20Sopenharmony_ci	return 0;
8498c2ecf20Sopenharmony_ci}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_cistatic int parse_funcid(tuple_t *tuple, cistpl_funcid_t *f)
8538c2ecf20Sopenharmony_ci{
8548c2ecf20Sopenharmony_ci	u_char *p;
8558c2ecf20Sopenharmony_ci	if (tuple->TupleDataLen < 2)
8568c2ecf20Sopenharmony_ci		return -EINVAL;
8578c2ecf20Sopenharmony_ci	p = (u_char *)tuple->TupleData;
8588c2ecf20Sopenharmony_ci	f->func = p[0];
8598c2ecf20Sopenharmony_ci	f->sysinit = p[1];
8608c2ecf20Sopenharmony_ci	return 0;
8618c2ecf20Sopenharmony_ci}
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_cistatic int parse_funce(tuple_t *tuple, cistpl_funce_t *f)
8658c2ecf20Sopenharmony_ci{
8668c2ecf20Sopenharmony_ci	u_char *p;
8678c2ecf20Sopenharmony_ci	int i;
8688c2ecf20Sopenharmony_ci	if (tuple->TupleDataLen < 1)
8698c2ecf20Sopenharmony_ci		return -EINVAL;
8708c2ecf20Sopenharmony_ci	p = (u_char *)tuple->TupleData;
8718c2ecf20Sopenharmony_ci	f->type = p[0];
8728c2ecf20Sopenharmony_ci	for (i = 1; i < tuple->TupleDataLen; i++)
8738c2ecf20Sopenharmony_ci		f->data[i-1] = p[i];
8748c2ecf20Sopenharmony_ci	return 0;
8758c2ecf20Sopenharmony_ci}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_cistatic int parse_config(tuple_t *tuple, cistpl_config_t *config)
8798c2ecf20Sopenharmony_ci{
8808c2ecf20Sopenharmony_ci	int rasz, rmsz, i;
8818c2ecf20Sopenharmony_ci	u_char *p;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	p = (u_char *)tuple->TupleData;
8848c2ecf20Sopenharmony_ci	rasz = *p & 0x03;
8858c2ecf20Sopenharmony_ci	rmsz = (*p & 0x3c) >> 2;
8868c2ecf20Sopenharmony_ci	if (tuple->TupleDataLen < rasz+rmsz+4)
8878c2ecf20Sopenharmony_ci		return -EINVAL;
8888c2ecf20Sopenharmony_ci	config->last_idx = *(++p);
8898c2ecf20Sopenharmony_ci	p++;
8908c2ecf20Sopenharmony_ci	config->base = 0;
8918c2ecf20Sopenharmony_ci	for (i = 0; i <= rasz; i++)
8928c2ecf20Sopenharmony_ci		config->base += p[i] << (8*i);
8938c2ecf20Sopenharmony_ci	p += rasz+1;
8948c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++)
8958c2ecf20Sopenharmony_ci		config->rmask[i] = 0;
8968c2ecf20Sopenharmony_ci	for (i = 0; i <= rmsz; i++)
8978c2ecf20Sopenharmony_ci		config->rmask[i>>2] += p[i] << (8*(i%4));
8988c2ecf20Sopenharmony_ci	config->subtuples = tuple->TupleDataLen - (rasz+rmsz+4);
8998c2ecf20Sopenharmony_ci	return 0;
9008c2ecf20Sopenharmony_ci}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci/* The following routines are all used to parse the nightmarish
9038c2ecf20Sopenharmony_ci * config table entries.
9048c2ecf20Sopenharmony_ci */
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_cistatic u_char *parse_power(u_char *p, u_char *q, cistpl_power_t *pwr)
9078c2ecf20Sopenharmony_ci{
9088c2ecf20Sopenharmony_ci	int i;
9098c2ecf20Sopenharmony_ci	u_int scale;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	if (p == q)
9128c2ecf20Sopenharmony_ci		return NULL;
9138c2ecf20Sopenharmony_ci	pwr->present = *p;
9148c2ecf20Sopenharmony_ci	pwr->flags = 0;
9158c2ecf20Sopenharmony_ci	p++;
9168c2ecf20Sopenharmony_ci	for (i = 0; i < 7; i++)
9178c2ecf20Sopenharmony_ci		if (pwr->present & (1<<i)) {
9188c2ecf20Sopenharmony_ci			if (p == q)
9198c2ecf20Sopenharmony_ci				return NULL;
9208c2ecf20Sopenharmony_ci			pwr->param[i] = POWER_CVT(*p);
9218c2ecf20Sopenharmony_ci			scale = POWER_SCALE(*p);
9228c2ecf20Sopenharmony_ci			while (*p & 0x80) {
9238c2ecf20Sopenharmony_ci				if (++p == q)
9248c2ecf20Sopenharmony_ci					return NULL;
9258c2ecf20Sopenharmony_ci				if ((*p & 0x7f) < 100)
9268c2ecf20Sopenharmony_ci					pwr->param[i] +=
9278c2ecf20Sopenharmony_ci						(*p & 0x7f) * scale / 100;
9288c2ecf20Sopenharmony_ci				else if (*p == 0x7d)
9298c2ecf20Sopenharmony_ci					pwr->flags |= CISTPL_POWER_HIGHZ_OK;
9308c2ecf20Sopenharmony_ci				else if (*p == 0x7e)
9318c2ecf20Sopenharmony_ci					pwr->param[i] = 0;
9328c2ecf20Sopenharmony_ci				else if (*p == 0x7f)
9338c2ecf20Sopenharmony_ci					pwr->flags |= CISTPL_POWER_HIGHZ_REQ;
9348c2ecf20Sopenharmony_ci				else
9358c2ecf20Sopenharmony_ci					return NULL;
9368c2ecf20Sopenharmony_ci			}
9378c2ecf20Sopenharmony_ci			p++;
9388c2ecf20Sopenharmony_ci		}
9398c2ecf20Sopenharmony_ci	return p;
9408c2ecf20Sopenharmony_ci}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_cistatic u_char *parse_timing(u_char *p, u_char *q, cistpl_timing_t *timing)
9448c2ecf20Sopenharmony_ci{
9458c2ecf20Sopenharmony_ci	u_char scale;
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	if (p == q)
9488c2ecf20Sopenharmony_ci		return NULL;
9498c2ecf20Sopenharmony_ci	scale = *p;
9508c2ecf20Sopenharmony_ci	if ((scale & 3) != 3) {
9518c2ecf20Sopenharmony_ci		if (++p == q)
9528c2ecf20Sopenharmony_ci			return NULL;
9538c2ecf20Sopenharmony_ci		timing->wait = SPEED_CVT(*p);
9548c2ecf20Sopenharmony_ci		timing->waitscale = exponent[scale & 3];
9558c2ecf20Sopenharmony_ci	} else
9568c2ecf20Sopenharmony_ci		timing->wait = 0;
9578c2ecf20Sopenharmony_ci	scale >>= 2;
9588c2ecf20Sopenharmony_ci	if ((scale & 7) != 7) {
9598c2ecf20Sopenharmony_ci		if (++p == q)
9608c2ecf20Sopenharmony_ci			return NULL;
9618c2ecf20Sopenharmony_ci		timing->ready = SPEED_CVT(*p);
9628c2ecf20Sopenharmony_ci		timing->rdyscale = exponent[scale & 7];
9638c2ecf20Sopenharmony_ci	} else
9648c2ecf20Sopenharmony_ci		timing->ready = 0;
9658c2ecf20Sopenharmony_ci	scale >>= 3;
9668c2ecf20Sopenharmony_ci	if (scale != 7) {
9678c2ecf20Sopenharmony_ci		if (++p == q)
9688c2ecf20Sopenharmony_ci			return NULL;
9698c2ecf20Sopenharmony_ci		timing->reserved = SPEED_CVT(*p);
9708c2ecf20Sopenharmony_ci		timing->rsvscale = exponent[scale];
9718c2ecf20Sopenharmony_ci	} else
9728c2ecf20Sopenharmony_ci		timing->reserved = 0;
9738c2ecf20Sopenharmony_ci	p++;
9748c2ecf20Sopenharmony_ci	return p;
9758c2ecf20Sopenharmony_ci}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_cistatic u_char *parse_io(u_char *p, u_char *q, cistpl_io_t *io)
9798c2ecf20Sopenharmony_ci{
9808c2ecf20Sopenharmony_ci	int i, j, bsz, lsz;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	if (p == q)
9838c2ecf20Sopenharmony_ci		return NULL;
9848c2ecf20Sopenharmony_ci	io->flags = *p;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	if (!(*p & 0x80)) {
9878c2ecf20Sopenharmony_ci		io->nwin = 1;
9888c2ecf20Sopenharmony_ci		io->win[0].base = 0;
9898c2ecf20Sopenharmony_ci		io->win[0].len = (1 << (io->flags & CISTPL_IO_LINES_MASK));
9908c2ecf20Sopenharmony_ci		return p+1;
9918c2ecf20Sopenharmony_ci	}
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	if (++p == q)
9948c2ecf20Sopenharmony_ci		return NULL;
9958c2ecf20Sopenharmony_ci	io->nwin = (*p & 0x0f) + 1;
9968c2ecf20Sopenharmony_ci	bsz = (*p & 0x30) >> 4;
9978c2ecf20Sopenharmony_ci	if (bsz == 3)
9988c2ecf20Sopenharmony_ci		bsz++;
9998c2ecf20Sopenharmony_ci	lsz = (*p & 0xc0) >> 6;
10008c2ecf20Sopenharmony_ci	if (lsz == 3)
10018c2ecf20Sopenharmony_ci		lsz++;
10028c2ecf20Sopenharmony_ci	p++;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	for (i = 0; i < io->nwin; i++) {
10058c2ecf20Sopenharmony_ci		io->win[i].base = 0;
10068c2ecf20Sopenharmony_ci		io->win[i].len = 1;
10078c2ecf20Sopenharmony_ci		for (j = 0; j < bsz; j++, p++) {
10088c2ecf20Sopenharmony_ci			if (p == q)
10098c2ecf20Sopenharmony_ci				return NULL;
10108c2ecf20Sopenharmony_ci			io->win[i].base += *p << (j*8);
10118c2ecf20Sopenharmony_ci		}
10128c2ecf20Sopenharmony_ci		for (j = 0; j < lsz; j++, p++) {
10138c2ecf20Sopenharmony_ci			if (p == q)
10148c2ecf20Sopenharmony_ci				return NULL;
10158c2ecf20Sopenharmony_ci			io->win[i].len += *p << (j*8);
10168c2ecf20Sopenharmony_ci		}
10178c2ecf20Sopenharmony_ci	}
10188c2ecf20Sopenharmony_ci	return p;
10198c2ecf20Sopenharmony_ci}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_cistatic u_char *parse_mem(u_char *p, u_char *q, cistpl_mem_t *mem)
10238c2ecf20Sopenharmony_ci{
10248c2ecf20Sopenharmony_ci	int i, j, asz, lsz, has_ha;
10258c2ecf20Sopenharmony_ci	u_int len, ca, ha;
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	if (p == q)
10288c2ecf20Sopenharmony_ci		return NULL;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	mem->nwin = (*p & 0x07) + 1;
10318c2ecf20Sopenharmony_ci	lsz = (*p & 0x18) >> 3;
10328c2ecf20Sopenharmony_ci	asz = (*p & 0x60) >> 5;
10338c2ecf20Sopenharmony_ci	has_ha = (*p & 0x80);
10348c2ecf20Sopenharmony_ci	if (++p == q)
10358c2ecf20Sopenharmony_ci		return NULL;
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	for (i = 0; i < mem->nwin; i++) {
10388c2ecf20Sopenharmony_ci		len = ca = ha = 0;
10398c2ecf20Sopenharmony_ci		for (j = 0; j < lsz; j++, p++) {
10408c2ecf20Sopenharmony_ci			if (p == q)
10418c2ecf20Sopenharmony_ci				return NULL;
10428c2ecf20Sopenharmony_ci			len += *p << (j*8);
10438c2ecf20Sopenharmony_ci		}
10448c2ecf20Sopenharmony_ci		for (j = 0; j < asz; j++, p++) {
10458c2ecf20Sopenharmony_ci			if (p == q)
10468c2ecf20Sopenharmony_ci				return NULL;
10478c2ecf20Sopenharmony_ci			ca += *p << (j*8);
10488c2ecf20Sopenharmony_ci		}
10498c2ecf20Sopenharmony_ci		if (has_ha)
10508c2ecf20Sopenharmony_ci			for (j = 0; j < asz; j++, p++) {
10518c2ecf20Sopenharmony_ci				if (p == q)
10528c2ecf20Sopenharmony_ci					return NULL;
10538c2ecf20Sopenharmony_ci				ha += *p << (j*8);
10548c2ecf20Sopenharmony_ci			}
10558c2ecf20Sopenharmony_ci		mem->win[i].len = len << 8;
10568c2ecf20Sopenharmony_ci		mem->win[i].card_addr = ca << 8;
10578c2ecf20Sopenharmony_ci		mem->win[i].host_addr = ha << 8;
10588c2ecf20Sopenharmony_ci	}
10598c2ecf20Sopenharmony_ci	return p;
10608c2ecf20Sopenharmony_ci}
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_cistatic u_char *parse_irq(u_char *p, u_char *q, cistpl_irq_t *irq)
10648c2ecf20Sopenharmony_ci{
10658c2ecf20Sopenharmony_ci	if (p == q)
10668c2ecf20Sopenharmony_ci		return NULL;
10678c2ecf20Sopenharmony_ci	irq->IRQInfo1 = *p; p++;
10688c2ecf20Sopenharmony_ci	if (irq->IRQInfo1 & IRQ_INFO2_VALID) {
10698c2ecf20Sopenharmony_ci		if (p+2 > q)
10708c2ecf20Sopenharmony_ci			return NULL;
10718c2ecf20Sopenharmony_ci		irq->IRQInfo2 = (p[1]<<8) + p[0];
10728c2ecf20Sopenharmony_ci		p += 2;
10738c2ecf20Sopenharmony_ci	}
10748c2ecf20Sopenharmony_ci	return p;
10758c2ecf20Sopenharmony_ci}
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_cistatic int parse_cftable_entry(tuple_t *tuple,
10798c2ecf20Sopenharmony_ci			       cistpl_cftable_entry_t *entry)
10808c2ecf20Sopenharmony_ci{
10818c2ecf20Sopenharmony_ci	u_char *p, *q, features;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	p = tuple->TupleData;
10848c2ecf20Sopenharmony_ci	q = p + tuple->TupleDataLen;
10858c2ecf20Sopenharmony_ci	entry->index = *p & 0x3f;
10868c2ecf20Sopenharmony_ci	entry->flags = 0;
10878c2ecf20Sopenharmony_ci	if (*p & 0x40)
10888c2ecf20Sopenharmony_ci		entry->flags |= CISTPL_CFTABLE_DEFAULT;
10898c2ecf20Sopenharmony_ci	if (*p & 0x80) {
10908c2ecf20Sopenharmony_ci		if (++p == q)
10918c2ecf20Sopenharmony_ci			return -EINVAL;
10928c2ecf20Sopenharmony_ci		if (*p & 0x10)
10938c2ecf20Sopenharmony_ci			entry->flags |= CISTPL_CFTABLE_BVDS;
10948c2ecf20Sopenharmony_ci		if (*p & 0x20)
10958c2ecf20Sopenharmony_ci			entry->flags |= CISTPL_CFTABLE_WP;
10968c2ecf20Sopenharmony_ci		if (*p & 0x40)
10978c2ecf20Sopenharmony_ci			entry->flags |= CISTPL_CFTABLE_RDYBSY;
10988c2ecf20Sopenharmony_ci		if (*p & 0x80)
10998c2ecf20Sopenharmony_ci			entry->flags |= CISTPL_CFTABLE_MWAIT;
11008c2ecf20Sopenharmony_ci		entry->interface = *p & 0x0f;
11018c2ecf20Sopenharmony_ci	} else
11028c2ecf20Sopenharmony_ci		entry->interface = 0;
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	/* Process optional features */
11058c2ecf20Sopenharmony_ci	if (++p == q)
11068c2ecf20Sopenharmony_ci		return -EINVAL;
11078c2ecf20Sopenharmony_ci	features = *p; p++;
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	/* Power options */
11108c2ecf20Sopenharmony_ci	if ((features & 3) > 0) {
11118c2ecf20Sopenharmony_ci		p = parse_power(p, q, &entry->vcc);
11128c2ecf20Sopenharmony_ci		if (p == NULL)
11138c2ecf20Sopenharmony_ci			return -EINVAL;
11148c2ecf20Sopenharmony_ci	} else
11158c2ecf20Sopenharmony_ci		entry->vcc.present = 0;
11168c2ecf20Sopenharmony_ci	if ((features & 3) > 1) {
11178c2ecf20Sopenharmony_ci		p = parse_power(p, q, &entry->vpp1);
11188c2ecf20Sopenharmony_ci		if (p == NULL)
11198c2ecf20Sopenharmony_ci			return -EINVAL;
11208c2ecf20Sopenharmony_ci	} else
11218c2ecf20Sopenharmony_ci		entry->vpp1.present = 0;
11228c2ecf20Sopenharmony_ci	if ((features & 3) > 2) {
11238c2ecf20Sopenharmony_ci		p = parse_power(p, q, &entry->vpp2);
11248c2ecf20Sopenharmony_ci		if (p == NULL)
11258c2ecf20Sopenharmony_ci			return -EINVAL;
11268c2ecf20Sopenharmony_ci	} else
11278c2ecf20Sopenharmony_ci		entry->vpp2.present = 0;
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	/* Timing options */
11308c2ecf20Sopenharmony_ci	if (features & 0x04) {
11318c2ecf20Sopenharmony_ci		p = parse_timing(p, q, &entry->timing);
11328c2ecf20Sopenharmony_ci		if (p == NULL)
11338c2ecf20Sopenharmony_ci			return -EINVAL;
11348c2ecf20Sopenharmony_ci	} else {
11358c2ecf20Sopenharmony_ci		entry->timing.wait = 0;
11368c2ecf20Sopenharmony_ci		entry->timing.ready = 0;
11378c2ecf20Sopenharmony_ci		entry->timing.reserved = 0;
11388c2ecf20Sopenharmony_ci	}
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	/* I/O window options */
11418c2ecf20Sopenharmony_ci	if (features & 0x08) {
11428c2ecf20Sopenharmony_ci		p = parse_io(p, q, &entry->io);
11438c2ecf20Sopenharmony_ci		if (p == NULL)
11448c2ecf20Sopenharmony_ci			return -EINVAL;
11458c2ecf20Sopenharmony_ci	} else
11468c2ecf20Sopenharmony_ci		entry->io.nwin = 0;
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	/* Interrupt options */
11498c2ecf20Sopenharmony_ci	if (features & 0x10) {
11508c2ecf20Sopenharmony_ci		p = parse_irq(p, q, &entry->irq);
11518c2ecf20Sopenharmony_ci		if (p == NULL)
11528c2ecf20Sopenharmony_ci			return -EINVAL;
11538c2ecf20Sopenharmony_ci	} else
11548c2ecf20Sopenharmony_ci		entry->irq.IRQInfo1 = 0;
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	switch (features & 0x60) {
11578c2ecf20Sopenharmony_ci	case 0x00:
11588c2ecf20Sopenharmony_ci		entry->mem.nwin = 0;
11598c2ecf20Sopenharmony_ci		break;
11608c2ecf20Sopenharmony_ci	case 0x20:
11618c2ecf20Sopenharmony_ci		entry->mem.nwin = 1;
11628c2ecf20Sopenharmony_ci		entry->mem.win[0].len = get_unaligned_le16(p) << 8;
11638c2ecf20Sopenharmony_ci		entry->mem.win[0].card_addr = 0;
11648c2ecf20Sopenharmony_ci		entry->mem.win[0].host_addr = 0;
11658c2ecf20Sopenharmony_ci		p += 2;
11668c2ecf20Sopenharmony_ci		if (p > q)
11678c2ecf20Sopenharmony_ci			return -EINVAL;
11688c2ecf20Sopenharmony_ci		break;
11698c2ecf20Sopenharmony_ci	case 0x40:
11708c2ecf20Sopenharmony_ci		entry->mem.nwin = 1;
11718c2ecf20Sopenharmony_ci		entry->mem.win[0].len = get_unaligned_le16(p) << 8;
11728c2ecf20Sopenharmony_ci		entry->mem.win[0].card_addr = get_unaligned_le16(p + 2) << 8;
11738c2ecf20Sopenharmony_ci		entry->mem.win[0].host_addr = 0;
11748c2ecf20Sopenharmony_ci		p += 4;
11758c2ecf20Sopenharmony_ci		if (p > q)
11768c2ecf20Sopenharmony_ci			return -EINVAL;
11778c2ecf20Sopenharmony_ci		break;
11788c2ecf20Sopenharmony_ci	case 0x60:
11798c2ecf20Sopenharmony_ci		p = parse_mem(p, q, &entry->mem);
11808c2ecf20Sopenharmony_ci		if (p == NULL)
11818c2ecf20Sopenharmony_ci			return -EINVAL;
11828c2ecf20Sopenharmony_ci		break;
11838c2ecf20Sopenharmony_ci	}
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	/* Misc features */
11868c2ecf20Sopenharmony_ci	if (features & 0x80) {
11878c2ecf20Sopenharmony_ci		if (p == q)
11888c2ecf20Sopenharmony_ci			return -EINVAL;
11898c2ecf20Sopenharmony_ci		entry->flags |= (*p << 8);
11908c2ecf20Sopenharmony_ci		while (*p & 0x80)
11918c2ecf20Sopenharmony_ci			if (++p == q)
11928c2ecf20Sopenharmony_ci				return -EINVAL;
11938c2ecf20Sopenharmony_ci		p++;
11948c2ecf20Sopenharmony_ci	}
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	entry->subtuples = q-p;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	return 0;
11998c2ecf20Sopenharmony_ci}
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_cistatic int parse_device_geo(tuple_t *tuple, cistpl_device_geo_t *geo)
12038c2ecf20Sopenharmony_ci{
12048c2ecf20Sopenharmony_ci	u_char *p, *q;
12058c2ecf20Sopenharmony_ci	int n;
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	p = (u_char *)tuple->TupleData;
12088c2ecf20Sopenharmony_ci	q = p + tuple->TupleDataLen;
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	for (n = 0; n < CISTPL_MAX_DEVICES; n++) {
12118c2ecf20Sopenharmony_ci		if (p > q-6)
12128c2ecf20Sopenharmony_ci			break;
12138c2ecf20Sopenharmony_ci		geo->geo[n].buswidth = p[0];
12148c2ecf20Sopenharmony_ci		geo->geo[n].erase_block = 1 << (p[1]-1);
12158c2ecf20Sopenharmony_ci		geo->geo[n].read_block  = 1 << (p[2]-1);
12168c2ecf20Sopenharmony_ci		geo->geo[n].write_block = 1 << (p[3]-1);
12178c2ecf20Sopenharmony_ci		geo->geo[n].partition   = 1 << (p[4]-1);
12188c2ecf20Sopenharmony_ci		geo->geo[n].interleave  = 1 << (p[5]-1);
12198c2ecf20Sopenharmony_ci		p += 6;
12208c2ecf20Sopenharmony_ci	}
12218c2ecf20Sopenharmony_ci	geo->ngeo = n;
12228c2ecf20Sopenharmony_ci	return 0;
12238c2ecf20Sopenharmony_ci}
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_cistatic int parse_vers_2(tuple_t *tuple, cistpl_vers_2_t *v2)
12278c2ecf20Sopenharmony_ci{
12288c2ecf20Sopenharmony_ci	u_char *p, *q;
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	if (tuple->TupleDataLen < 10)
12318c2ecf20Sopenharmony_ci		return -EINVAL;
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	p = tuple->TupleData;
12348c2ecf20Sopenharmony_ci	q = p + tuple->TupleDataLen;
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	v2->vers = p[0];
12378c2ecf20Sopenharmony_ci	v2->comply = p[1];
12388c2ecf20Sopenharmony_ci	v2->dindex = get_unaligned_le16(p + 2);
12398c2ecf20Sopenharmony_ci	v2->vspec8 = p[6];
12408c2ecf20Sopenharmony_ci	v2->vspec9 = p[7];
12418c2ecf20Sopenharmony_ci	v2->nhdr = p[8];
12428c2ecf20Sopenharmony_ci	p += 9;
12438c2ecf20Sopenharmony_ci	return parse_strings(p, q, 2, v2->str, &v2->vendor, NULL);
12448c2ecf20Sopenharmony_ci}
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_cistatic int parse_org(tuple_t *tuple, cistpl_org_t *org)
12488c2ecf20Sopenharmony_ci{
12498c2ecf20Sopenharmony_ci	u_char *p, *q;
12508c2ecf20Sopenharmony_ci	int i;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	p = tuple->TupleData;
12538c2ecf20Sopenharmony_ci	q = p + tuple->TupleDataLen;
12548c2ecf20Sopenharmony_ci	if (p == q)
12558c2ecf20Sopenharmony_ci		return -EINVAL;
12568c2ecf20Sopenharmony_ci	org->data_org = *p;
12578c2ecf20Sopenharmony_ci	if (++p == q)
12588c2ecf20Sopenharmony_ci		return -EINVAL;
12598c2ecf20Sopenharmony_ci	for (i = 0; i < 30; i++) {
12608c2ecf20Sopenharmony_ci		org->desc[i] = *p;
12618c2ecf20Sopenharmony_ci		if (*p == '\0')
12628c2ecf20Sopenharmony_ci			break;
12638c2ecf20Sopenharmony_ci		if (++p == q)
12648c2ecf20Sopenharmony_ci			return -EINVAL;
12658c2ecf20Sopenharmony_ci	}
12668c2ecf20Sopenharmony_ci	return 0;
12678c2ecf20Sopenharmony_ci}
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_cistatic int parse_format(tuple_t *tuple, cistpl_format_t *fmt)
12718c2ecf20Sopenharmony_ci{
12728c2ecf20Sopenharmony_ci	u_char *p;
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	if (tuple->TupleDataLen < 10)
12758c2ecf20Sopenharmony_ci		return -EINVAL;
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	p = tuple->TupleData;
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	fmt->type = p[0];
12808c2ecf20Sopenharmony_ci	fmt->edc = p[1];
12818c2ecf20Sopenharmony_ci	fmt->offset = get_unaligned_le32(p + 2);
12828c2ecf20Sopenharmony_ci	fmt->length = get_unaligned_le32(p + 6);
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	return 0;
12858c2ecf20Sopenharmony_ci}
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ciint pcmcia_parse_tuple(tuple_t *tuple, cisparse_t *parse)
12898c2ecf20Sopenharmony_ci{
12908c2ecf20Sopenharmony_ci	int ret = 0;
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	if (tuple->TupleDataLen > tuple->TupleDataMax)
12938c2ecf20Sopenharmony_ci		return -EINVAL;
12948c2ecf20Sopenharmony_ci	switch (tuple->TupleCode) {
12958c2ecf20Sopenharmony_ci	case CISTPL_DEVICE:
12968c2ecf20Sopenharmony_ci	case CISTPL_DEVICE_A:
12978c2ecf20Sopenharmony_ci		ret = parse_device(tuple, &parse->device);
12988c2ecf20Sopenharmony_ci		break;
12998c2ecf20Sopenharmony_ci	case CISTPL_CHECKSUM:
13008c2ecf20Sopenharmony_ci		ret = parse_checksum(tuple, &parse->checksum);
13018c2ecf20Sopenharmony_ci		break;
13028c2ecf20Sopenharmony_ci	case CISTPL_LONGLINK_A:
13038c2ecf20Sopenharmony_ci	case CISTPL_LONGLINK_C:
13048c2ecf20Sopenharmony_ci		ret = parse_longlink(tuple, &parse->longlink);
13058c2ecf20Sopenharmony_ci		break;
13068c2ecf20Sopenharmony_ci	case CISTPL_LONGLINK_MFC:
13078c2ecf20Sopenharmony_ci		ret = parse_longlink_mfc(tuple, &parse->longlink_mfc);
13088c2ecf20Sopenharmony_ci		break;
13098c2ecf20Sopenharmony_ci	case CISTPL_VERS_1:
13108c2ecf20Sopenharmony_ci		ret = parse_vers_1(tuple, &parse->version_1);
13118c2ecf20Sopenharmony_ci		break;
13128c2ecf20Sopenharmony_ci	case CISTPL_ALTSTR:
13138c2ecf20Sopenharmony_ci		ret = parse_altstr(tuple, &parse->altstr);
13148c2ecf20Sopenharmony_ci		break;
13158c2ecf20Sopenharmony_ci	case CISTPL_JEDEC_A:
13168c2ecf20Sopenharmony_ci	case CISTPL_JEDEC_C:
13178c2ecf20Sopenharmony_ci		ret = parse_jedec(tuple, &parse->jedec);
13188c2ecf20Sopenharmony_ci		break;
13198c2ecf20Sopenharmony_ci	case CISTPL_MANFID:
13208c2ecf20Sopenharmony_ci		ret = parse_manfid(tuple, &parse->manfid);
13218c2ecf20Sopenharmony_ci		break;
13228c2ecf20Sopenharmony_ci	case CISTPL_FUNCID:
13238c2ecf20Sopenharmony_ci		ret = parse_funcid(tuple, &parse->funcid);
13248c2ecf20Sopenharmony_ci		break;
13258c2ecf20Sopenharmony_ci	case CISTPL_FUNCE:
13268c2ecf20Sopenharmony_ci		ret = parse_funce(tuple, &parse->funce);
13278c2ecf20Sopenharmony_ci		break;
13288c2ecf20Sopenharmony_ci	case CISTPL_CONFIG:
13298c2ecf20Sopenharmony_ci		ret = parse_config(tuple, &parse->config);
13308c2ecf20Sopenharmony_ci		break;
13318c2ecf20Sopenharmony_ci	case CISTPL_CFTABLE_ENTRY:
13328c2ecf20Sopenharmony_ci		ret = parse_cftable_entry(tuple, &parse->cftable_entry);
13338c2ecf20Sopenharmony_ci		break;
13348c2ecf20Sopenharmony_ci	case CISTPL_DEVICE_GEO:
13358c2ecf20Sopenharmony_ci	case CISTPL_DEVICE_GEO_A:
13368c2ecf20Sopenharmony_ci		ret = parse_device_geo(tuple, &parse->device_geo);
13378c2ecf20Sopenharmony_ci		break;
13388c2ecf20Sopenharmony_ci	case CISTPL_VERS_2:
13398c2ecf20Sopenharmony_ci		ret = parse_vers_2(tuple, &parse->vers_2);
13408c2ecf20Sopenharmony_ci		break;
13418c2ecf20Sopenharmony_ci	case CISTPL_ORG:
13428c2ecf20Sopenharmony_ci		ret = parse_org(tuple, &parse->org);
13438c2ecf20Sopenharmony_ci		break;
13448c2ecf20Sopenharmony_ci	case CISTPL_FORMAT:
13458c2ecf20Sopenharmony_ci	case CISTPL_FORMAT_A:
13468c2ecf20Sopenharmony_ci		ret = parse_format(tuple, &parse->format);
13478c2ecf20Sopenharmony_ci		break;
13488c2ecf20Sopenharmony_ci	case CISTPL_NO_LINK:
13498c2ecf20Sopenharmony_ci	case CISTPL_LINKTARGET:
13508c2ecf20Sopenharmony_ci		ret = 0;
13518c2ecf20Sopenharmony_ci		break;
13528c2ecf20Sopenharmony_ci	default:
13538c2ecf20Sopenharmony_ci		ret = -EINVAL;
13548c2ecf20Sopenharmony_ci		break;
13558c2ecf20Sopenharmony_ci	}
13568c2ecf20Sopenharmony_ci	if (ret)
13578c2ecf20Sopenharmony_ci		pr_debug("parse_tuple failed %d\n", ret);
13588c2ecf20Sopenharmony_ci	return ret;
13598c2ecf20Sopenharmony_ci}
13608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_parse_tuple);
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci/**
13648c2ecf20Sopenharmony_ci * pccard_validate_cis() - check whether card has a sensible CIS
13658c2ecf20Sopenharmony_ci * @s:		the struct pcmcia_socket we are to check
13668c2ecf20Sopenharmony_ci * @info:	returns the number of tuples in the (valid) CIS, or 0
13678c2ecf20Sopenharmony_ci *
13688c2ecf20Sopenharmony_ci * This tries to determine if a card has a sensible CIS.  In @info, it
13698c2ecf20Sopenharmony_ci * returns the number of tuples in the CIS, or 0 if the CIS looks bad. The
13708c2ecf20Sopenharmony_ci * checks include making sure several critical tuples are present and
13718c2ecf20Sopenharmony_ci * valid; seeing if the total number of tuples is reasonable; and
13728c2ecf20Sopenharmony_ci * looking for tuples that use reserved codes.
13738c2ecf20Sopenharmony_ci *
13748c2ecf20Sopenharmony_ci * The function returns 0 on success.
13758c2ecf20Sopenharmony_ci */
13768c2ecf20Sopenharmony_ciint pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info)
13778c2ecf20Sopenharmony_ci{
13788c2ecf20Sopenharmony_ci	tuple_t *tuple;
13798c2ecf20Sopenharmony_ci	cisparse_t *p;
13808c2ecf20Sopenharmony_ci	unsigned int count = 0;
13818c2ecf20Sopenharmony_ci	int ret, reserved, dev_ok = 0, ident_ok = 0;
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	if (!s)
13848c2ecf20Sopenharmony_ci		return -EINVAL;
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	if (s->functions || !(s->state & SOCKET_PRESENT)) {
13878c2ecf20Sopenharmony_ci		WARN_ON(1);
13888c2ecf20Sopenharmony_ci		return -EINVAL;
13898c2ecf20Sopenharmony_ci	}
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	/* We do not want to validate the CIS cache... */
13928c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
13938c2ecf20Sopenharmony_ci	destroy_cis_cache(s);
13948c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	tuple = kmalloc(sizeof(*tuple), GFP_KERNEL);
13978c2ecf20Sopenharmony_ci	if (tuple == NULL) {
13988c2ecf20Sopenharmony_ci		dev_warn(&s->dev, "no memory to validate CIS\n");
13998c2ecf20Sopenharmony_ci		return -ENOMEM;
14008c2ecf20Sopenharmony_ci	}
14018c2ecf20Sopenharmony_ci	p = kmalloc(sizeof(*p), GFP_KERNEL);
14028c2ecf20Sopenharmony_ci	if (p == NULL) {
14038c2ecf20Sopenharmony_ci		kfree(tuple);
14048c2ecf20Sopenharmony_ci		dev_warn(&s->dev, "no memory to validate CIS\n");
14058c2ecf20Sopenharmony_ci		return -ENOMEM;
14068c2ecf20Sopenharmony_ci	}
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	count = reserved = 0;
14098c2ecf20Sopenharmony_ci	tuple->DesiredTuple = RETURN_FIRST_TUPLE;
14108c2ecf20Sopenharmony_ci	tuple->Attributes = TUPLE_RETURN_COMMON;
14118c2ecf20Sopenharmony_ci	ret = pccard_get_first_tuple(s, BIND_FN_ALL, tuple);
14128c2ecf20Sopenharmony_ci	if (ret != 0)
14138c2ecf20Sopenharmony_ci		goto done;
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	/* First tuple should be DEVICE; we should really have either that
14168c2ecf20Sopenharmony_ci	   or a CFTABLE_ENTRY of some sort */
14178c2ecf20Sopenharmony_ci	if ((tuple->TupleCode == CISTPL_DEVICE) ||
14188c2ecf20Sopenharmony_ci	    (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY, p)) ||
14198c2ecf20Sopenharmony_ci	    (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY_CB, p)))
14208c2ecf20Sopenharmony_ci		dev_ok++;
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci	/* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2
14238c2ecf20Sopenharmony_ci	   tuple, for card identification.  Certain old D-Link and Linksys
14248c2ecf20Sopenharmony_ci	   cards have only a broken VERS_2 tuple; hence the bogus test. */
14258c2ecf20Sopenharmony_ci	if ((pccard_read_tuple(s, BIND_FN_ALL, CISTPL_MANFID, p) == 0) ||
14268c2ecf20Sopenharmony_ci	    (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_1, p) == 0) ||
14278c2ecf20Sopenharmony_ci	    (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_2, p) != -ENOSPC))
14288c2ecf20Sopenharmony_ci		ident_ok++;
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci	if (!dev_ok && !ident_ok)
14318c2ecf20Sopenharmony_ci		goto done;
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	for (count = 1; count < MAX_TUPLES; count++) {
14348c2ecf20Sopenharmony_ci		ret = pccard_get_next_tuple(s, BIND_FN_ALL, tuple);
14358c2ecf20Sopenharmony_ci		if (ret != 0)
14368c2ecf20Sopenharmony_ci			break;
14378c2ecf20Sopenharmony_ci		if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) ||
14388c2ecf20Sopenharmony_ci		    ((tuple->TupleCode > 0x47) && (tuple->TupleCode < 0x80)) ||
14398c2ecf20Sopenharmony_ci		    ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff)))
14408c2ecf20Sopenharmony_ci			reserved++;
14418c2ecf20Sopenharmony_ci	}
14428c2ecf20Sopenharmony_ci	if ((count == MAX_TUPLES) || (reserved > 5) ||
14438c2ecf20Sopenharmony_ci		((!dev_ok || !ident_ok) && (count > 10)))
14448c2ecf20Sopenharmony_ci		count = 0;
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci	ret = 0;
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_cidone:
14498c2ecf20Sopenharmony_ci	/* invalidate CIS cache on failure */
14508c2ecf20Sopenharmony_ci	if (!dev_ok || !ident_ok || !count) {
14518c2ecf20Sopenharmony_ci		mutex_lock(&s->ops_mutex);
14528c2ecf20Sopenharmony_ci		destroy_cis_cache(s);
14538c2ecf20Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
14548c2ecf20Sopenharmony_ci		/* We differentiate between dev_ok, ident_ok and count
14558c2ecf20Sopenharmony_ci		   failures to allow for an override for anonymous cards
14568c2ecf20Sopenharmony_ci		   in ds.c */
14578c2ecf20Sopenharmony_ci		if (!dev_ok || !ident_ok)
14588c2ecf20Sopenharmony_ci			ret = -EIO;
14598c2ecf20Sopenharmony_ci		else
14608c2ecf20Sopenharmony_ci			ret = -EFAULT;
14618c2ecf20Sopenharmony_ci	}
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	if (info)
14648c2ecf20Sopenharmony_ci		*info = count;
14658c2ecf20Sopenharmony_ci	kfree(tuple);
14668c2ecf20Sopenharmony_ci	kfree(p);
14678c2ecf20Sopenharmony_ci	return ret;
14688c2ecf20Sopenharmony_ci}
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci#define to_socket(_dev) container_of(_dev, struct pcmcia_socket, dev)
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_cistatic ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf,
14748c2ecf20Sopenharmony_ci				  loff_t off, size_t count)
14758c2ecf20Sopenharmony_ci{
14768c2ecf20Sopenharmony_ci	tuple_t tuple;
14778c2ecf20Sopenharmony_ci	int status, i;
14788c2ecf20Sopenharmony_ci	loff_t pointer = 0;
14798c2ecf20Sopenharmony_ci	ssize_t ret = 0;
14808c2ecf20Sopenharmony_ci	u_char *tuplebuffer;
14818c2ecf20Sopenharmony_ci	u_char *tempbuffer;
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	tuplebuffer = kmalloc_array(256, sizeof(u_char), GFP_KERNEL);
14848c2ecf20Sopenharmony_ci	if (!tuplebuffer)
14858c2ecf20Sopenharmony_ci		return -ENOMEM;
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci	tempbuffer = kmalloc_array(258, sizeof(u_char), GFP_KERNEL);
14888c2ecf20Sopenharmony_ci	if (!tempbuffer) {
14898c2ecf20Sopenharmony_ci		ret = -ENOMEM;
14908c2ecf20Sopenharmony_ci		goto free_tuple;
14918c2ecf20Sopenharmony_ci	}
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	memset(&tuple, 0, sizeof(tuple_t));
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	tuple.Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON;
14968c2ecf20Sopenharmony_ci	tuple.DesiredTuple = RETURN_FIRST_TUPLE;
14978c2ecf20Sopenharmony_ci	tuple.TupleOffset = 0;
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci	status = pccard_get_first_tuple(s, BIND_FN_ALL, &tuple);
15008c2ecf20Sopenharmony_ci	while (!status) {
15018c2ecf20Sopenharmony_ci		tuple.TupleData = tuplebuffer;
15028c2ecf20Sopenharmony_ci		tuple.TupleDataMax = 255;
15038c2ecf20Sopenharmony_ci		memset(tuplebuffer, 0, sizeof(u_char) * 255);
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci		status = pccard_get_tuple_data(s, &tuple);
15068c2ecf20Sopenharmony_ci		if (status)
15078c2ecf20Sopenharmony_ci			break;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci		if (off < (pointer + 2 + tuple.TupleDataLen)) {
15108c2ecf20Sopenharmony_ci			tempbuffer[0] = tuple.TupleCode & 0xff;
15118c2ecf20Sopenharmony_ci			tempbuffer[1] = tuple.TupleLink & 0xff;
15128c2ecf20Sopenharmony_ci			for (i = 0; i < tuple.TupleDataLen; i++)
15138c2ecf20Sopenharmony_ci				tempbuffer[i + 2] = tuplebuffer[i] & 0xff;
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci			for (i = 0; i < (2 + tuple.TupleDataLen); i++) {
15168c2ecf20Sopenharmony_ci				if (((i + pointer) >= off) &&
15178c2ecf20Sopenharmony_ci				    (i + pointer) < (off + count)) {
15188c2ecf20Sopenharmony_ci					buf[ret] = tempbuffer[i];
15198c2ecf20Sopenharmony_ci					ret++;
15208c2ecf20Sopenharmony_ci				}
15218c2ecf20Sopenharmony_ci			}
15228c2ecf20Sopenharmony_ci		}
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci		pointer += 2 + tuple.TupleDataLen;
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci		if (pointer >= (off + count))
15278c2ecf20Sopenharmony_ci			break;
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci		if (tuple.TupleCode == CISTPL_END)
15308c2ecf20Sopenharmony_ci			break;
15318c2ecf20Sopenharmony_ci		status = pccard_get_next_tuple(s, BIND_FN_ALL, &tuple);
15328c2ecf20Sopenharmony_ci	}
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci	kfree(tempbuffer);
15358c2ecf20Sopenharmony_ci free_tuple:
15368c2ecf20Sopenharmony_ci	kfree(tuplebuffer);
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci	return ret;
15398c2ecf20Sopenharmony_ci}
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_cistatic ssize_t pccard_show_cis(struct file *filp, struct kobject *kobj,
15438c2ecf20Sopenharmony_ci			       struct bin_attribute *bin_attr,
15448c2ecf20Sopenharmony_ci			       char *buf, loff_t off, size_t count)
15458c2ecf20Sopenharmony_ci{
15468c2ecf20Sopenharmony_ci	unsigned int size = 0x200;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	if (off >= size)
15498c2ecf20Sopenharmony_ci		count = 0;
15508c2ecf20Sopenharmony_ci	else {
15518c2ecf20Sopenharmony_ci		struct pcmcia_socket *s;
15528c2ecf20Sopenharmony_ci		unsigned int chains = 1;
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci		if (off + count > size)
15558c2ecf20Sopenharmony_ci			count = size - off;
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci		s = to_socket(container_of(kobj, struct device, kobj));
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci		if (!(s->state & SOCKET_PRESENT))
15608c2ecf20Sopenharmony_ci			return -ENODEV;
15618c2ecf20Sopenharmony_ci		if (!s->functions && pccard_validate_cis(s, &chains))
15628c2ecf20Sopenharmony_ci			return -EIO;
15638c2ecf20Sopenharmony_ci		if (!chains)
15648c2ecf20Sopenharmony_ci			return -ENODATA;
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci		count = pccard_extract_cis(s, buf, off, count);
15678c2ecf20Sopenharmony_ci	}
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	return count;
15708c2ecf20Sopenharmony_ci}
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_cistatic ssize_t pccard_store_cis(struct file *filp, struct kobject *kobj,
15748c2ecf20Sopenharmony_ci				struct bin_attribute *bin_attr,
15758c2ecf20Sopenharmony_ci				char *buf, loff_t off, size_t count)
15768c2ecf20Sopenharmony_ci{
15778c2ecf20Sopenharmony_ci	struct pcmcia_socket *s;
15788c2ecf20Sopenharmony_ci	int error;
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	error = security_locked_down(LOCKDOWN_PCMCIA_CIS);
15818c2ecf20Sopenharmony_ci	if (error)
15828c2ecf20Sopenharmony_ci		return error;
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	s = to_socket(container_of(kobj, struct device, kobj));
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci	if (off)
15878c2ecf20Sopenharmony_ci		return -EINVAL;
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci	if (count >= CISTPL_MAX_CIS_SIZE)
15908c2ecf20Sopenharmony_ci		return -EINVAL;
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci	if (!(s->state & SOCKET_PRESENT))
15938c2ecf20Sopenharmony_ci		return -ENODEV;
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci	error = pcmcia_replace_cis(s, buf, count);
15968c2ecf20Sopenharmony_ci	if (error)
15978c2ecf20Sopenharmony_ci		return -EIO;
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci	return count;
16028c2ecf20Sopenharmony_ci}
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_ciconst struct bin_attribute pccard_cis_attr = {
16068c2ecf20Sopenharmony_ci	.attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR },
16078c2ecf20Sopenharmony_ci	.size = 0x200,
16088c2ecf20Sopenharmony_ci	.read = pccard_show_cis,
16098c2ecf20Sopenharmony_ci	.write = pccard_store_cis,
16108c2ecf20Sopenharmony_ci};
1611