cistpl.c
来自「linux 内核源代码」· C语言 代码 · 共 1,512 行 · 第 1/3 页
C
1,512 行
/* * cistpl.c -- 16-bit PCMCIA Card Information Structure parser * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * The initial developer of the original code is David A. Hinds * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. * * (C) 1999 David A. Hinds */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/major.h>#include <linux/errno.h>#include <linux/timer.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/pci.h>#include <linux/ioport.h>#include <asm/io.h>#include <asm/byteorder.h>#include <asm/unaligned.h>#include <pcmcia/cs_types.h>#include <pcmcia/ss.h>#include <pcmcia/cs.h>#include <pcmcia/bulkmem.h>#include <pcmcia/cisreg.h>#include <pcmcia/cistpl.h>#include "cs_internal.h"static const u_char mantissa[] = { 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90};static const u_int exponent[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000};/* Convert an extended speed byte to a time in nanoseconds */#define SPEED_CVT(v) \ (mantissa[(((v)>>3)&15)-1] * exponent[(v)&7] / 10)/* Convert a power byte to a current in 0.1 microamps */#define POWER_CVT(v) \ (mantissa[((v)>>3)&15] * exponent[(v)&7] / 10)#define POWER_SCALE(v) (exponent[(v)&7])/* Upper limit on reasonable # of tuples */#define MAX_TUPLES 200/*====================================================================*//* Parameters that can be set with 'insmod' *//* 16-bit CIS? */static int cis_width;module_param(cis_width, int, 0444);void release_cis_mem(struct pcmcia_socket *s){ if (s->cis_mem.flags & MAP_ACTIVE) { s->cis_mem.flags &= ~MAP_ACTIVE; s->ops->set_mem_map(s, &s->cis_mem); if (s->cis_mem.res) { release_resource(s->cis_mem.res); kfree(s->cis_mem.res); s->cis_mem.res = NULL; } iounmap(s->cis_virt); s->cis_virt = NULL; }}EXPORT_SYMBOL(release_cis_mem);/* * Map the card memory at "card_offset" into virtual space. * If flags & MAP_ATTRIB, map the attribute space, otherwise * map the memory space. */static void __iomem *set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags){ pccard_mem_map *mem = &s->cis_mem; int ret; if (!(s->features & SS_CAP_STATIC_MAP) && (mem->res == NULL)) { mem->res = pcmcia_find_mem_region(0, s->map_size, s->map_size, 0, s); if (mem->res == NULL) { printk(KERN_NOTICE "cs: unable to map card memory!\n"); return NULL; } s->cis_virt = NULL; } if (!(s->features & SS_CAP_STATIC_MAP) && (!s->cis_virt)) s->cis_virt = ioremap(mem->res->start, s->map_size); mem->card_start = card_offset; mem->flags = flags; ret = s->ops->set_mem_map(s, mem); if (ret) { iounmap(s->cis_virt); s->cis_virt = NULL; return NULL; } if (s->features & SS_CAP_STATIC_MAP) { if (s->cis_virt) iounmap(s->cis_virt); s->cis_virt = ioremap(mem->static_start, s->map_size); } return s->cis_virt;}/*====================================================================== Low-level functions to read and write CIS memory. I think the write routine is only useful for writing one-byte registers. ======================================================================*//* Bits in attr field */#define IS_ATTR 1#define IS_INDIRECT 8int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr){ void __iomem *sys, *end; unsigned char *buf = ptr; cs_dbg(s, 3, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len); if (attr & IS_INDIRECT) { /* Indirect accesses use a bunch of special registers at fixed locations in common memory */ u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN; if (attr & IS_ATTR) { addr *= 2; flags = ICTRL0_AUTOINC; } sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0)); if (!sys) { memset(ptr, 0xff, len); return -1; } writeb(flags, sys+CISREG_ICTRL0); writeb(addr & 0xff, sys+CISREG_IADDR0); writeb((addr>>8) & 0xff, sys+CISREG_IADDR1); writeb((addr>>16) & 0xff, sys+CISREG_IADDR2); writeb((addr>>24) & 0xff, sys+CISREG_IADDR3); for ( ; len > 0; len--, buf++) *buf = readb(sys+CISREG_IDATA0); } else { u_int inc = 1, card_offset, flags; flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0); if (attr) { flags |= MAP_ATTRIB; inc++; addr *= 2; } card_offset = addr & ~(s->map_size-1); while (len) { sys = set_cis_map(s, card_offset, flags); if (!sys) { memset(ptr, 0xff, len); return -1; } end = sys + s->map_size; sys = sys + (addr & (s->map_size-1)); for ( ; len > 0; len--, buf++, sys += inc) { if (sys == end) break; *buf = readb(sys); } card_offset += s->map_size; addr = 0; } } cs_dbg(s, 3, " %#2.2x %#2.2x %#2.2x %#2.2x ...\n", *(u_char *)(ptr+0), *(u_char *)(ptr+1), *(u_char *)(ptr+2), *(u_char *)(ptr+3)); return 0;}EXPORT_SYMBOL(pcmcia_read_cis_mem);void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr){ void __iomem *sys, *end; unsigned char *buf = ptr; cs_dbg(s, 3, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len); if (attr & IS_INDIRECT) { /* Indirect accesses use a bunch of special registers at fixed locations in common memory */ u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN; if (attr & IS_ATTR) { addr *= 2; flags = ICTRL0_AUTOINC; } sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0)); if (!sys) return; /* FIXME: Error */ writeb(flags, sys+CISREG_ICTRL0); writeb(addr & 0xff, sys+CISREG_IADDR0); writeb((addr>>8) & 0xff, sys+CISREG_IADDR1); writeb((addr>>16) & 0xff, sys+CISREG_IADDR2); writeb((addr>>24) & 0xff, sys+CISREG_IADDR3); for ( ; len > 0; len--, buf++) writeb(*buf, sys+CISREG_IDATA0); } else { u_int inc = 1, card_offset, flags; flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0); if (attr & IS_ATTR) { flags |= MAP_ATTRIB; inc++; addr *= 2; } card_offset = addr & ~(s->map_size-1); while (len) { sys = set_cis_map(s, card_offset, flags); if (!sys) return; /* FIXME: error */ end = sys + s->map_size; sys = sys + (addr & (s->map_size-1)); for ( ; len > 0; len--, buf++, sys += inc) { if (sys == end) break; writeb(*buf, sys); } card_offset += s->map_size; addr = 0; } }}EXPORT_SYMBOL(pcmcia_write_cis_mem);/*====================================================================== This is a wrapper around read_cis_mem, with the same interface, but which caches information, for cards whose CIS may not be readable all the time. ======================================================================*/static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr){ struct cis_cache_entry *cis; int ret; if (s->fake_cis) { if (s->fake_cis_len > addr+len) memcpy(ptr, s->fake_cis+addr, len); else memset(ptr, 0xff, len); return; } list_for_each_entry(cis, &s->cis_cache, node) { if (cis->addr == addr && cis->len == len && cis->attr == attr) { memcpy(ptr, cis->cache, len); return; } }#ifdef CONFIG_CARDBUS if (s->state & SOCKET_CARDBUS) ret = read_cb_mem(s, attr, addr, len, ptr); else#endif ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr); if (ret == 0) { /* Copy data into the cache */ cis = kmalloc(sizeof(struct cis_cache_entry) + len, GFP_KERNEL); if (cis) { cis->addr = addr; cis->len = len; cis->attr = attr; memcpy(cis->cache, ptr, len); list_add(&cis->node, &s->cis_cache); } }}static voidremove_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len){ struct cis_cache_entry *cis; list_for_each_entry(cis, &s->cis_cache, node) if (cis->addr == addr && cis->len == len && cis->attr == attr) { list_del(&cis->node); kfree(cis); break; }}void destroy_cis_cache(struct pcmcia_socket *s){ struct list_head *l, *n; list_for_each_safe(l, n, &s->cis_cache) { struct cis_cache_entry *cis = list_entry(l, struct cis_cache_entry, node); list_del(&cis->node); kfree(cis); } /* * If there was a fake CIS, destroy that as well. */ kfree(s->fake_cis); s->fake_cis = NULL;}EXPORT_SYMBOL(destroy_cis_cache);/*====================================================================== This verifies if the CIS of a card matches what is in the CIS cache. ======================================================================*/int verify_cis_cache(struct pcmcia_socket *s){ struct cis_cache_entry *cis; char *buf; buf = kmalloc(256, GFP_KERNEL); if (buf == NULL) return -1; list_for_each_entry(cis, &s->cis_cache, node) { int len = cis->len; if (len > 256) len = 256;#ifdef CONFIG_CARDBUS if (s->state & SOCKET_CARDBUS) read_cb_mem(s, cis->attr, cis->addr, len, buf); else#endif pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf); if (memcmp(buf, cis->cache, len) != 0) { kfree(buf); return -1; } } kfree(buf); return 0;}/*====================================================================== For really bad cards, we provide a facility for uploading a replacement CIS. ======================================================================*/int pcmcia_replace_cis(struct pcmcia_socket *s, cisdump_t *cis){ kfree(s->fake_cis); s->fake_cis = NULL; if (cis->Length > CISTPL_MAX_CIS_SIZE) return CS_BAD_SIZE; s->fake_cis = kmalloc(cis->Length, GFP_KERNEL); if (s->fake_cis == NULL) return CS_OUT_OF_RESOURCE; s->fake_cis_len = cis->Length; memcpy(s->fake_cis, cis->Data, cis->Length); return CS_SUCCESS;}EXPORT_SYMBOL(pcmcia_replace_cis);/*====================================================================== The high-level CIS tuple services ======================================================================*/static inline u16 cis_get_u16(void *ptr){ return le16_to_cpu(get_unaligned((__le16 *) ptr));}static inline u32 cis_get_u32(void *ptr){ return le32_to_cpu(get_unaligned((__le32 *) ptr));}typedef struct tuple_flags { u_int link_space:4; u_int has_link:1; u_int mfc_fn:3; u_int space:4;} tuple_flags;#define LINK_SPACE(f) (((tuple_flags *)(&(f)))->link_space)#define HAS_LINK(f) (((tuple_flags *)(&(f)))->has_link)#define MFC_FN(f) (((tuple_flags *)(&(f)))->mfc_fn)#define SPACE(f) (((tuple_flags *)(&(f)))->space)int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int func, tuple_t *tuple);int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple_t *tuple){ if (!s) return CS_BAD_HANDLE; if (!(s->state & SOCKET_PRESENT)) return CS_NO_CARD; tuple->TupleLink = tuple->Flags = 0;#ifdef CONFIG_CARDBUS if (s->state & SOCKET_CARDBUS) { struct pci_dev *dev = s->cb_dev; u_int ptr; pci_bus_read_config_dword(dev->subordinate, 0, PCI_CARDBUS_CIS, &ptr); tuple->CISOffset = ptr & ~7; SPACE(tuple->Flags) = (ptr & 7); } else#endif { /* Assume presence of a LONGLINK_C to address 0 */ tuple->CISOffset = tuple->LinkOffset = 0; SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1; } if (!(s->state & SOCKET_CARDBUS) && (s->functions > 1) && !(tuple->Attributes & TUPLE_RETURN_COMMON)) { cisdata_t req = tuple->DesiredTuple; tuple->DesiredTuple = CISTPL_LONGLINK_MFC; if (pccard_get_next_tuple(s, function, tuple) == CS_SUCCESS) { tuple->DesiredTuple = CISTPL_LINKTARGET; if (pccard_get_next_tuple(s, function, tuple) != CS_SUCCESS) return CS_NO_MORE_ITEMS; } else tuple->CISOffset = tuple->TupleLink = 0; tuple->DesiredTuple = req; } return pccard_get_next_tuple(s, function, tuple);}EXPORT_SYMBOL(pccard_get_first_tuple);static int follow_link(struct pcmcia_socket *s, tuple_t *tuple){ u_char link[5]; u_int ofs; if (MFC_FN(tuple->Flags)) { /* Get indirect link from the MFC tuple */ read_cis_cache(s, LINK_SPACE(tuple->Flags), tuple->LinkOffset, 5, link); ofs = cis_get_u32(link + 1); SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR); /* Move to the next indirect link */ tuple->LinkOffset += 5; MFC_FN(tuple->Flags)--; } else if (HAS_LINK(tuple->Flags)) { ofs = tuple->LinkOffset; SPACE(tuple->Flags) = LINK_SPACE(tuple->Flags); HAS_LINK(tuple->Flags) = 0; } else { return -1; } if (!(s->state & SOCKET_CARDBUS) && SPACE(tuple->Flags)) { /* This is ugly, but a common CIS error is to code the long link offset incorrectly, so we check the right spot... */ read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link); if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) && (strncmp(link+2, "CIS", 3) == 0)) return ofs; remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5); /* Then, we try the wrong spot... */ ofs = ofs >> 1; } read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link); if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) && (strncmp(link+2, "CIS", 3) == 0)) return ofs; remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5); return -1;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?