Logo Search packages:      
Sourcecode: pcmcia-cs version File versions  Download package

pnp_rsrc.c

#define __NO_VERSION__

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/pnp_bios.h>
#include <linux/pnp_resource.h>
#include <asm/irq.h>

/* From rsrc_mgr.c */
void request_io_region(u_long base, u_long num, char *name);

/*======================================================================

    PnP interrupt table manager

======================================================================*/

struct irq_entry {
    char *name;
    struct irq_entry *next;
};

static struct irq_entry *irq[NR_IRQS];

int proc_read_irq(char *buf, char **start, off_t pos,
              int count, int *eof, void *data)
{
    int i;
    struct irq_entry *e;
    char *p = buf;
    for (i = 0; i < NR_IRQS; i++) {
      if (!irq[i]) continue;
      p += sprintf(p, "%3d:  ", i);
      for (e = irq[i]; e; e = e->next) {
          strcpy(p, e->name);
          if (e->next)
            strcat(p, ", ");
          p += strlen(p);
      }
      strcat(p, "\n");
      p++;
    }
    return (p - buf);
}

void alloc_pnp_irq(int n, char *name)
{
    struct irq_entry **e = &irq[n];
    while (*e != NULL)
      e = &((*e)->next);
    *e = kmalloc(sizeof(*e), GFP_KERNEL);
    if (*e) {
      (*e)->name = name;
      (*e)->next = NULL;
    }
}

int check_pnp_irq(int n)
{
    return (irq[n] ? -EBUSY : 0);
}

void free_pnp_irqs(void)
{
    int n;
    struct irq_entry *e, *f;
    for (n = 0; n < NR_IRQS; n++)
      for (e = irq[n]; e; e = f) {
          f = e->next;
          kfree(e);
      }
}

/*======================================================================

    PCI device resource enumeration

======================================================================*/

#ifdef CONFIG_PCI

static char *pci_names = NULL;

static int pci_claim_resources(void)
{
    struct pci_dev *dev;
    int r;
    unsigned long flags;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13))
    unsigned long a;
    u32 b, sz, idx;
    u16 cmd, tmp;
#endif
    char *name;

    r = 0; pci_for_each_dev(dev) r++;
    name = pci_names = kmalloc(r*12, GFP_KERNEL);
    if (!name) return -ENOMEM;
    
    save_flags(flags);
    cli();
    pci_for_each_dev(dev) {
      if (dev->hdr_type != PCI_HEADER_TYPE_NORMAL)
          continue;
      sprintf(name, "pci %02x:%02x.%1x", dev->bus->number,
            PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
      if (dev->irq)
          alloc_pnp_irq(dev->irq, name);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13))
      /* Disable IO and memory while we fiddle */
      pci_read_config_word(dev, PCI_COMMAND, &cmd);
      tmp = cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
      pci_write_config_word(dev, PCI_COMMAND, tmp);
      for (idx = 0; idx < 6; idx++) {
          a = dev->base_address[idx];
          r = PCI_BASE_ADDRESS_0 + 4*idx;
          if (((a & PCI_BASE_ADDRESS_SPACE_IO) &&
             !(a & PCI_BASE_ADDRESS_IO_MASK)) ||
            !(a & PCI_BASE_ADDRESS_MEM_MASK))
            continue;
          pci_read_config_dword(dev, r, &b);
          pci_write_config_dword(dev, r, ~0);
          pci_read_config_dword(dev, r, &sz);
          pci_write_config_dword(dev, r, b);
          if (a & PCI_BASE_ADDRESS_SPACE_IO) {
            a &= PCI_BASE_ADDRESS_IO_MASK;
            sz = (~(sz & PCI_BASE_ADDRESS_IO_MASK))+1;
            sz &= 0xffff;
            if (sz <= 0x100)
                request_io_region(a, sz, name);
          } else {
            a &= PCI_BASE_ADDRESS_MEM_MASK;
            sz = (~(sz & PCI_BASE_ADDRESS_MEM_MASK))+1;
            request_mem_region(a, sz, name);
          }
      }
      if (dev->rom_address & ~1) {
          r = PCI_ROM_ADDRESS;
          pci_read_config_dword(dev, r, &b);
          pci_write_config_dword(dev, r, ~0);
          pci_read_config_dword(dev, r, &sz);
          pci_write_config_dword(dev, r, b);
          sz = (~(sz & ~1))+1;
          request_mem_region(dev->rom_address & ~1, sz, name);
      }
      pci_write_config_word(dev, PCI_COMMAND, cmd);
#endif
      name += 12;
    }
    restore_flags(flags);
    return 0;
}

#endif /* CONFIG_PCI */

/*======================================================================

    PnP device resource enumeration
    
======================================================================*/

#define flip16(n) le16_to_cpu(n)
#define flip32(n) le32_to_cpu(n)

static struct pnp_dev_node_info node_info;

static void pnp_scan_node(char *name, u8 *buf, int len)
{
    union pnp_resource *p = (union pnp_resource *)buf;
    int tag = 0, sz;
    
    while (((u8 *)p < buf+len) && (tag != PNP_RES_SMTAG_END)) {
      if (p->lg.tag & PNP_RES_LARGE_ITEM) {
          
          union pnp_large_resource *r = &p->lg.d;
          tag = p->lg.tag & ~PNP_RES_LARGE_ITEM;
          sz = flip16(p->lg.sz) + 3;
          switch (tag) {
          case PNP_RES_LGTAG_MEM:
            if (r->mem.len && (r->mem.min == r->mem.max))
                request_mem_region(flip16(r->mem.min)<<8,
                               flip16(r->mem.len), name);
            break;
          case PNP_RES_LGTAG_MEM32:
            if (r->mem32.len && (r->mem32.min == r->mem32.max))
                request_mem_region(flip32(r->mem32.min),
                               flip32(r->mem32.len), name);
            break;
          case PNP_RES_LGTAG_MEM32_FIXED:
            if (r->mem32_fixed.len)
                request_mem_region(flip32(r->mem32_fixed.base),
                               flip32(r->mem32_fixed.len), name);
            break;
          }
          
      } else {
          
          union pnp_small_resource *r = &p->sm.d;
          tag = (p->sm.tag >> 3); sz = (p->sm.tag & 7) + 1;
          switch (tag) {
          case PNP_RES_SMTAG_IRQ:
            if (r->irq.mask && !(r->irq.mask & (r->irq.mask-1)))
                alloc_pnp_irq(ffs(flip16(r->irq.mask))-1, name);
            break;
          case PNP_RES_SMTAG_IO:
            if (r->io.len && (r->io.min == r->io.max))
                request_io_region(flip16(r->io.min),
                              r->io.len, name);
            break;
          case PNP_RES_SMTAG_IO_FIXED:
            if (r->io_fixed.len)
                request_io_region(flip16(r->io_fixed.base),
                              r->io_fixed.len, name);
            break;
          }
          
      }
      (u8 *)p += sz;
    }
}

static char *pnp_names = NULL;

static int pnp_claim_resources(void)
{
    struct pnp_bios_node *node;
    char *name;
    u8 num;
    
    node = kmalloc(node_info.max_node_size, GFP_KERNEL);
    if (!node) return -ENOMEM;
    pnp_names = kmalloc(node_info.no_nodes*7, GFP_KERNEL);
    if (!pnp_names) {
      kfree(node);
      return -ENOMEM;
    }
    for (name = pnp_names, num = 0; num != 0xff; name += 7) {
      pnp_bios_get_dev_node(&num, 0, node);
      sprintf(name, "pnp %02x", node->handle);
      pnp_scan_node(name, node->data, node->size - sizeof(*node));
    }
    kfree(node);
    return 0;
}

/*====================================================================*/

void pnp_rsrc_init(void)
{
#ifdef CONFIG_PCI
    if (pcibios_present())
      pci_claim_resources();
#endif
    if (pnp_bios_present()) {
      if ((pnp_bios_dev_node_info(&node_info) == 0) &&
          (node_info.no_nodes > 0))
          pnp_claim_resources();
    }
}

void pnp_rsrc_done(void)
{
    if (pnp_names)
      kfree(pnp_names);
    free_pnp_irqs();
#ifdef CONFIG_PCI
    if (pci_names)
      kfree(pci_names);
#endif
}

Generated by  Doxygen 1.6.0   Back to index