diff options
Diffstat (limited to 'drivers/pnp')
-rw-r--r-- | drivers/pnp/card.c | 25 | ||||
-rw-r--r-- | drivers/pnp/driver.c | 47 | ||||
-rw-r--r-- | drivers/pnp/manager.c | 78 | ||||
-rw-r--r-- | drivers/pnp/pnpbios/bioscalls.c | 45 | ||||
-rw-r--r-- | drivers/pnp/pnpbios/core.c | 8 |
5 files changed, 152 insertions, 51 deletions
diff --git a/drivers/pnp/card.c b/drivers/pnp/card.c index bd7c966ea2d..0ecbe4edbec 100644 --- a/drivers/pnp/card.c +++ b/drivers/pnp/card.c @@ -69,6 +69,7 @@ static int card_probe(struct pnp_card * card, struct pnp_card_driver * drv) return 0; clink->card = card; clink->driver = drv; + clink->pm_state = PMSG_ON; if (drv->probe) { if (drv->probe(clink, id)>=0) return 1; @@ -333,6 +334,28 @@ void pnp_release_card_device(struct pnp_dev * dev) up_write(&dev->dev.bus->subsys.rwsem); } +/* + * suspend/resume callbacks + */ +static int card_suspend(struct pnp_dev *dev, pm_message_t state) +{ + struct pnp_card_link *link = dev->card_link; + if (link->pm_state.event == state.event) + return 0; + link->pm_state = state; + return link->driver->suspend(link, state); +} + +static int card_resume(struct pnp_dev *dev) +{ + struct pnp_card_link *link = dev->card_link; + if (link->pm_state.event == PM_EVENT_ON) + return 0; + link->pm_state = PMSG_ON; + link->driver->resume(link); + return 0; +} + /** * pnp_register_card_driver - registers a PnP card driver with the PnP Layer * @drv: pointer to the driver to register @@ -348,6 +371,8 @@ int pnp_register_card_driver(struct pnp_card_driver * drv) drv->link.flags = drv->flags; drv->link.probe = NULL; drv->link.remove = &card_remove_first; + drv->link.suspend = drv->suspend ? card_suspend : NULL; + drv->link.resume = drv->resume ? card_resume : NULL; spin_lock(&pnp_lock); list_add_tail(&drv->global_list, &pnp_card_drivers); diff --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c index d3ccce706ab..15fb758a9e5 100644 --- a/drivers/pnp/driver.c +++ b/drivers/pnp/driver.c @@ -146,10 +146,57 @@ static int pnp_bus_match(struct device *dev, struct device_driver *drv) return 1; } +static int pnp_bus_suspend(struct device *dev, pm_message_t state) +{ + struct pnp_dev * pnp_dev = to_pnp_dev(dev); + struct pnp_driver * pnp_drv = pnp_dev->driver; + int error; + + if (!pnp_drv) + return 0; + + if (pnp_drv->suspend) { + error = pnp_drv->suspend(pnp_dev, state); + if (error) + return error; + } + + if (!(pnp_drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE) && + pnp_can_disable(pnp_dev)) { + error = pnp_stop_dev(pnp_dev); + if (error) + return error; + } + + return 0; +} + +static int pnp_bus_resume(struct device *dev) +{ + struct pnp_dev * pnp_dev = to_pnp_dev(dev); + struct pnp_driver * pnp_drv = pnp_dev->driver; + int error; + + if (!pnp_drv) + return 0; + + if (!(pnp_drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE)) { + error = pnp_start_dev(pnp_dev); + if (error) + return error; + } + + if (pnp_drv->resume) + return pnp_drv->resume(pnp_dev); + + return 0; +} struct bus_type pnp_bus_type = { .name = "pnp", .match = pnp_bus_match, + .suspend = pnp_bus_suspend, + .resume = pnp_bus_resume, }; diff --git a/drivers/pnp/manager.c b/drivers/pnp/manager.c index 261668618b2..c4256aa32bc 100644 --- a/drivers/pnp/manager.c +++ b/drivers/pnp/manager.c @@ -470,6 +470,53 @@ int pnp_auto_config_dev(struct pnp_dev *dev) } /** + * pnp_start_dev - low-level start of the PnP device + * @dev: pointer to the desired device + * + * assumes that resources have alread been allocated + */ + +int pnp_start_dev(struct pnp_dev *dev) +{ + if (!pnp_can_write(dev)) { + pnp_info("Device %s does not supported activation.", dev->dev.bus_id); + return -EINVAL; + } + + if (dev->protocol->set(dev, &dev->res)<0) { + pnp_err("Failed to activate device %s.", dev->dev.bus_id); + return -EIO; + } + + pnp_info("Device %s activated.", dev->dev.bus_id); + + return 0; +} + +/** + * pnp_stop_dev - low-level disable of the PnP device + * @dev: pointer to the desired device + * + * does not free resources + */ + +int pnp_stop_dev(struct pnp_dev *dev) +{ + if (!pnp_can_disable(dev)) { + pnp_info("Device %s does not supported disabling.", dev->dev.bus_id); + return -EINVAL; + } + if (dev->protocol->disable(dev)<0) { + pnp_err("Failed to disable device %s.", dev->dev.bus_id); + return -EIO; + } + + pnp_info("Device %s disabled.", dev->dev.bus_id); + + return 0; +} + +/** * pnp_activate_dev - activates a PnP device for use * @dev: pointer to the desired device * @@ -477,6 +524,8 @@ int pnp_auto_config_dev(struct pnp_dev *dev) */ int pnp_activate_dev(struct pnp_dev *dev) { + int error; + if (!dev) return -EINVAL; if (dev->active) { @@ -487,18 +536,11 @@ int pnp_activate_dev(struct pnp_dev *dev) if (pnp_auto_config_dev(dev)) return -EBUSY; - if (!pnp_can_write(dev)) { - pnp_info("Device %s does not supported activation.", dev->dev.bus_id); - return -EINVAL; - } - - if (dev->protocol->set(dev, &dev->res)<0) { - pnp_err("Failed to activate device %s.", dev->dev.bus_id); - return -EIO; - } + error = pnp_start_dev(dev); + if (error) + return error; dev->active = 1; - pnp_info("Device %s activated.", dev->dev.bus_id); return 1; } @@ -511,23 +553,19 @@ int pnp_activate_dev(struct pnp_dev *dev) */ int pnp_disable_dev(struct pnp_dev *dev) { + int error; + if (!dev) return -EINVAL; if (!dev->active) { return 0; /* the device is already disabled */ } - if (!pnp_can_disable(dev)) { - pnp_info("Device %s does not supported disabling.", dev->dev.bus_id); - return -EINVAL; - } - if (dev->protocol->disable(dev)<0) { - pnp_err("Failed to disable device %s.", dev->dev.bus_id); - return -EIO; - } + error = pnp_stop_dev(dev); + if (error) + return error; dev->active = 0; - pnp_info("Device %s disabled.", dev->dev.bus_id); /* release the resources so that other devices can use them */ down(&pnp_res_mutex); @@ -558,6 +596,8 @@ EXPORT_SYMBOL(pnp_manual_config_dev); #if 0 EXPORT_SYMBOL(pnp_auto_config_dev); #endif +EXPORT_SYMBOL(pnp_start_dev); +EXPORT_SYMBOL(pnp_stop_dev); EXPORT_SYMBOL(pnp_activate_dev); EXPORT_SYMBOL(pnp_disable_dev); EXPORT_SYMBOL(pnp_resource_change); diff --git a/drivers/pnp/pnpbios/bioscalls.c b/drivers/pnp/pnpbios/bioscalls.c index 6b7583f497d..a1f0b0ba2bf 100644 --- a/drivers/pnp/pnpbios/bioscalls.c +++ b/drivers/pnp/pnpbios/bioscalls.c @@ -31,15 +31,6 @@ static struct { } pnp_bios_callpoint; -/* The PnP BIOS entries in the GDT */ -#define PNP_GDT (GDT_ENTRY_PNPBIOS_BASE * 8) - -#define PNP_CS32 (PNP_GDT+0x00) /* segment for calling fn */ -#define PNP_CS16 (PNP_GDT+0x08) /* code segment for BIOS */ -#define PNP_DS (PNP_GDT+0x10) /* data segment for BIOS */ -#define PNP_TS1 (PNP_GDT+0x18) /* transfer data segment */ -#define PNP_TS2 (PNP_GDT+0x20) /* another data segment */ - /* * These are some opcodes for a "static asmlinkage" * As this code is *not* executed inside the linux kernel segment, but in a @@ -67,16 +58,11 @@ __asm__( ".previous \n" ); -#define Q_SET_SEL(cpu, selname, address, size) \ -do { \ -set_base(per_cpu(cpu_gdt_table,cpu)[(selname) >> 3], __va((u32)(address))); \ -set_limit(per_cpu(cpu_gdt_table,cpu)[(selname) >> 3], size); \ -} while(0) - #define Q2_SET_SEL(cpu, selname, address, size) \ do { \ -set_base(per_cpu(cpu_gdt_table,cpu)[(selname) >> 3], (u32)(address)); \ -set_limit(per_cpu(cpu_gdt_table,cpu)[(selname) >> 3], size); \ +struct desc_struct *gdt = get_cpu_gdt_table((cpu)); \ +set_base(gdt[(selname) >> 3], (u32)(address)); \ +set_limit(gdt[(selname) >> 3], size); \ } while(0) static struct desc_struct bad_bios_desc = { 0, 0x00409200 }; @@ -115,8 +101,8 @@ static inline u16 call_pnp_bios(u16 func, u16 arg1, u16 arg2, u16 arg3, return PNP_FUNCTION_NOT_SUPPORTED; cpu = get_cpu(); - save_desc_40 = per_cpu(cpu_gdt_table,cpu)[0x40 / 8]; - per_cpu(cpu_gdt_table,cpu)[0x40 / 8] = bad_bios_desc; + save_desc_40 = get_cpu_gdt_table(cpu)[0x40 / 8]; + get_cpu_gdt_table(cpu)[0x40 / 8] = bad_bios_desc; /* On some boxes IRQ's during PnP BIOS calls are deadly. */ spin_lock_irqsave(&pnp_bios_lock, flags); @@ -158,7 +144,7 @@ static inline u16 call_pnp_bios(u16 func, u16 arg1, u16 arg2, u16 arg3, ); spin_unlock_irqrestore(&pnp_bios_lock, flags); - per_cpu(cpu_gdt_table,cpu)[0x40 / 8] = save_desc_40; + get_cpu_gdt_table(cpu)[0x40 / 8] = save_desc_40; put_cpu(); /* If we get here and this is set then the PnP BIOS faulted on us. */ @@ -290,12 +276,15 @@ int pnp_bios_dev_node_info(struct pnp_dev_node_info *data) static int __pnp_bios_get_dev_node(u8 *nodenum, char boot, struct pnp_bios_node *data) { u16 status; + u16 tmp_nodenum; if (!pnp_bios_present()) return PNP_FUNCTION_NOT_SUPPORTED; if ( !boot && pnpbios_dont_use_current_config ) return PNP_FUNCTION_NOT_SUPPORTED; + tmp_nodenum = *nodenum; status = call_pnp_bios(PNP_GET_SYS_DEV_NODE, 0, PNP_TS1, 0, PNP_TS2, boot ? 2 : 1, PNP_DS, 0, - nodenum, sizeof(char), data, 65536); + &tmp_nodenum, sizeof(tmp_nodenum), data, 65536); + *nodenum = tmp_nodenum; return status; } @@ -535,10 +524,12 @@ void pnpbios_calls_init(union pnp_bios_install_struct *header) set_base(bad_bios_desc, __va((unsigned long)0x40 << 4)); _set_limit((char *)&bad_bios_desc, 4095 - (0x40 << 4)); - for(i=0; i < NR_CPUS; i++) - { - Q2_SET_SEL(i, PNP_CS32, &pnp_bios_callfunc, 64 * 1024); - Q_SET_SEL(i, PNP_CS16, header->fields.pm16cseg, 64 * 1024); - Q_SET_SEL(i, PNP_DS, header->fields.pm16dseg, 64 * 1024); - } + for (i = 0; i < NR_CPUS; i++) { + struct desc_struct *gdt = get_cpu_gdt_table(i); + if (!gdt) + continue; + set_base(gdt[GDT_ENTRY_PNPBIOS_CS32], &pnp_bios_callfunc); + set_base(gdt[GDT_ENTRY_PNPBIOS_CS16], __va(header->fields.pm16cseg)); + set_base(gdt[GDT_ENTRY_PNPBIOS_DS], __va(header->fields.pm16dseg)); + } } diff --git a/drivers/pnp/pnpbios/core.c b/drivers/pnp/pnpbios/core.c index f49674f0794..b154b3f52cb 100644 --- a/drivers/pnp/pnpbios/core.c +++ b/drivers/pnp/pnpbios/core.c @@ -56,7 +56,6 @@ #include <linux/mm.h> #include <linux/smp.h> #include <linux/slab.h> -#include <linux/kobject_uevent.h> #include <linux/completion.h> #include <linux/spinlock.h> #include <linux/dmi.h> @@ -106,8 +105,6 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info) char *argv [3], **envp, *buf, *scratch; int i = 0, value; - if (!hotplug_path [0]) - return -ENOENT; if (!current->fs->root) { return -EAGAIN; } @@ -119,8 +116,9 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info) return -ENOMEM; } - /* only one standardized param to hotplug command: type */ - argv [0] = hotplug_path; + /* FIXME: if there are actual users of this, it should be integrated into + * the driver core and use the usual infrastructure like sysfs and uevents */ + argv [0] = "/sbin/pnpbios"; argv [1] = "dock"; argv [2] = NULL; |