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 + -
显示快捷键?