rsrc_nonstatic.c

来自「linux 内核源代码」· C语言 代码 · 共 1,065 行 · 第 1/2 页

C
1,065
字号
/* * rsrc_nonstatic.c -- Resource management routines for !SS_CAP_STATIC_MAP sockets * * 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/init.h>#include <linux/interrupt.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/ioport.h>#include <linux/timer.h>#include <linux/pci.h>#include <linux/device.h>#include <asm/irq.h>#include <asm/io.h>#include <pcmcia/cs_types.h>#include <pcmcia/ss.h>#include <pcmcia/cs.h>#include <pcmcia/bulkmem.h>#include <pcmcia/cistpl.h>#include "cs_internal.h"MODULE_AUTHOR("David A. Hinds, Dominik Brodowski");MODULE_LICENSE("GPL");/* Parameters that can be set with 'insmod' */#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)INT_MODULE_PARM(probe_mem,	1);		/* memory probe? */#ifdef CONFIG_PCMCIA_PROBEINT_MODULE_PARM(probe_io,	1);		/* IO port probe? */INT_MODULE_PARM(mem_limit,	0x10000);#endif/* for io_db and mem_db */struct resource_map {	u_long			base, num;	struct resource_map	*next;};struct socket_data {	struct resource_map		mem_db;	struct resource_map		io_db;	unsigned int			rsrc_mem_probe;};static DEFINE_MUTEX(rsrc_mutex);#define MEM_PROBE_LOW	(1 << 0)#define MEM_PROBE_HIGH	(1 << 1)/*======================================================================    Linux resource management extensions======================================================================*/static struct resource *make_resource(resource_size_t b, resource_size_t n, int flags, char *name){	struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);	if (res) {		res->name = name;		res->start = b;		res->end = b + n - 1;		res->flags = flags;	}	return res;}static struct resource *claim_region(struct pcmcia_socket *s, resource_size_t base,		resource_size_t size, int type, char *name){	struct resource *res, *parent;	parent = type & IORESOURCE_MEM ? &iomem_resource : &ioport_resource;	res = make_resource(base, size, type | IORESOURCE_BUSY, name);	if (res) {#ifdef CONFIG_PCI		if (s && s->cb_dev)			parent = pci_find_parent_resource(s->cb_dev, res);#endif		if (!parent || request_resource(parent, res)) {			kfree(res);			res = NULL;		}	}	return res;}static void free_region(struct resource *res){	if (res) {		release_resource(res);		kfree(res);	}}/*======================================================================    These manage the internal databases of available resources.======================================================================*/static int add_interval(struct resource_map *map, u_long base, u_long num){    struct resource_map *p, *q;    for (p = map; ; p = p->next) {	if ((p != map) && (p->base+p->num-1 >= base))	    return -1;	if ((p->next == map) || (p->next->base > base+num-1))	    break;    }    q = kmalloc(sizeof(struct resource_map), GFP_KERNEL);    if (!q) return CS_OUT_OF_RESOURCE;    q->base = base; q->num = num;    q->next = p->next; p->next = q;    return CS_SUCCESS;}/*====================================================================*/static int sub_interval(struct resource_map *map, u_long base, u_long num){    struct resource_map *p, *q;    for (p = map; ; p = q) {	q = p->next;	if (q == map)	    break;	if ((q->base+q->num > base) && (base+num > q->base)) {	    if (q->base >= base) {		if (q->base+q->num <= base+num) {		    /* Delete whole block */		    p->next = q->next;		    kfree(q);		    /* don't advance the pointer yet */		    q = p;		} else {		    /* Cut off bit from the front */		    q->num = q->base + q->num - base - num;		    q->base = base + num;		}	    } else if (q->base+q->num <= base+num) {		/* Cut off bit from the end */		q->num = base - q->base;	    } else {		/* Split the block into two pieces */		p = kmalloc(sizeof(struct resource_map), GFP_KERNEL);		if (!p) return CS_OUT_OF_RESOURCE;		p->base = base+num;		p->num = q->base+q->num - p->base;		q->num = base - q->base;		p->next = q->next ; q->next = p;	    }	}    }    return CS_SUCCESS;}/*======================================================================    These routines examine a region of IO or memory addresses to    determine what ranges might be genuinely available.======================================================================*/#ifdef CONFIG_PCMCIA_PROBEstatic void do_io_probe(struct pcmcia_socket *s, kio_addr_t base, kio_addr_t num){    struct resource *res;    struct socket_data *s_data = s->resource_data;    kio_addr_t i, j, bad;    int any;    u_char *b, hole, most;    printk(KERN_INFO "cs: IO port probe %#lx-%#lx:",	   base, base+num-1);    /* First, what does a floating port look like? */    b = kzalloc(256, GFP_KERNEL);    if (!b) {            printk(KERN_ERR "do_io_probe: unable to kmalloc 256 bytes");            return;    }    for (i = base, most = 0; i < base+num; i += 8) {	res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA IO probe");	if (!res)	    continue;	hole = inb(i);	for (j = 1; j < 8; j++)	    if (inb(i+j) != hole) break;	free_region(res);	if ((j == 8) && (++b[hole] > b[most]))	    most = hole;	if (b[most] == 127) break;    }    kfree(b);    bad = any = 0;    for (i = base; i < base+num; i += 8) {	res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA IO probe");	if (!res)	    continue;	for (j = 0; j < 8; j++)	    if (inb(i+j) != most) break;	free_region(res);	if (j < 8) {	    if (!any)		printk(" excluding");	    if (!bad)		bad = any = i;	} else {	    if (bad) {		sub_interval(&s_data->io_db, bad, i-bad);		printk(" %#lx-%#lx", bad, i-1);		bad = 0;	    }	}    }    if (bad) {	if ((num > 16) && (bad == base) && (i == base+num)) {	    printk(" nothing: probe failed.\n");	    return;	} else {	    sub_interval(&s_data->io_db, bad, i-bad);	    printk(" %#lx-%#lx", bad, i-1);	}    }    printk(any ? "\n" : " clean.\n");}#endif/*======================================================================    This is tricky... when we set up CIS memory, we try to validate    the memory window space allocations.======================================================================*//* Validation function for cards with a valid CIS */static int readable(struct pcmcia_socket *s, struct resource *res, cisinfo_t *info){	int ret = -1;	s->cis_mem.res = res;	s->cis_virt = ioremap(res->start, s->map_size);	if (s->cis_virt) {		ret = pccard_validate_cis(s, BIND_FN_ALL, info);		/* invalidate mapping and CIS cache */		iounmap(s->cis_virt);		s->cis_virt = NULL;		destroy_cis_cache(s);	}	s->cis_mem.res = NULL;	if ((ret != 0) || (info->Chains == 0))		return 0;	return 1;}/* Validation function for simple memory cards */static int checksum(struct pcmcia_socket *s, struct resource *res){	pccard_mem_map map;	int i, a = 0, b = -1, d;	void __iomem *virt;	virt = ioremap(res->start, s->map_size);	if (virt) {		map.map = 0;		map.flags = MAP_ACTIVE;		map.speed = 0;		map.res = res;		map.card_start = 0;		s->ops->set_mem_map(s, &map);		/* Don't bother checking every word... */		for (i = 0; i < s->map_size; i += 44) {			d = readl(virt+i);			a += d;			b &= d;		}		map.flags = 0;		s->ops->set_mem_map(s, &map);		iounmap(virt);	}	return (b == -1) ? -1 : (a>>1);}static intcis_readable(struct pcmcia_socket *s, unsigned long base, unsigned long size){	struct resource *res1, *res2;	cisinfo_t info1, info2;	int ret = 0;	res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "cs memory probe");	res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, "cs memory probe");	if (res1 && res2) {		ret = readable(s, res1, &info1);		ret += readable(s, res2, &info2);	}	free_region(res2);	free_region(res1);	return (ret == 2) && (info1.Chains == info2.Chains);}static intchecksum_match(struct pcmcia_socket *s, unsigned long base, unsigned long size){	struct resource *res1, *res2;	int a = -1, b = -1;	res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "cs memory probe");	res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, "cs memory probe");	if (res1 && res2) {		a = checksum(s, res1);		b = checksum(s, res2);	}	free_region(res2);	free_region(res1);	return (a == b) && (a >= 0);}/*======================================================================    The memory probe.  If the memory list includes a 64K-aligned block    below 1MB, we probe in 64K chunks, and as soon as we accumulate at    least mem_limit free space, we quit.======================================================================*/static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s){    struct socket_data *s_data = s->resource_data;    u_long i, j, bad, fail, step;    printk(KERN_INFO "cs: memory probe 0x%06lx-0x%06lx:",	   base, base+num-1);    bad = fail = 0;    step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);    /* don't allow too large steps */    if (step > 0x800000)	step = 0x800000;    /* cis_readable wants to map 2x map_size */    if (step < 2 * s->map_size)	step = 2 * s->map_size;    for (i = j = base; i < base+num; i = j + step) {	if (!fail) {	    for (j = i; j < base+num; j += step) {		if (cis_readable(s, j, step))		    break;	    }	    fail = ((i == base) && (j == base+num));	}	if (fail) {	    for (j = i; j < base+num; j += 2*step)		if (checksum_match(s, j, step) &&		    checksum_match(s, j + step, step))		    break;	}	if (i != j) {	    if (!bad) printk(" excluding");	    printk(" %#05lx-%#05lx", i, j-1);	    sub_interval(&s_data->mem_db, i, j-i);	    bad += j-i;	}    }    printk(bad ? "\n" : " clean.\n");    return (num - bad);}#ifdef CONFIG_PCMCIA_PROBEstatic u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s){	struct socket_data *s_data = s->resource_data;	u_long ok;	if (m == &s_data->mem_db)		return 0;	ok = inv_probe(m->next, s);	if (ok) {		if (m->base >= 0x100000)			sub_interval(&s_data->mem_db, m->base, m->num);		return ok;	}	if (m->base < 0x100000)		return 0;	return do_mem_probe(m->base, m->num, s);}static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask){	struct resource_map *m, mm;	static unsigned char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };	unsigned long b, i, ok = 0;	struct socket_data *s_data = s->resource_data;	/* We do up to four passes through the list */	if (probe_mask & MEM_PROBE_HIGH) {		if (inv_probe(s_data->mem_db.next, s) > 0)			return 0;		printk(KERN_NOTICE "cs: warning: no high memory space "		       "available!\n");		return -ENODEV;	}	for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {		mm = *m;		/* Only probe < 1 MB */		if (mm.base >= 0x100000)			continue;		if ((mm.base | mm.num) & 0xffff) {			ok += do_mem_probe(mm.base, mm.num, s);			continue;		}		/* Special probe for 64K-aligned block */		for (i = 0; i < 4; i++) {			b = order[i] << 12;			if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.num)) {				if (ok >= mem_limit)					sub_interval(&s_data->mem_db, b, 0x10000);				else					ok += do_mem_probe(b, 0x10000, s);			}		}	}	if (ok > 0)		return 0;	return -ENODEV;}#else /* CONFIG_PCMCIA_PROBE */static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask){	struct resource_map *m, mm;	struct socket_data *s_data = s->resource_data;	unsigned long ok = 0;	for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {		mm = *m;		ok += do_mem_probe(mm.base, mm.num, s);	}	if (ok > 0)		return 0;	return -ENODEV;}#endif /* CONFIG_PCMCIA_PROBE *//* * Locking note: Must be called with skt_mutex held! */static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s){	struct socket_data *s_data = s->resource_data;	unsigned int probe_mask = MEM_PROBE_LOW;	int ret = 0;	if (!probe_mem)		return 0;	mutex_lock(&rsrc_mutex);	if (s->features & SS_CAP_PAGE_REGS)		probe_mask = MEM_PROBE_HIGH;	if (probe_mask & ~s_data->rsrc_mem_probe) {		if (s->state & SOCKET_PRESENT)			ret = validate_mem(s, probe_mask);		if (!ret)			s_data->rsrc_mem_probe |= probe_mask;	}	mutex_unlock(&rsrc_mutex);	return ret;}struct pcmcia_align_data {	unsigned long	mask;	unsigned long	offset;	struct resource_map	*map;};static voidpcmcia_common_align(void *align_data, struct resource *res,			resource_size_t size, resource_size_t align){	struct pcmcia_align_data *data = align_data;	resource_size_t start;	/*	 * Ensure that we have the correct start address	 */	start = (res->start & ~data->mask) + data->offset;	if (start < res->start)		start += data->mask + 1;	res->start = start;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?