Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 1627 → Rev 1628

/drivers/devman/Makefile
1,4 → 1,5
 
 
CC = gcc
FASM = e:/fasm/fasm.exe
 
27,7 → 28,10
NAME_SRCS= acpi.c \
scan.c \
pci_irq.c \
pci/pci.c
pci_root.c \
pci/pci.c \
pci/probe.c \
pci_bind.c
 
 
all: $(NAME).dll
/drivers/devman/acpi.c
110,30 → 110,13
};
 
 
/*
int acpi_pci_bind_root(struct acpi_device *device)
{
device->ops.bind = acpi_pci_bind;
device->ops.unbind = acpi_pci_unbind;
 
return 0;
}
*/
 
static bool pci_use_crs = false;
 
#define IORESOURCE_BUS 0x00001000
 
struct acpi_pci_root {
struct list_head node;
struct acpi_device * device;
struct acpi_pci_id id;
struct pci_bus *bus;
u16 segment;
struct resource secondary; /* downstream bus range */
 
};
 
static LIST_HEAD(acpi_pci_roots);
 
#define ACPI_PCI_ROOT_CLASS "pci_bridge"
179,6 → 162,23
}
 
 
static void acpi_pci_bridge_scan(struct acpi_device *device)
{
int status;
struct acpi_device *child = NULL;
 
if (device->flags.bus_address)
if (device->parent && device->parent->ops.bind) {
status = device->parent->ops.bind(device);
if (!status) {
list_for_each_entry(child, &device->children, node)
acpi_pci_bridge_scan(child);
}
}
}
 
 
 
struct pci_root_info
{
struct acpi_device *bridge;
416,7 → 416,7
bus = pci_create_bus(busnum, &pci_root_ops, sd);
if (bus) {
get_current_resources(device, busnum, domain, bus);
// bus->subordinate = pci_scan_child_bus(bus);
bus->subordinate = pci_scan_child_bus(bus);
}
}
 
528,9 → 528,9
* -----------------------
* Thus binding the ACPI and PCI devices.
*/
// result = acpi_pci_bind_root(device);
// if (result)
// goto end;
result = acpi_pci_bind_root(device);
if (result)
goto end;
 
/*
* PCI Routing Table
544,8 → 544,8
/*
* Scan and bind all _ADR-Based Devices
*/
// list_for_each_entry(child, &device->children, node)
// acpi_pci_bridge_scan(child);
list_for_each_entry(child, &device->children, node)
acpi_pci_bridge_scan(child);
 
return 0;
 
762,72 → 762,7
};
 
#if 0
scan_devices();
 
{
bool retval = false;
u32_t bus, last_bus;
 
if( (last_bus = PciApi(1))==-1)
return retval;
 
dbgprintf("last bus %x\n", last_bus);
 
for(bus=0; bus <= last_bus; bus++)
{
u32_t dev;
 
for(dev = 0; dev < 32; dev++)
{
u32_t fn;
 
for(fn = 0; fn < 8; fn++)
{
 
u32_t id;
u32_t irq_bios, irq_acpi;
u32_t irq_pin;
u16_t pcicmd;
u32_t tmp;
 
u32_t devfn = (dev<<3 )|fn;
 
id = PciRead32(bus,devfn, PCI_VENDOR_ID);
 
/* some broken boards return 0 or ~0 if a slot is empty: */
if (id == 0xffffffff || id == 0x00000000 ||
id == 0x0000ffff || id == 0xffff0000)
continue;
 
pcicmd = PciRead16(bus,devfn, PCI_COMMAND);
if (! pcicmd & PCI_COMMAND_IO)
continue;
 
tmp = PciRead32(bus,devfn, 0x3C);
 
irq_bios = tmp & 0xFF;
irq_pin = (tmp >> 8) & 0xFF;
 
int slot = (fn >> 3) & 0x1f;
 
irq_acpi = irqtable[ dev * PCI_MAX_PINS +(irq_pin-1) ];
 
if( irq_acpi < 0)
dbgprintf("PCI: no ACPI IRQ routing for "
"device %d.%d.%d INT%c\n",bus,dev,fn,'A'+irq_pin-1);
 
dbgprintf("pci device %x_%x bus %d dev %d fn %d,"
"IRQ PIN %d BIOS IRQ %d ACPI IRQ %d\n",
id & 0xFFFF, id>>16, bus, dev, fn, irq_pin, irq_bios, irq_acpi);
};
}
};
};
#endif
 
 
#if 0
 
ACPI_STATUS
get_device_by_hid_callback(ACPI_HANDLE obj, u32_t depth, void* context,
void** retval)
/drivers/devman/acpi_bus.h
1,5 → 1,38
 
struct acpi_device;
 
 
/*
* ACPI Driver
* -----------
*/
 
typedef int (*acpi_op_add) (struct acpi_device * device);
typedef int (*acpi_op_remove) (struct acpi_device * device, int type);
typedef int (*acpi_op_start) (struct acpi_device * device);
//typedef int (*acpi_op_suspend) (struct acpi_device * device,
// pm_message_t state);
typedef int (*acpi_op_resume) (struct acpi_device * device);
typedef int (*acpi_op_bind) (struct acpi_device * device);
typedef int (*acpi_op_unbind) (struct acpi_device * device);
typedef void (*acpi_op_notify) (struct acpi_device * device, u32 event);
 
struct acpi_bus_ops {
u32 acpi_op_add:1;
u32 acpi_op_start:1;
};
 
struct acpi_device_ops {
acpi_op_add add;
acpi_op_remove remove;
acpi_op_start start;
// acpi_op_suspend suspend;
acpi_op_resume resume;
acpi_op_bind bind;
acpi_op_unbind unbind;
acpi_op_notify notify;
};
 
struct resource_list {
struct resource_list *next;
struct resource *res;
36,13 → 69,7
#define ACPI_SMBUS_IBM_HID "SMBUSIBM"
 
 
struct acpi_bus_ops
{
u32_t acpi_op_add:1;
u32_t acpi_op_start:1;
};
 
 
#define ACPI_ID_LEN 16 /* only 9 bytes needed here, 16 bytes are used */
/* to workaround crosscompile issues */
 
106,7 → 133,7
// struct acpi_device_wakeup wakeup;
// struct acpi_device_perf performance;
// struct acpi_device_dir dir;
// struct acpi_device_ops ops;
struct acpi_device_ops ops;
// struct acpi_driver *driver;
void *driver_data;
// struct device dev;
115,7 → 142,17
};
 
 
struct acpi_pci_root {
struct list_head node;
struct acpi_device * device;
struct acpi_pci_id id;
struct pci_bus *bus;
u16 segment;
struct resource secondary; /* downstream bus range */
 
};
 
 
#define acpi_device_bid(d) ((d)->pnp.bus_id)
#define acpi_device_adr(d) ((d)->pnp.bus_address)
char *acpi_device_hid(struct acpi_device *device);
126,3 → 163,7
const struct acpi_device_ids *ids);
 
int acpi_pci_irq_add_prt(ACPI_HANDLE handle, struct pci_bus *bus);
int acpi_pci_bind_root(struct acpi_device *device);
struct pci_dev *acpi_get_pci_dev(ACPI_HANDLE handle);
int acpi_is_root_bridge(ACPI_HANDLE handle);
 
/drivers/devman/pci/pci.c
8,6 → 8,7
LIST_HEAD(pci_root_buses);
 
#define IO_SPACE_LIMIT 0xffff
#define PCIBIOS_SUCCESSFUL 0x00
 
struct resource ioport_resource = {
.name = "PCI IO",
23,13 → 24,90
.flags = IORESOURCE_MEM,
};
 
#define PCI_FIND_CAP_TTL 48
 
static inline int pci_domain_nr(struct pci_bus *bus)
static int __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn,
u8 pos, int cap, int *ttl)
{
struct pci_sysdata *sd = bus->sysdata;
return sd->domain;
u8 id;
 
while ((*ttl)--) {
pci_bus_read_config_byte(bus, devfn, pos, &pos);
if (pos < 0x40)
break;
pos &= ~3;
pci_bus_read_config_byte(bus, devfn, pos + PCI_CAP_LIST_ID,
&id);
if (id == 0xff)
break;
if (id == cap)
return pos;
pos += PCI_CAP_LIST_NEXT;
}
return 0;
}
 
static int __pci_find_next_cap(struct pci_bus *bus, unsigned int devfn,
u8 pos, int cap)
{
int ttl = PCI_FIND_CAP_TTL;
 
return __pci_find_next_cap_ttl(bus, devfn, pos, cap, &ttl);
}
static int __pci_bus_find_cap_start(struct pci_bus *bus,
unsigned int devfn, u8 hdr_type)
{
u16 status;
 
pci_bus_read_config_word(bus, devfn, PCI_STATUS, &status);
if (!(status & PCI_STATUS_CAP_LIST))
return 0;
 
switch (hdr_type) {
case PCI_HEADER_TYPE_NORMAL:
case PCI_HEADER_TYPE_BRIDGE:
return PCI_CAPABILITY_LIST;
case PCI_HEADER_TYPE_CARDBUS:
return PCI_CB_CAPABILITY_LIST;
default:
return 0;
}
 
return 0;
}
 
 
/**
* pci_find_capability - query for devices' capabilities
* @dev: PCI device to query
* @cap: capability code
*
* Tell if a device supports a given PCI capability.
* Returns the address of the requested capability structure within the
* device's PCI configuration space or 0 in case the device does not
* support it. Possible values for @cap:
*
* %PCI_CAP_ID_PM Power Management
* %PCI_CAP_ID_AGP Accelerated Graphics Port
* %PCI_CAP_ID_VPD Vital Product Data
* %PCI_CAP_ID_SLOTID Slot Identification
* %PCI_CAP_ID_MSI Message Signalled Interrupts
* %PCI_CAP_ID_CHSWP CompactPCI HotSwap
* %PCI_CAP_ID_PCIX PCI-X
* %PCI_CAP_ID_EXP PCI Express
*/
int pci_find_capability(struct pci_dev *dev, int cap)
{
int pos;
 
pos = __pci_bus_find_cap_start(dev->bus, dev->devfn, dev->hdr_type);
if (pos)
pos = __pci_find_next_cap(dev->bus, dev->devfn, pos, cap);
 
return pos;
}
 
 
static struct pci_bus * pci_alloc_bus(void)
{
struct pci_bus *b;
149,3 → 227,92
}
 
 
/**
* pci_get_slot - locate PCI device for a given PCI slot
* @bus: PCI bus on which desired PCI device resides
* @devfn: encodes number of PCI slot in which the desired PCI
* device resides and the logical device number within that slot
* in case of multi-function devices.
*
* Given a PCI bus and slot/function number, the desired PCI device
* is located in the list of PCI devices.
* If the device is found, its reference count is increased and this
* function returns a pointer to its data structure. The caller must
* decrement the reference count by calling pci_dev_put().
* If no device is found, %NULL is returned.
*/
struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn)
{
struct list_head *tmp;
struct pci_dev *dev;
 
// WARN_ON(in_interrupt());
// down_read(&pci_bus_sem);
 
list_for_each(tmp, &bus->devices) {
dev = pci_dev_b(tmp);
if (dev->devfn == devfn)
goto out;
}
 
dev = NULL;
out:
// pci_dev_get(dev);
// up_read(&pci_bus_sem);
return dev;
}
 
 
 
 
/**
* pci_find_ext_capability - Find an extended capability
* @dev: PCI device to query
* @cap: capability code
*
* Returns the address of the requested extended capability structure
* within the device's PCI configuration space or 0 if the device does
* not support it. Possible values for @cap:
*
* %PCI_EXT_CAP_ID_ERR Advanced Error Reporting
* %PCI_EXT_CAP_ID_VC Virtual Channel
* %PCI_EXT_CAP_ID_DSN Device Serial Number
* %PCI_EXT_CAP_ID_PWR Power Budgeting
*/
int pci_find_ext_capability(struct pci_dev *dev, int cap)
{
u32 header;
int ttl;
int pos = PCI_CFG_SPACE_SIZE;
 
/* minimum 8 bytes per capability */
ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8;
 
if (dev->cfg_size <= PCI_CFG_SPACE_SIZE)
return 0;
 
if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL)
return 0;
 
/*
* If we have no capabilities, this is indicated by cap ID,
* cap version and next pointer all being 0.
*/
if (header == 0)
return 0;
 
while (ttl-- > 0) {
if (PCI_EXT_CAP_ID(header) == cap)
return pos;
 
pos = PCI_EXT_CAP_NEXT(header);
if (pos < PCI_CFG_SPACE_SIZE)
break;
 
if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL)
break;
}
 
return 0;
}
 
/drivers/devman/pci/probe.c
0,0 → 1,419
 
#include <ddk.h>
#include <linux/errno.h>
#include <mutex.h>
#include <pci.h>
#include <syscall.h>
 
 
#define LEGACY_IO_RESOURCE (IORESOURCE_IO | IORESOURCE_PCI_FIXED)
 
/**
* pci_setup_device - fill in class and map information of a device
* @dev: the device structure to fill
*
* Initialize the device structure with information about the device's
* vendor,class,memory and IO-space addresses,IRQ lines etc.
* Called at initialisation of the PCI subsystem and by CardBus services.
* Returns 0 on success and negative if unknown type of device (not normal,
* bridge or CardBus).
*/
int pci_setup_device(struct pci_dev *dev)
{
u32 class;
u8 hdr_type;
struct pci_slot *slot;
int pos = 0;
 
if (pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type))
return -EIO;
 
dev->sysdata = dev->bus->sysdata;
// dev->dev.parent = dev->bus->bridge;
// dev->dev.bus = &pci_bus_type;
dev->hdr_type = hdr_type & 0x7f;
dev->multifunction = !!(hdr_type & 0x80);
dev->error_state = pci_channel_io_normal;
set_pcie_port_type(dev);
 
list_for_each_entry(slot, &dev->bus->slots, list)
if (PCI_SLOT(dev->devfn) == slot->number)
dev->slot = slot;
 
/* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer)
set this higher, assuming the system even supports it. */
dev->dma_mask = 0xffffffff;
 
// dev_set_name(&dev->dev, "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus),
// dev->bus->number, PCI_SLOT(dev->devfn),
// PCI_FUNC(dev->devfn));
 
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
dev->revision = class & 0xff;
class >>= 8; /* upper 3 bytes */
dev->class = class;
class >>= 8;
 
dbgprintf("found [%04x:%04x] class %06x header type %02x\n",
dev->vendor, dev->device, class, dev->hdr_type);
 
/* need to have dev->class ready */
dev->cfg_size = pci_cfg_space_size(dev);
 
/* "Unknown power state" */
dev->current_state = PCI_UNKNOWN;
 
/* Early fixups, before probing the BARs */
// pci_fixup_device(pci_fixup_early, dev);
/* device class may be changed after fixup */
class = dev->class >> 8;
 
switch (dev->hdr_type) { /* header type */
case PCI_HEADER_TYPE_NORMAL: /* standard header */
if (class == PCI_CLASS_BRIDGE_PCI)
goto bad;
pci_read_irq(dev);
pci_read_bases(dev, 6, PCI_ROM_ADDRESS);
pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device);
 
/*
* Do the ugly legacy mode stuff here rather than broken chip
* quirk code. Legacy mode ATA controllers have fixed
* addresses. These are not always echoed in BAR0-3, and
* BAR0-3 in a few cases contain junk!
*/
if (class == PCI_CLASS_STORAGE_IDE) {
u8 progif;
pci_read_config_byte(dev, PCI_CLASS_PROG, &progif);
if ((progif & 1) == 0) {
dev->resource[0].start = 0x1F0;
dev->resource[0].end = 0x1F7;
dev->resource[0].flags = LEGACY_IO_RESOURCE;
dev->resource[1].start = 0x3F6;
dev->resource[1].end = 0x3F6;
dev->resource[1].flags = LEGACY_IO_RESOURCE;
}
if ((progif & 4) == 0) {
dev->resource[2].start = 0x170;
dev->resource[2].end = 0x177;
dev->resource[2].flags = LEGACY_IO_RESOURCE;
dev->resource[3].start = 0x376;
dev->resource[3].end = 0x376;
dev->resource[3].flags = LEGACY_IO_RESOURCE;
}
}
break;
 
case PCI_HEADER_TYPE_BRIDGE: /* bridge header */
if (class != PCI_CLASS_BRIDGE_PCI)
goto bad;
/* The PCI-to-PCI bridge spec requires that subtractive
decoding (i.e. transparent) bridge must have programming
interface code of 0x01. */
pci_read_irq(dev);
dev->transparent = ((dev->class & 0xff) == 1);
pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
set_pcie_hotplug_bridge(dev);
pos = pci_find_capability(dev, PCI_CAP_ID_SSVID);
if (pos) {
pci_read_config_word(dev, pos + PCI_SSVID_VENDOR_ID, &dev->subsystem_vendor);
pci_read_config_word(dev, pos + PCI_SSVID_DEVICE_ID, &dev->subsystem_device);
}
break;
 
case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */
if (class != PCI_CLASS_BRIDGE_CARDBUS)
goto bad;
pci_read_irq(dev);
pci_read_bases(dev, 1, 0);
pci_read_config_word(dev, PCI_CB_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
pci_read_config_word(dev, PCI_CB_SUBSYSTEM_ID, &dev->subsystem_device);
break;
 
default: /* unknown header */
dbgprintf("unknown header type %02x, "
"ignoring device\n", dev->hdr_type);
return -EIO;
 
bad:
dbgprintf("ignoring class %02x (doesn't match header "
"type %02x)\n", class, dev->hdr_type);
dev->class = PCI_CLASS_NOT_DEFINED;
}
 
/* We found a fine healthy device, go go go... */
return 0;
}
 
 
 
struct pci_dev *alloc_pci_dev(void)
{
struct pci_dev *dev;
 
dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL);
if (!dev)
return NULL;
 
INIT_LIST_HEAD(&dev->bus_list);
 
return dev;
}
 
/*
* Read the config data for a PCI device, sanity-check it
* and fill in the dev structure...
*/
static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
{
struct pci_dev *dev;
u32 l;
int timeout = 10;
 
if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))
return NULL;
 
/* some broken boards return 0 or ~0 if a slot is empty: */
if (l == 0xffffffff || l == 0x00000000 ||
l == 0x0000ffff || l == 0xffff0000)
return NULL;
 
/* Configuration request Retry Status */
while (l == 0xffff0001) {
delay(timeout/10);
timeout *= 2;
if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))
return NULL;
/* Card hasn't responded in 60 seconds? Must be stuck. */
if (delay > 60 * 1000) {
printk(KERN_WARNING "pci %04x:%02x:%02x.%d: not "
"responding\n", pci_domain_nr(bus),
bus->number, PCI_SLOT(devfn),
PCI_FUNC(devfn));
return NULL;
}
}
 
dev = alloc_pci_dev();
if (!dev)
return NULL;
 
dev->bus = bus;
dev->devfn = devfn;
dev->vendor = l & 0xffff;
dev->device = (l >> 16) & 0xffff;
 
if (pci_setup_device(dev)) {
kfree(dev);
return NULL;
}
 
return dev;
}
 
 
struct pci_dev * pci_scan_single_device(struct pci_bus *bus, int devfn)
{
struct pci_dev *dev;
 
dev = pci_get_slot(bus, devfn);
if (dev) {
// pci_dev_put(dev);
return dev;
}
 
dev = pci_scan_device(bus, devfn);
if (!dev)
return NULL;
 
pci_device_add(dev, bus);
 
return dev;
}
 
static unsigned next_ari_fn(struct pci_dev *dev, unsigned fn)
{
u16 cap;
unsigned pos, next_fn;
 
if (!dev)
return 0;
 
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI);
if (!pos)
return 0;
pci_read_config_word(dev, pos + 4, &cap);
next_fn = cap >> 8;
if (next_fn <= fn)
return 0;
return next_fn;
}
 
static unsigned next_trad_fn(struct pci_dev *dev, unsigned fn)
{
return (fn + 1) % 8;
}
 
static unsigned no_next_fn(struct pci_dev *dev, unsigned fn)
{
return 0;
}
 
static int only_one_child(struct pci_bus *bus)
{
struct pci_dev *parent = bus->self;
if (!parent || !pci_is_pcie(parent))
return 0;
if (parent->pcie_type == PCI_EXP_TYPE_ROOT_PORT ||
parent->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)
return 1;
return 0;
}
 
/**
* pci_scan_slot - scan a PCI slot on a bus for devices.
* @bus: PCI bus to scan
* @devfn: slot number to scan (must have zero function.)
*
* Scan a PCI slot on the specified PCI bus for devices, adding
* discovered devices to the @bus->devices list. New devices
* will not have is_added set.
*
* Returns the number of new devices found.
*/
int pci_scan_slot(struct pci_bus *bus, int devfn)
{
unsigned fn, nr = 0;
struct pci_dev *dev;
unsigned (*next_fn)(struct pci_dev *, unsigned) = no_next_fn;
 
if (only_one_child(bus) && (devfn > 0))
return 0; /* Already scanned the entire slot */
 
dev = pci_scan_single_device(bus, devfn);
if (!dev)
return 0;
if (!dev->is_added)
nr++;
 
if (pci_ari_enabled(bus))
next_fn = next_ari_fn;
else if (dev->multifunction)
next_fn = next_trad_fn;
 
for (fn = next_fn(dev, 0); fn > 0; fn = next_fn(dev, fn)) {
dev = pci_scan_single_device(bus, devfn + fn);
if (dev) {
if (!dev->is_added)
nr++;
dev->multifunction = 1;
}
}
 
/* only one slot has pcie device */
if (bus->self && nr)
pcie_aspm_init_link_state(bus->self);
 
return nr;
}
 
 
unsigned int pci_scan_child_bus(struct pci_bus *bus)
{
unsigned int devfn, pass, max = bus->secondary;
struct pci_dev *dev;
 
dbgprintf("scanning bus\n");
 
/* Go find them, Rover! */
for (devfn = 0; devfn < 0x100; devfn += 8)
pci_scan_slot(bus, devfn);
 
/* Reserve buses for SR-IOV capability. */
max += pci_iov_bus_range(bus);
 
/*
* After performing arch-dependent fixup of the bus, look behind
* all PCI-to-PCI bridges on this bus.
*/
if (!bus->is_added) {
dbgprintf("fixups for bus\n");
pcibios_fixup_bus(bus);
if (pci_is_root_bus(bus))
bus->is_added = 1;
}
 
for (pass=0; pass < 2; pass++)
list_for_each_entry(dev, &bus->devices, bus_list) {
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
max = pci_scan_bridge(bus, dev, max, pass);
}
 
/*
* We've scanned the bus and so we know all about what's on
* the other side of any bridges that may be on this bus plus
* any devices.
*
* Return how far we've got finding sub-buses.
*/
dbgprintf("bus scan returning with max=%02x\n", max);
return max;
}
 
/**
* pci_cfg_space_size - get the configuration space size of the PCI device.
* @dev: PCI device
*
* Regular PCI devices have 256 bytes, but PCI-X 2 and PCI Express devices
* have 4096 bytes. Even if the device is capable, that doesn't mean we can
* access it. Maybe we don't have a way to generate extended config space
* accesses, or the device is behind a reverse Express bridge. So we try
* reading the dword at 0x100 which must either be 0 or a valid extended
* capability header.
*/
int pci_cfg_space_size_ext(struct pci_dev *dev)
{
u32 status;
int pos = PCI_CFG_SPACE_SIZE;
 
if (pci_read_config_dword(dev, pos, &status) != PCIBIOS_SUCCESSFUL)
goto fail;
if (status == 0xffffffff)
goto fail;
 
return PCI_CFG_SPACE_EXP_SIZE;
 
fail:
return PCI_CFG_SPACE_SIZE;
}
 
int pci_cfg_space_size(struct pci_dev *dev)
{
int pos;
u32 status;
u16 class;
 
class = dev->class >> 8;
if (class == PCI_CLASS_BRIDGE_HOST)
return pci_cfg_space_size_ext(dev);
 
pos = pci_pcie_cap(dev);
if (!pos) {
pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
if (!pos)
goto fail;
 
pci_read_config_dword(dev, pos + PCI_X_STATUS, &status);
if (!(status & (PCI_X_STATUS_266MHZ | PCI_X_STATUS_533MHZ)))
goto fail;
}
 
return pci_cfg_space_size_ext(dev);
 
fail:
return PCI_CFG_SPACE_SIZE;
}
 
 
/drivers/devman/pci_bind.c
0,0 → 1,98
 
#include <ddk.h>
#include <linux/errno.h>
#include <mutex.h>
#include <pci.h>
#include <syscall.h>
 
#include "acpi.h"
#include "acpi_bus.h"
 
 
#define PREFIX "ACPI: "
 
 
static int acpi_pci_unbind(struct acpi_device *device)
{
struct pci_dev *dev;
 
dev = acpi_get_pci_dev(device->handle);
if (!dev)
goto out;
 
// device_set_run_wake(&dev->dev, false);
// pci_acpi_remove_pm_notifier(device);
 
if (!dev->subordinate)
goto out;
 
// acpi_pci_irq_del_prt(dev->subordinate);
 
device->ops.bind = NULL;
device->ops.unbind = NULL;
 
out:
// pci_dev_put(dev);
return 0;
}
 
static int acpi_pci_bind(struct acpi_device *device)
{
ACPI_STATUS status;
ACPI_HANDLE handle;
struct pci_bus *bus;
struct pci_dev *dev;
 
dev = acpi_get_pci_dev(device->handle);
if (!dev)
return 0;
 
// pci_acpi_add_pm_notifier(device, dev);
// if (device->wakeup.flags.run_wake)
// device_set_run_wake(&dev->dev, true);
 
/*
* Install the 'bind' function to facilitate callbacks for
* children of the P2P bridge.
*/
if (dev->subordinate) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Device %04x:%02x:%02x.%d is a PCI bridge\n",
pci_domain_nr(dev->bus), dev->bus->number,
PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)));
device->ops.bind = acpi_pci_bind;
device->ops.unbind = acpi_pci_unbind;
}
 
/*
* Evaluate and parse _PRT, if exists. This code allows parsing of
* _PRT objects within the scope of non-bridge devices. Note that
* _PRTs within the scope of a PCI bridge assume the bridge's
* subordinate bus number.
*
* TBD: Can _PRTs exist within the scope of non-bridge PCI devices?
*/
status = AcpiGetHandle(device->handle, METHOD_NAME__PRT, &handle);
if (ACPI_FAILURE(status))
goto out;
 
if (dev->subordinate)
bus = dev->subordinate;
else
bus = dev->bus;
 
acpi_pci_irq_add_prt(device->handle, bus);
 
out:
// pci_dev_put(dev);
return 0;
}
 
int acpi_pci_bind_root(struct acpi_device *device)
{
device->ops.bind = acpi_pci_bind;
device->ops.unbind = acpi_pci_unbind;
 
return 0;
}
 
/drivers/devman/pci_root.c
0,0 → 1,149
 
#include <ddk.h>
#include <linux/errno.h>
#include <mutex.h>
#include <pci.h>
#include <syscall.h>
 
#include "acpi.h"
#include "acpi_bus.h"
 
 
#define PREFIX "ACPI: "
 
 
struct acpi_handle_node {
struct list_head node;
ACPI_HANDLE handle;
};
 
static const struct acpi_device_ids root_device_ids[] = {
{"PNP0A03", 0},
{"", 0},
};
 
static LIST_HEAD(acpi_pci_roots);
 
 
/**
* acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge
* @handle - the ACPI CA node in question.
*
* Note: we could make this API take a struct acpi_device * instead, but
* for now, it's more convenient to operate on an acpi_handle.
*/
int acpi_is_root_bridge(ACPI_HANDLE handle)
{
int ret;
struct acpi_device *device;
 
ret = acpi_bus_get_device(handle, &device);
if (ret)
return 0;
 
ret = acpi_match_device_ids(device, root_device_ids);
if (ret)
return 0;
else
return 1;
}
 
 
struct acpi_pci_root *acpi_pci_find_root(ACPI_HANDLE handle)
{
struct acpi_pci_root *root;
 
list_for_each_entry(root, &acpi_pci_roots, node) {
if (root->device->handle == handle)
return root;
}
return NULL;
}
 
 
/**
* acpi_get_pci_dev - convert ACPI CA handle to struct pci_dev
* @handle: the handle in question
*
* Given an ACPI CA handle, the desired PCI device is located in the
* list of PCI devices.
*
* If the device is found, its reference count is increased and this
* function returns a pointer to its data structure. The caller must
* decrement the reference count by calling pci_dev_put().
* If no device is found, %NULL is returned.
*/
struct pci_dev *acpi_get_pci_dev(ACPI_HANDLE handle)
{
int dev, fn;
unsigned long long adr;
ACPI_STATUS status;
ACPI_HANDLE phandle;
struct pci_bus *pbus;
struct pci_dev *pdev = NULL;
struct acpi_handle_node *node, *tmp;
struct acpi_pci_root *root;
LIST_HEAD(device_list);
 
/*
* Walk up the ACPI CA namespace until we reach a PCI root bridge.
*/
phandle = handle;
while (!acpi_is_root_bridge(phandle)) {
node = kzalloc(sizeof(struct acpi_handle_node), GFP_KERNEL);
if (!node)
goto out;
 
INIT_LIST_HEAD(&node->node);
node->handle = phandle;
list_add(&node->node, &device_list);
 
status = AcpiGetParent(phandle, &phandle);
if (ACPI_FAILURE(status))
goto out;
}
 
root = acpi_pci_find_root(phandle);
if (!root)
goto out;
 
pbus = root->bus;
 
/*
* Now, walk back down the PCI device tree until we return to our
* original handle. Assumes that everything between the PCI root
* bridge and the device we're looking for must be a P2P bridge.
*/
list_for_each_entry(node, &device_list, node) {
ACPI_HANDLE hnd = node->handle;
status = acpi_evaluate_integer(hnd, "_ADR", NULL, &adr);
if (ACPI_FAILURE(status))
goto out;
dev = (adr >> 16) & 0xffff;
fn = adr & 0xffff;
 
pdev = pci_get_slot(pbus, PCI_DEVFN(dev, fn));
if (!pdev || hnd == handle)
break;
 
pbus = pdev->subordinate;
// pci_dev_put(pdev);
 
/*
* This function may be called for a non-PCI device that has a
* PCI parent (eg. a disk under a PCI SATA controller). In that
* case pdev->subordinate will be NULL for the parent.
*/
if (!pbus) {
dbgprintf("Not a PCI-to-PCI bridge\n");
pdev = NULL;
break;
}
}
out:
list_for_each_entry_safe(node, tmp, &device_list, node)
kfree(node);
 
return pdev;
}
 
/drivers/devman/scan.c
626,10 → 626,10
/*
* Bind _ADR-Based Devices when hot add
*/
// if (device->flags.bus_address) {
// if (device->parent && device->parent->ops.bind)
// device->parent->ops.bind(device);
// }
if (device->flags.bus_address) {
if (device->parent && device->parent->ops.bind)
device->parent->ops.bind(device);
}
 
end:
if (!result) {
/drivers/devman/scan.cc
0,0 → 1,1557
/*
* scan.c - support for transforming the ACPI namespace into individual objects
*/
 
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/acpi.h>
#include <linux/signal.h>
#include <linux/kthread.h>
#include <linux/dmi.h>
 
#include <acpi/acpi_drivers.h>
 
#include "internal.h"
 
#define _COMPONENT ACPI_BUS_COMPONENT
ACPI_MODULE_NAME("scan");
#define STRUCT_TO_INT(s) (*((int*)&s))
extern struct acpi_device *acpi_root;
 
#define ACPI_BUS_CLASS "system_bus"
#define ACPI_BUS_HID "LNXSYBUS"
#define ACPI_BUS_DEVICE_NAME "System Bus"
 
#define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent)
 
static LIST_HEAD(acpi_device_list);
static LIST_HEAD(acpi_bus_id_list);
DEFINE_MUTEX(acpi_device_lock);
LIST_HEAD(acpi_wakeup_device_list);
 
struct acpi_device_bus_id{
char bus_id[15];
unsigned int instance_no;
struct list_head node;
};
 
/*
* Creates hid/cid(s) string needed for modalias and uevent
* e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get:
* char *modalias: "acpi:IBM0001:ACPI0001"
*/
static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
int size)
{
int len;
int count;
struct acpi_hardware_id *id;
 
len = snprintf(modalias, size, "acpi:");
size -= len;
 
list_for_each_entry(id, &acpi_dev->pnp.ids, list) {
count = snprintf(&modalias[len], size, "%s:", id->id);
if (count < 0 || count >= size)
return -EINVAL;
len += count;
size -= count;
}
 
modalias[len] = '\0';
return len;
}
 
static ssize_t
acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) {
struct acpi_device *acpi_dev = to_acpi_device(dev);
int len;
 
/* Device has no HID and no CID or string is >1024 */
len = create_modalias(acpi_dev, buf, 1024);
if (len <= 0)
return 0;
buf[len++] = '\n';
return len;
}
static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
 
static void acpi_bus_hot_remove_device(void *context)
{
struct acpi_device *device;
acpi_handle handle = context;
struct acpi_object_list arg_list;
union acpi_object arg;
acpi_status status = AE_OK;
 
if (acpi_bus_get_device(handle, &device))
return;
 
if (!device)
return;
 
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Hot-removing device %s...\n", dev_name(&device->dev)));
 
if (acpi_bus_trim(device, 1)) {
printk(KERN_ERR PREFIX
"Removing device failed\n");
return;
}
 
/* power off device */
status = acpi_evaluate_object(handle, "_PS3", NULL, NULL);
if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
printk(KERN_WARNING PREFIX
"Power-off device failed\n");
 
if (device->flags.lockable) {
arg_list.count = 1;
arg_list.pointer = &arg;
arg.type = ACPI_TYPE_INTEGER;
arg.integer.value = 0;
acpi_evaluate_object(handle, "_LCK", &arg_list, NULL);
}
 
arg_list.count = 1;
arg_list.pointer = &arg;
arg.type = ACPI_TYPE_INTEGER;
arg.integer.value = 1;
 
/*
* TBD: _EJD support.
*/
status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL);
if (ACPI_FAILURE(status))
printk(KERN_WARNING PREFIX
"Eject device failed\n");
 
return;
}
 
static ssize_t
acpi_eject_store(struct device *d, struct device_attribute *attr,
const char *buf, size_t count)
{
int ret = count;
acpi_status status;
acpi_object_type type = 0;
struct acpi_device *acpi_device = to_acpi_device(d);
 
if ((!count) || (buf[0] != '1')) {
return -EINVAL;
}
#ifndef FORCE_EJECT
if (acpi_device->driver == NULL) {
ret = -ENODEV;
goto err;
}
#endif
status = acpi_get_type(acpi_device->handle, &type);
if (ACPI_FAILURE(status) || (!acpi_device->flags.ejectable)) {
ret = -ENODEV;
goto err;
}
 
acpi_os_hotplug_execute(acpi_bus_hot_remove_device, acpi_device->handle);
err:
return ret;
}
 
static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store);
 
static ssize_t
acpi_device_hid_show(struct device *dev, struct device_attribute *attr, char *buf) {
struct acpi_device *acpi_dev = to_acpi_device(dev);
 
return sprintf(buf, "%s\n", acpi_device_hid(acpi_dev));
}
static DEVICE_ATTR(hid, 0444, acpi_device_hid_show, NULL);
 
static ssize_t
acpi_device_path_show(struct device *dev, struct device_attribute *attr, char *buf) {
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL};
int result;
 
result = acpi_get_name(acpi_dev->handle, ACPI_FULL_PATHNAME, &path);
if (result)
goto end;
 
result = sprintf(buf, "%s\n", (char*)path.pointer);
kfree(path.pointer);
end:
return result;
}
static DEVICE_ATTR(path, 0444, acpi_device_path_show, NULL);
 
static int acpi_device_setup_files(struct acpi_device *dev)
{
acpi_status status;
acpi_handle temp;
int result = 0;
 
/*
* Devices gotten from FADT don't have a "path" attribute
*/
if (dev->handle) {
result = device_create_file(&dev->dev, &dev_attr_path);
if (result)
goto end;
}
 
result = device_create_file(&dev->dev, &dev_attr_hid);
if (result)
goto end;
 
result = device_create_file(&dev->dev, &dev_attr_modalias);
if (result)
goto end;
 
/*
* If device has _EJ0, 'eject' file is created that is used to trigger
* hot-removal function from userland.
*/
status = acpi_get_handle(dev->handle, "_EJ0", &temp);
if (ACPI_SUCCESS(status))
result = device_create_file(&dev->dev, &dev_attr_eject);
end:
return result;
}
 
static void acpi_device_remove_files(struct acpi_device *dev)
{
acpi_status status;
acpi_handle temp;
 
/*
* If device has _EJ0, 'eject' file is created that is used to trigger
* hot-removal function from userland.
*/
status = acpi_get_handle(dev->handle, "_EJ0", &temp);
if (ACPI_SUCCESS(status))
device_remove_file(&dev->dev, &dev_attr_eject);
 
device_remove_file(&dev->dev, &dev_attr_modalias);
device_remove_file(&dev->dev, &dev_attr_hid);
if (dev->handle)
device_remove_file(&dev->dev, &dev_attr_path);
}
/* --------------------------------------------------------------------------
ACPI Bus operations
-------------------------------------------------------------------------- */
 
int acpi_match_device_ids(struct acpi_device *device,
const struct acpi_device_id *ids)
{
const struct acpi_device_id *id;
struct acpi_hardware_id *hwid;
 
/*
* If the device is not present, it is unnecessary to load device
* driver for it.
*/
if (!device->status.present)
return -ENODEV;
 
for (id = ids; id->id[0]; id++)
list_for_each_entry(hwid, &device->pnp.ids, list)
if (!strcmp((char *) id->id, hwid->id))
return 0;
 
return -ENOENT;
}
EXPORT_SYMBOL(acpi_match_device_ids);
 
static void acpi_free_ids(struct acpi_device *device)
{
struct acpi_hardware_id *id, *tmp;
 
list_for_each_entry_safe(id, tmp, &device->pnp.ids, list) {
kfree(id->id);
kfree(id);
}
}
 
static void acpi_device_release(struct device *dev)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
 
acpi_free_ids(acpi_dev);
kfree(acpi_dev);
}
 
static int acpi_device_suspend(struct device *dev, pm_message_t state)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_driver *acpi_drv = acpi_dev->driver;
 
if (acpi_drv && acpi_drv->ops.suspend)
return acpi_drv->ops.suspend(acpi_dev, state);
return 0;
}
 
static int acpi_device_resume(struct device *dev)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_driver *acpi_drv = acpi_dev->driver;
 
if (acpi_drv && acpi_drv->ops.resume)
return acpi_drv->ops.resume(acpi_dev);
return 0;
}
 
static int acpi_bus_match(struct device *dev, struct device_driver *drv)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_driver *acpi_drv = to_acpi_driver(drv);
 
return !acpi_match_device_ids(acpi_dev, acpi_drv->ids);
}
 
static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
int len;
 
if (add_uevent_var(env, "MODALIAS="))
return -ENOMEM;
len = create_modalias(acpi_dev, &env->buf[env->buflen - 1],
sizeof(env->buf) - env->buflen);
if (len >= (sizeof(env->buf) - env->buflen))
return -ENOMEM;
env->buflen += len;
return 0;
}
 
static void acpi_device_notify(acpi_handle handle, u32 event, void *data)
{
struct acpi_device *device = data;
 
device->driver->ops.notify(device, event);
}
 
static acpi_status acpi_device_notify_fixed(void *data)
{
struct acpi_device *device = data;
 
/* Fixed hardware devices have no handles */
acpi_device_notify(NULL, ACPI_FIXED_HARDWARE_EVENT, device);
return AE_OK;
}
 
static int acpi_device_install_notify_handler(struct acpi_device *device)
{
acpi_status status;
 
if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON)
status =
acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
acpi_device_notify_fixed,
device);
else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON)
status =
acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
acpi_device_notify_fixed,
device);
else
status = acpi_install_notify_handler(device->handle,
ACPI_DEVICE_NOTIFY,
acpi_device_notify,
device);
 
if (ACPI_FAILURE(status))
return -EINVAL;
return 0;
}
 
static void acpi_device_remove_notify_handler(struct acpi_device *device)
{
if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON)
acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
acpi_device_notify_fixed);
else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON)
acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
acpi_device_notify_fixed);
else
acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
acpi_device_notify);
}
 
static int acpi_bus_driver_init(struct acpi_device *, struct acpi_driver *);
static int acpi_start_single_object(struct acpi_device *);
static int acpi_device_probe(struct device * dev)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver);
int ret;
 
ret = acpi_bus_driver_init(acpi_dev, acpi_drv);
if (!ret) {
if (acpi_dev->bus_ops.acpi_op_start)
acpi_start_single_object(acpi_dev);
 
if (acpi_drv->ops.notify) {
ret = acpi_device_install_notify_handler(acpi_dev);
if (ret) {
if (acpi_drv->ops.remove)
acpi_drv->ops.remove(acpi_dev,
acpi_dev->removal_type);
return ret;
}
}
 
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Found driver [%s] for device [%s]\n",
acpi_drv->name, acpi_dev->pnp.bus_id));
get_device(dev);
}
return ret;
}
 
static int acpi_device_remove(struct device * dev)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_driver *acpi_drv = acpi_dev->driver;
 
if (acpi_drv) {
if (acpi_drv->ops.notify)
acpi_device_remove_notify_handler(acpi_dev);
if (acpi_drv->ops.remove)
acpi_drv->ops.remove(acpi_dev, acpi_dev->removal_type);
}
acpi_dev->driver = NULL;
acpi_dev->driver_data = NULL;
 
put_device(dev);
return 0;
}
 
struct bus_type acpi_bus_type = {
.name = "acpi",
.suspend = acpi_device_suspend,
.resume = acpi_device_resume,
.match = acpi_bus_match,
.probe = acpi_device_probe,
.remove = acpi_device_remove,
.uevent = acpi_device_uevent,
};
 
static int acpi_device_register(struct acpi_device *device)
{
int result;
struct acpi_device_bus_id *acpi_device_bus_id, *new_bus_id;
int found = 0;
 
/*
* Linkage
* -------
* Link this device to its parent and siblings.
*/
INIT_LIST_HEAD(&device->children);
INIT_LIST_HEAD(&device->node);
INIT_LIST_HEAD(&device->wakeup_list);
 
new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL);
if (!new_bus_id) {
printk(KERN_ERR PREFIX "Memory allocation error\n");
return -ENOMEM;
}
 
mutex_lock(&acpi_device_lock);
/*
* Find suitable bus_id and instance number in acpi_bus_id_list
* If failed, create one and link it into acpi_bus_id_list
*/
list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node) {
if (!strcmp(acpi_device_bus_id->bus_id,
acpi_device_hid(device))) {
acpi_device_bus_id->instance_no++;
found = 1;
kfree(new_bus_id);
break;
}
}
if (!found) {
acpi_device_bus_id = new_bus_id;
strcpy(acpi_device_bus_id->bus_id, acpi_device_hid(device));
acpi_device_bus_id->instance_no = 0;
list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list);
}
dev_set_name(&device->dev, "%s:%02x", acpi_device_bus_id->bus_id, acpi_device_bus_id->instance_no);
 
if (device->parent)
list_add_tail(&device->node, &device->parent->children);
 
if (device->wakeup.flags.valid)
list_add_tail(&device->wakeup_list, &acpi_wakeup_device_list);
mutex_unlock(&acpi_device_lock);
 
if (device->parent)
device->dev.parent = &device->parent->dev;
device->dev.bus = &acpi_bus_type;
device->dev.release = &acpi_device_release;
result = device_register(&device->dev);
if (result) {
dev_err(&device->dev, "Error registering device\n");
goto end;
}
 
result = acpi_device_setup_files(device);
if (result)
printk(KERN_ERR PREFIX "Error creating sysfs interface for device %s\n",
dev_name(&device->dev));
 
device->removal_type = ACPI_BUS_REMOVAL_NORMAL;
return 0;
end:
mutex_lock(&acpi_device_lock);
if (device->parent)
list_del(&device->node);
list_del(&device->wakeup_list);
mutex_unlock(&acpi_device_lock);
return result;
}
 
static void acpi_device_unregister(struct acpi_device *device, int type)
{
mutex_lock(&acpi_device_lock);
if (device->parent)
list_del(&device->node);
 
list_del(&device->wakeup_list);
mutex_unlock(&acpi_device_lock);
 
acpi_detach_data(device->handle, acpi_bus_data_handler);
 
acpi_device_remove_files(device);
device_unregister(&device->dev);
}
 
/* --------------------------------------------------------------------------
Driver Management
-------------------------------------------------------------------------- */
/**
* acpi_bus_driver_init - add a device to a driver
* @device: the device to add and initialize
* @driver: driver for the device
*
* Used to initialize a device via its device driver. Called whenever a
* driver is bound to a device. Invokes the driver's add() ops.
*/
static int
acpi_bus_driver_init(struct acpi_device *device, struct acpi_driver *driver)
{
int result = 0;
 
if (!device || !driver)
return -EINVAL;
 
if (!driver->ops.add)
return -ENOSYS;
 
result = driver->ops.add(device);
if (result) {
device->driver = NULL;
device->driver_data = NULL;
return result;
}
 
device->driver = driver;
 
/*
* TBD - Configuration Management: Assign resources to device based
* upon possible configuration and currently allocated resources.
*/
 
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Driver successfully bound to device\n"));
return 0;
}
 
static int acpi_start_single_object(struct acpi_device *device)
{
int result = 0;
struct acpi_driver *driver;
 
 
if (!(driver = device->driver))
return 0;
 
if (driver->ops.start) {
result = driver->ops.start(device);
if (result && driver->ops.remove)
driver->ops.remove(device, ACPI_BUS_REMOVAL_NORMAL);
}
 
return result;
}
 
/**
* acpi_bus_register_driver - register a driver with the ACPI bus
* @driver: driver being registered
*
* Registers a driver with the ACPI bus. Searches the namespace for all
* devices that match the driver's criteria and binds. Returns zero for
* success or a negative error status for failure.
*/
int acpi_bus_register_driver(struct acpi_driver *driver)
{
int ret;
 
if (acpi_disabled)
return -ENODEV;
driver->drv.name = driver->name;
driver->drv.bus = &acpi_bus_type;
driver->drv.owner = driver->owner;
 
ret = driver_register(&driver->drv);
return ret;
}
 
EXPORT_SYMBOL(acpi_bus_register_driver);
 
/**
* acpi_bus_unregister_driver - unregisters a driver with the APIC bus
* @driver: driver to unregister
*
* Unregisters a driver with the ACPI bus. Searches the namespace for all
* devices that match the driver's criteria and unbinds.
*/
void acpi_bus_unregister_driver(struct acpi_driver *driver)
{
driver_unregister(&driver->drv);
}
 
EXPORT_SYMBOL(acpi_bus_unregister_driver);
 
/* --------------------------------------------------------------------------
Device Enumeration
-------------------------------------------------------------------------- */
static struct acpi_device *acpi_bus_get_parent(acpi_handle handle)
{
acpi_status status;
int ret;
struct acpi_device *device;
 
/*
* Fixed hardware devices do not appear in the namespace and do not
* have handles, but we fabricate acpi_devices for them, so we have
* to deal with them specially.
*/
if (handle == NULL)
return acpi_root;
 
do {
status = acpi_get_parent(handle, &handle);
if (status == AE_NULL_ENTRY)
return NULL;
if (ACPI_FAILURE(status))
return acpi_root;
 
ret = acpi_bus_get_device(handle, &device);
if (ret == 0)
return device;
} while (1);
}
 
acpi_status
acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd)
{
acpi_status status;
acpi_handle tmp;
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *obj;
 
status = acpi_get_handle(handle, "_EJD", &tmp);
if (ACPI_FAILURE(status))
return status;
 
status = acpi_evaluate_object(handle, "_EJD", NULL, &buffer);
if (ACPI_SUCCESS(status)) {
obj = buffer.pointer;
status = acpi_get_handle(ACPI_ROOT_OBJECT, obj->string.pointer,
ejd);
kfree(buffer.pointer);
}
return status;
}
EXPORT_SYMBOL_GPL(acpi_bus_get_ejd);
 
void acpi_bus_data_handler(acpi_handle handle, void *context)
{
 
/* TBD */
 
return;
}
 
static int acpi_bus_get_perf_flags(struct acpi_device *device)
{
device->performance.state = ACPI_STATE_UNKNOWN;
return 0;
}
 
static acpi_status
acpi_bus_extract_wakeup_device_power_package(struct acpi_device *device,
union acpi_object *package)
{
int i = 0;
union acpi_object *element = NULL;
 
if (!device || !package || (package->package.count < 2))
return AE_BAD_PARAMETER;
 
element = &(package->package.elements[0]);
if (!element)
return AE_BAD_PARAMETER;
if (element->type == ACPI_TYPE_PACKAGE) {
if ((element->package.count < 2) ||
(element->package.elements[0].type !=
ACPI_TYPE_LOCAL_REFERENCE)
|| (element->package.elements[1].type != ACPI_TYPE_INTEGER))
return AE_BAD_DATA;
device->wakeup.gpe_device =
element->package.elements[0].reference.handle;
device->wakeup.gpe_number =
(u32) element->package.elements[1].integer.value;
} else if (element->type == ACPI_TYPE_INTEGER) {
device->wakeup.gpe_number = element->integer.value;
} else
return AE_BAD_DATA;
 
element = &(package->package.elements[1]);
if (element->type != ACPI_TYPE_INTEGER) {
return AE_BAD_DATA;
}
device->wakeup.sleep_state = element->integer.value;
 
if ((package->package.count - 2) > ACPI_MAX_HANDLES) {
return AE_NO_MEMORY;
}
device->wakeup.resources.count = package->package.count - 2;
for (i = 0; i < device->wakeup.resources.count; i++) {
element = &(package->package.elements[i + 2]);
if (element->type != ACPI_TYPE_LOCAL_REFERENCE)
return AE_BAD_DATA;
 
device->wakeup.resources.handles[i] = element->reference.handle;
}
 
acpi_gpe_can_wake(device->wakeup.gpe_device, device->wakeup.gpe_number);
 
return AE_OK;
}
 
static void acpi_bus_set_run_wake_flags(struct acpi_device *device)
{
struct acpi_device_id button_device_ids[] = {
{"PNP0C0D", 0},
{"PNP0C0C", 0},
{"PNP0C0E", 0},
{"", 0},
};
acpi_status status;
acpi_event_status event_status;
 
device->wakeup.run_wake_count = 0;
device->wakeup.flags.notifier_present = 0;
 
/* Power button, Lid switch always enable wakeup */
if (!acpi_match_device_ids(device, button_device_ids)) {
device->wakeup.flags.run_wake = 1;
device->wakeup.flags.always_enabled = 1;
return;
}
 
status = acpi_get_gpe_status(device->wakeup.gpe_device,
device->wakeup.gpe_number,
&event_status);
if (status == AE_OK)
device->wakeup.flags.run_wake =
!!(event_status & ACPI_EVENT_FLAG_HANDLE);
}
 
static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
{
acpi_status status = 0;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *package = NULL;
int psw_error;
 
/* _PRW */
status = acpi_evaluate_object(device->handle, "_PRW", NULL, &buffer);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRW"));
goto end;
}
 
package = (union acpi_object *)buffer.pointer;
status = acpi_bus_extract_wakeup_device_power_package(device, package);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "Extracting _PRW package"));
goto end;
}
 
kfree(buffer.pointer);
 
device->wakeup.flags.valid = 1;
device->wakeup.prepare_count = 0;
acpi_bus_set_run_wake_flags(device);
/* Call _PSW/_DSW object to disable its ability to wake the sleeping
* system for the ACPI device with the _PRW object.
* The _PSW object is depreciated in ACPI 3.0 and is replaced by _DSW.
* So it is necessary to call _DSW object first. Only when it is not
* present will the _PSW object used.
*/
psw_error = acpi_device_sleep_wake(device, 0, 0, 0);
if (psw_error)
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"error in _DSW or _PSW evaluation\n"));
 
end:
if (ACPI_FAILURE(status))
device->flags.wake_capable = 0;
return 0;
}
 
static int acpi_bus_get_power_flags(struct acpi_device *device)
{
acpi_status status = 0;
acpi_handle handle = NULL;
u32 i = 0;
 
 
/*
* Power Management Flags
*/
status = acpi_get_handle(device->handle, "_PSC", &handle);
if (ACPI_SUCCESS(status))
device->power.flags.explicit_get = 1;
status = acpi_get_handle(device->handle, "_IRC", &handle);
if (ACPI_SUCCESS(status))
device->power.flags.inrush_current = 1;
 
/*
* Enumerate supported power management states
*/
for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3; i++) {
struct acpi_device_power_state *ps = &device->power.states[i];
char object_name[5] = { '_', 'P', 'R', '0' + i, '\0' };
 
/* Evaluate "_PRx" to se if power resources are referenced */
acpi_evaluate_reference(device->handle, object_name, NULL,
&ps->resources);
if (ps->resources.count) {
device->power.flags.power_resources = 1;
ps->flags.valid = 1;
}
 
/* Evaluate "_PSx" to see if we can do explicit sets */
object_name[2] = 'S';
status = acpi_get_handle(device->handle, object_name, &handle);
if (ACPI_SUCCESS(status)) {
ps->flags.explicit_set = 1;
ps->flags.valid = 1;
}
 
/* State is valid if we have some power control */
if (ps->resources.count || ps->flags.explicit_set)
ps->flags.valid = 1;
 
ps->power = -1; /* Unknown - driver assigned */
ps->latency = -1; /* Unknown - driver assigned */
}
 
/* Set defaults for D0 and D3 states (always valid) */
device->power.states[ACPI_STATE_D0].flags.valid = 1;
device->power.states[ACPI_STATE_D0].power = 100;
device->power.states[ACPI_STATE_D3].flags.valid = 1;
device->power.states[ACPI_STATE_D3].power = 0;
 
/* TBD: System wake support and resource requirements. */
 
device->power.state = ACPI_STATE_UNKNOWN;
acpi_bus_get_power(device->handle, &(device->power.state));
 
return 0;
}
 
static int acpi_bus_get_flags(struct acpi_device *device)
{
acpi_status status = AE_OK;
acpi_handle temp = NULL;
 
 
/* Presence of _STA indicates 'dynamic_status' */
status = acpi_get_handle(device->handle, "_STA", &temp);
if (ACPI_SUCCESS(status))
device->flags.dynamic_status = 1;
 
/* Presence of _RMV indicates 'removable' */
status = acpi_get_handle(device->handle, "_RMV", &temp);
if (ACPI_SUCCESS(status))
device->flags.removable = 1;
 
/* Presence of _EJD|_EJ0 indicates 'ejectable' */
status = acpi_get_handle(device->handle, "_EJD", &temp);
if (ACPI_SUCCESS(status))
device->flags.ejectable = 1;
else {
status = acpi_get_handle(device->handle, "_EJ0", &temp);
if (ACPI_SUCCESS(status))
device->flags.ejectable = 1;
}
 
/* Presence of _LCK indicates 'lockable' */
status = acpi_get_handle(device->handle, "_LCK", &temp);
if (ACPI_SUCCESS(status))
device->flags.lockable = 1;
 
/* Presence of _PS0|_PR0 indicates 'power manageable' */
status = acpi_get_handle(device->handle, "_PS0", &temp);
if (ACPI_FAILURE(status))
status = acpi_get_handle(device->handle, "_PR0", &temp);
if (ACPI_SUCCESS(status))
device->flags.power_manageable = 1;
 
/* Presence of _PRW indicates wake capable */
status = acpi_get_handle(device->handle, "_PRW", &temp);
if (ACPI_SUCCESS(status))
device->flags.wake_capable = 1;
 
/* TBD: Performance management */
 
return 0;
}
 
static void acpi_device_get_busid(struct acpi_device *device)
{
char bus_id[5] = { '?', 0 };
struct acpi_buffer buffer = { sizeof(bus_id), bus_id };
int i = 0;
 
/*
* Bus ID
* ------
* The device's Bus ID is simply the object name.
* TBD: Shouldn't this value be unique (within the ACPI namespace)?
*/
if (ACPI_IS_ROOT_DEVICE(device)) {
strcpy(device->pnp.bus_id, "ACPI");
return;
}
 
switch (device->device_type) {
case ACPI_BUS_TYPE_POWER_BUTTON:
strcpy(device->pnp.bus_id, "PWRF");
break;
case ACPI_BUS_TYPE_SLEEP_BUTTON:
strcpy(device->pnp.bus_id, "SLPF");
break;
default:
acpi_get_name(device->handle, ACPI_SINGLE_NAME, &buffer);
/* Clean up trailing underscores (if any) */
for (i = 3; i > 1; i--) {
if (bus_id[i] == '_')
bus_id[i] = '\0';
else
break;
}
strcpy(device->pnp.bus_id, bus_id);
break;
}
}
 
/*
* acpi_bay_match - see if a device is an ejectable driver bay
*
* If an acpi object is ejectable and has one of the ACPI ATA methods defined,
* then we can safely call it an ejectable drive bay
*/
static int acpi_bay_match(struct acpi_device *device){
acpi_status status;
acpi_handle handle;
acpi_handle tmp;
acpi_handle phandle;
 
handle = device->handle;
 
status = acpi_get_handle(handle, "_EJ0", &tmp);
if (ACPI_FAILURE(status))
return -ENODEV;
 
if ((ACPI_SUCCESS(acpi_get_handle(handle, "_GTF", &tmp))) ||
(ACPI_SUCCESS(acpi_get_handle(handle, "_GTM", &tmp))) ||
(ACPI_SUCCESS(acpi_get_handle(handle, "_STM", &tmp))) ||
(ACPI_SUCCESS(acpi_get_handle(handle, "_SDD", &tmp))))
return 0;
 
if (acpi_get_parent(handle, &phandle))
return -ENODEV;
 
if ((ACPI_SUCCESS(acpi_get_handle(phandle, "_GTF", &tmp))) ||
(ACPI_SUCCESS(acpi_get_handle(phandle, "_GTM", &tmp))) ||
(ACPI_SUCCESS(acpi_get_handle(phandle, "_STM", &tmp))) ||
(ACPI_SUCCESS(acpi_get_handle(phandle, "_SDD", &tmp))))
return 0;
 
return -ENODEV;
}
 
/*
* acpi_dock_match - see if a device has a _DCK method
*/
static int acpi_dock_match(struct acpi_device *device)
{
acpi_handle tmp;
return acpi_get_handle(device->handle, "_DCK", &tmp);
}
 
char *acpi_device_hid(struct acpi_device *device)
{
struct acpi_hardware_id *hid;
 
hid = list_first_entry(&device->pnp.ids, struct acpi_hardware_id, list);
return hid->id;
}
EXPORT_SYMBOL(acpi_device_hid);
 
static void acpi_add_id(struct acpi_device *device, const char *dev_id)
{
struct acpi_hardware_id *id;
 
id = kmalloc(sizeof(*id), GFP_KERNEL);
if (!id)
return;
 
id->id = kmalloc(strlen(dev_id) + 1, GFP_KERNEL);
if (!id->id) {
kfree(id);
return;
}
 
strcpy(id->id, dev_id);
list_add_tail(&id->list, &device->pnp.ids);
}
 
/*
* Old IBM workstations have a DSDT bug wherein the SMBus object
* lacks the SMBUS01 HID and the methods do not have the necessary "_"
* prefix. Work around this.
*/
static int acpi_ibm_smbus_match(struct acpi_device *device)
{
acpi_handle h_dummy;
struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL};
int result;
 
if (!dmi_name_in_vendors("IBM"))
return -ENODEV;
 
/* Look for SMBS object */
result = acpi_get_name(device->handle, ACPI_SINGLE_NAME, &path);
if (result)
return result;
 
if (strcmp("SMBS", path.pointer)) {
result = -ENODEV;
goto out;
}
 
/* Does it have the necessary (but misnamed) methods? */
result = -ENODEV;
if (ACPI_SUCCESS(acpi_get_handle(device->handle, "SBI", &h_dummy)) &&
ACPI_SUCCESS(acpi_get_handle(device->handle, "SBR", &h_dummy)) &&
ACPI_SUCCESS(acpi_get_handle(device->handle, "SBW", &h_dummy)))
result = 0;
out:
kfree(path.pointer);
return result;
}
 
static void acpi_device_set_id(struct acpi_device *device)
{
acpi_status status;
struct acpi_device_info *info;
struct acpica_device_id_list *cid_list;
int i;
 
switch (device->device_type) {
case ACPI_BUS_TYPE_DEVICE:
if (ACPI_IS_ROOT_DEVICE(device)) {
acpi_add_id(device, ACPI_SYSTEM_HID);
break;
}
 
status = acpi_get_object_info(device->handle, &info);
if (ACPI_FAILURE(status)) {
printk(KERN_ERR PREFIX "%s: Error reading device info\n", __func__);
return;
}
 
if (info->valid & ACPI_VALID_HID)
acpi_add_id(device, info->hardware_id.string);
if (info->valid & ACPI_VALID_CID) {
cid_list = &info->compatible_id_list;
for (i = 0; i < cid_list->count; i++)
acpi_add_id(device, cid_list->ids[i].string);
}
if (info->valid & ACPI_VALID_ADR) {
device->pnp.bus_address = info->address;
device->flags.bus_address = 1;
}
 
kfree(info);
 
/*
* Some devices don't reliably have _HIDs & _CIDs, so add
* synthetic HIDs to make sure drivers can find them.
*/
if (acpi_is_video_device(device))
acpi_add_id(device, ACPI_VIDEO_HID);
else if (ACPI_SUCCESS(acpi_bay_match(device)))
acpi_add_id(device, ACPI_BAY_HID);
else if (ACPI_SUCCESS(acpi_dock_match(device)))
acpi_add_id(device, ACPI_DOCK_HID);
else if (!acpi_ibm_smbus_match(device))
acpi_add_id(device, ACPI_SMBUS_IBM_HID);
else if (!acpi_device_hid(device) &&
ACPI_IS_ROOT_DEVICE(device->parent)) {
acpi_add_id(device, ACPI_BUS_HID); /* \_SB, LNXSYBUS */
strcpy(device->pnp.device_name, ACPI_BUS_DEVICE_NAME);
strcpy(device->pnp.device_class, ACPI_BUS_CLASS);
}
 
break;
case ACPI_BUS_TYPE_POWER:
acpi_add_id(device, ACPI_POWER_HID);
break;
case ACPI_BUS_TYPE_PROCESSOR:
acpi_add_id(device, ACPI_PROCESSOR_OBJECT_HID);
break;
case ACPI_BUS_TYPE_THERMAL:
acpi_add_id(device, ACPI_THERMAL_HID);
break;
case ACPI_BUS_TYPE_POWER_BUTTON:
acpi_add_id(device, ACPI_BUTTON_HID_POWERF);
break;
case ACPI_BUS_TYPE_SLEEP_BUTTON:
acpi_add_id(device, ACPI_BUTTON_HID_SLEEPF);
break;
}
 
/*
* We build acpi_devices for some objects that don't have _HID or _CID,
* e.g., PCI bridges and slots. Drivers can't bind to these objects,
* but we do use them indirectly by traversing the acpi_device tree.
* This generic ID isn't useful for driver binding, but it provides
* the useful property that "every acpi_device has an ID."
*/
if (list_empty(&device->pnp.ids))
acpi_add_id(device, "device");
}
 
static int acpi_device_set_context(struct acpi_device *device)
{
acpi_status status;
 
/*
* Context
* -------
* Attach this 'struct acpi_device' to the ACPI object. This makes
* resolutions from handle->device very efficient. Fixed hardware
* devices have no handles, so we skip them.
*/
if (!device->handle)
return 0;
 
status = acpi_attach_data(device->handle,
acpi_bus_data_handler, device);
if (ACPI_SUCCESS(status))
return 0;
 
printk(KERN_ERR PREFIX "Error attaching device data\n");
return -ENODEV;
}
 
static int acpi_bus_remove(struct acpi_device *dev, int rmdevice)
{
if (!dev)
return -EINVAL;
 
dev->removal_type = ACPI_BUS_REMOVAL_EJECT;
device_release_driver(&dev->dev);
 
if (!rmdevice)
return 0;
 
/*
* unbind _ADR-Based Devices when hot removal
*/
if (dev->flags.bus_address) {
if ((dev->parent) && (dev->parent->ops.unbind))
dev->parent->ops.unbind(dev);
}
acpi_device_unregister(dev, ACPI_BUS_REMOVAL_EJECT);
 
return 0;
}
 
static int acpi_add_single_object(struct acpi_device **child,
acpi_handle handle, int type,
unsigned long long sta,
struct acpi_bus_ops *ops)
{
int result;
struct acpi_device *device;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 
device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL);
if (!device) {
printk(KERN_ERR PREFIX "Memory allocation error\n");
return -ENOMEM;
}
 
INIT_LIST_HEAD(&device->pnp.ids);
device->device_type = type;
device->handle = handle;
device->parent = acpi_bus_get_parent(handle);
device->bus_ops = *ops; /* workround for not call .start */
STRUCT_TO_INT(device->status) = sta;
 
acpi_device_get_busid(device);
 
/*
* Flags
* -----
* Note that we only look for object handles -- cannot evaluate objects
* until we know the device is present and properly initialized.
*/
result = acpi_bus_get_flags(device);
if (result)
goto end;
 
/*
* Initialize Device
* -----------------
* TBD: Synch with Core's enumeration/initialization process.
*/
acpi_device_set_id(device);
 
/*
* Power Management
* ----------------
*/
if (device->flags.power_manageable) {
result = acpi_bus_get_power_flags(device);
if (result)
goto end;
}
 
/*
* Wakeup device management
*-----------------------
*/
if (device->flags.wake_capable) {
result = acpi_bus_get_wakeup_device_flags(device);
if (result)
goto end;
}
 
/*
* Performance Management
* ----------------------
*/
if (device->flags.performance_manageable) {
result = acpi_bus_get_perf_flags(device);
if (result)
goto end;
}
 
if ((result = acpi_device_set_context(device)))
goto end;
 
result = acpi_device_register(device);
 
/*
* Bind _ADR-Based Devices when hot add
*/
if (device->flags.bus_address) {
if (device->parent && device->parent->ops.bind)
device->parent->ops.bind(device);
}
 
end:
if (!result) {
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Adding %s [%s] parent %s\n", dev_name(&device->dev),
(char *) buffer.pointer,
device->parent ? dev_name(&device->parent->dev) :
"(null)"));
kfree(buffer.pointer);
*child = device;
} else
acpi_device_release(&device->dev);
 
return result;
}
 
#define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \
ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING)
 
static int acpi_bus_type_and_status(acpi_handle handle, int *type,
unsigned long long *sta)
{
acpi_status status;
acpi_object_type acpi_type;
 
status = acpi_get_type(handle, &acpi_type);
if (ACPI_FAILURE(status))
return -ENODEV;
 
switch (acpi_type) {
case ACPI_TYPE_ANY: /* for ACPI_ROOT_OBJECT */
case ACPI_TYPE_DEVICE:
*type = ACPI_BUS_TYPE_DEVICE;
status = acpi_bus_get_status_handle(handle, sta);
if (ACPI_FAILURE(status))
return -ENODEV;
break;
case ACPI_TYPE_PROCESSOR:
*type = ACPI_BUS_TYPE_PROCESSOR;
status = acpi_bus_get_status_handle(handle, sta);
if (ACPI_FAILURE(status))
return -ENODEV;
break;
case ACPI_TYPE_THERMAL:
*type = ACPI_BUS_TYPE_THERMAL;
*sta = ACPI_STA_DEFAULT;
break;
case ACPI_TYPE_POWER:
*type = ACPI_BUS_TYPE_POWER;
*sta = ACPI_STA_DEFAULT;
break;
default:
return -ENODEV;
}
 
return 0;
}
 
static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl,
void *context, void **return_value)
{
struct acpi_bus_ops *ops = context;
int type;
unsigned long long sta;
struct acpi_device *device;
acpi_status status;
int result;
 
result = acpi_bus_type_and_status(handle, &type, &sta);
if (result)
return AE_OK;
 
if (!(sta & ACPI_STA_DEVICE_PRESENT) &&
!(sta & ACPI_STA_DEVICE_FUNCTIONING))
return AE_CTRL_DEPTH;
 
/*
* We may already have an acpi_device from a previous enumeration. If
* so, we needn't add it again, but we may still have to start it.
*/
device = NULL;
acpi_bus_get_device(handle, &device);
if (ops->acpi_op_add && !device)
acpi_add_single_object(&device, handle, type, sta, ops);
 
if (!device)
return AE_CTRL_DEPTH;
 
if (ops->acpi_op_start && !(ops->acpi_op_add)) {
status = acpi_start_single_object(device);
if (ACPI_FAILURE(status))
return AE_CTRL_DEPTH;
}
 
if (!*return_value)
*return_value = device;
return AE_OK;
}
 
static int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops,
struct acpi_device **child)
{
acpi_status status;
void *device = NULL;
 
status = acpi_bus_check_add(handle, 0, ops, &device);
if (ACPI_SUCCESS(status))
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
acpi_bus_check_add, NULL, ops, &device);
 
if (child)
*child = device;
 
if (device)
return 0;
else
return -ENODEV;
}
 
/*
* acpi_bus_add and acpi_bus_start
*
* scan a given ACPI tree and (probably recently hot-plugged)
* create and add or starts found devices.
*
* If no devices were found -ENODEV is returned which does not
* mean that this is a real error, there just have been no suitable
* ACPI objects in the table trunk from which the kernel could create
* a device and add/start an appropriate driver.
*/
 
int
acpi_bus_add(struct acpi_device **child,
struct acpi_device *parent, acpi_handle handle, int type)
{
struct acpi_bus_ops ops;
 
memset(&ops, 0, sizeof(ops));
ops.acpi_op_add = 1;
 
return acpi_bus_scan(handle, &ops, child);
}
EXPORT_SYMBOL(acpi_bus_add);
 
int acpi_bus_start(struct acpi_device *device)
{
struct acpi_bus_ops ops;
 
if (!device)
return -EINVAL;
 
memset(&ops, 0, sizeof(ops));
ops.acpi_op_start = 1;
 
return acpi_bus_scan(device->handle, &ops, NULL);
}
EXPORT_SYMBOL(acpi_bus_start);
 
int acpi_bus_trim(struct acpi_device *start, int rmdevice)
{
acpi_status status;
struct acpi_device *parent, *child;
acpi_handle phandle, chandle;
acpi_object_type type;
u32 level = 1;
int err = 0;
 
parent = start;
phandle = start->handle;
child = chandle = NULL;
 
while ((level > 0) && parent && (!err)) {
status = acpi_get_next_object(ACPI_TYPE_ANY, phandle,
chandle, &chandle);
 
/*
* If this scope is exhausted then move our way back up.
*/
if (ACPI_FAILURE(status)) {
level--;
chandle = phandle;
acpi_get_parent(phandle, &phandle);
child = parent;
parent = parent->parent;
 
if (level == 0)
err = acpi_bus_remove(child, rmdevice);
else
err = acpi_bus_remove(child, 1);
 
continue;
}
 
status = acpi_get_type(chandle, &type);
if (ACPI_FAILURE(status)) {
continue;
}
/*
* If there is a device corresponding to chandle then
* parse it (depth-first).
*/
if (acpi_bus_get_device(chandle, &child) == 0) {
level++;
phandle = chandle;
chandle = NULL;
parent = child;
}
continue;
}
return err;
}
EXPORT_SYMBOL_GPL(acpi_bus_trim);
 
static int acpi_bus_scan_fixed(void)
{
int result = 0;
struct acpi_device *device = NULL;
struct acpi_bus_ops ops;
 
memset(&ops, 0, sizeof(ops));
ops.acpi_op_add = 1;
ops.acpi_op_start = 1;
 
/*
* Enumerate all fixed-feature devices.
*/
if ((acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON) == 0) {
result = acpi_add_single_object(&device, NULL,
ACPI_BUS_TYPE_POWER_BUTTON,
ACPI_STA_DEFAULT,
&ops);
}
 
if ((acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON) == 0) {
result = acpi_add_single_object(&device, NULL,
ACPI_BUS_TYPE_SLEEP_BUTTON,
ACPI_STA_DEFAULT,
&ops);
}
 
return result;
}
 
int __init acpi_scan_init(void)
{
int result;
struct acpi_bus_ops ops;
 
memset(&ops, 0, sizeof(ops));
ops.acpi_op_add = 1;
ops.acpi_op_start = 1;
 
result = bus_register(&acpi_bus_type);
if (result) {
/* We don't want to quit even if we failed to add suspend/resume */
printk(KERN_ERR PREFIX "Could not register bus type\n");
}
 
/*
* Enumerate devices in the ACPI namespace.
*/
result = acpi_bus_scan(ACPI_ROOT_OBJECT, &ops, &acpi_root);
 
if (!result)
result = acpi_bus_scan_fixed();
 
if (result)
acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL);
 
return result;
}