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