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