1
linux/drivers/pnp/driver.c

252 lines
5.0 KiB
C
Raw Normal View History

/*
* driver.c - device id matching, driver model, etc.
*
* Copyright 2002 Adam Belay <ambx1@neo.rr.com>
*
*/
#include <linux/config.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/pnp.h>
#include "base.h"
static int compare_func(const char *ida, const char *idb)
{
int i;
/* we only need to compare the last 4 chars */
for (i=3; i<7; i++)
{
if (ida[i] != 'X' &&
idb[i] != 'X' &&
toupper(ida[i]) != toupper(idb[i]))
return 0;
}
return 1;
}
int compare_pnp_id(struct pnp_id *pos, const char *id)
{
if (!pos || !id || (strlen(id) != 7))
return 0;
if (memcmp(id,"ANYDEVS",7)==0)
return 1;
while (pos){
if (memcmp(pos->id,id,3)==0)
if (compare_func(pos->id,id)==1)
return 1;
pos = pos->next;
}
return 0;
}
static const struct pnp_device_id * match_device(struct pnp_driver *drv, struct pnp_dev *dev)
{
const struct pnp_device_id *drv_id = drv->id_table;
if (!drv_id)
return NULL;
while (*drv_id->id) {
if (compare_pnp_id(dev->id, drv_id->id))
return drv_id;
drv_id++;
}
return NULL;
}
int pnp_device_attach(struct pnp_dev *pnp_dev)
{
spin_lock(&pnp_lock);
if(pnp_dev->status != PNP_READY){
spin_unlock(&pnp_lock);
return -EBUSY;
}
pnp_dev->status = PNP_ATTACHED;
spin_unlock(&pnp_lock);
return 0;
}
void pnp_device_detach(struct pnp_dev *pnp_dev)
{
spin_lock(&pnp_lock);
if (pnp_dev->status == PNP_ATTACHED)
pnp_dev->status = PNP_READY;
spin_unlock(&pnp_lock);
pnp_disable_dev(pnp_dev);
}
static int pnp_device_probe(struct device *dev)
{
int error;
struct pnp_driver *pnp_drv;
struct pnp_dev *pnp_dev;
const struct pnp_device_id *dev_id = NULL;
pnp_dev = to_pnp_dev(dev);
pnp_drv = to_pnp_driver(dev->driver);
pnp_dbg("match found with the PnP device '%s' and the driver '%s'", dev->bus_id,pnp_drv->name);
error = pnp_device_attach(pnp_dev);
if (error < 0)
return error;
if (pnp_dev->active == 0) {
if (!(pnp_drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE)) {
error = pnp_activate_dev(pnp_dev);
if (error < 0)
return error;
}
} else if ((pnp_drv->flags & PNP_DRIVER_RES_DISABLE)
== PNP_DRIVER_RES_DISABLE) {
error = pnp_disable_dev(pnp_dev);
if (error < 0)
return error;
}
error = 0;
if (pnp_drv->probe) {
dev_id = match_device(pnp_drv, pnp_dev);
if (dev_id != NULL)
error = pnp_drv->probe(pnp_dev, dev_id);
}
if (error >= 0){
pnp_dev->driver = pnp_drv;
error = 0;
} else
goto fail;
return error;
fail:
pnp_device_detach(pnp_dev);
return error;
}
static int pnp_device_remove(struct device *dev)
{
struct pnp_dev * pnp_dev = to_pnp_dev(dev);
struct pnp_driver * drv = pnp_dev->driver;
if (drv) {
if (drv->remove)
drv->remove(pnp_dev);
pnp_dev->driver = NULL;
}
pnp_device_detach(pnp_dev);
return 0;
}
static int pnp_bus_match(struct device *dev, struct device_driver *drv)
{
struct pnp_dev * pnp_dev = to_pnp_dev(dev);
struct pnp_driver * pnp_drv = to_pnp_driver(drv);
if (match_device(pnp_drv, pnp_dev) == NULL)
return 0;
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,
.probe = pnp_device_probe,
.remove = pnp_device_remove,
.suspend = pnp_bus_suspend,
.resume = pnp_bus_resume,
};
int pnp_register_driver(struct pnp_driver *drv)
{
pnp_dbg("the driver '%s' has been registered", drv->name);
drv->driver.name = drv->name;
drv->driver.bus = &pnp_bus_type;
return driver_register(&drv->driver);
}
void pnp_unregister_driver(struct pnp_driver *drv)
{
driver_unregister(&drv->driver);
pnp_dbg("the driver '%s' has been unregistered", drv->name);
}
/**
* pnp_add_id - adds an EISA id to the specified device
* @id: pointer to a pnp_id structure
* @dev: pointer to the desired device
*
*/
int pnp_add_id(struct pnp_id *id, struct pnp_dev *dev)
{
struct pnp_id *ptr;
if (!id)
return -EINVAL;
if (!dev)
return -EINVAL;
id->next = NULL;
ptr = dev->id;
while (ptr && ptr->next)
ptr = ptr->next;
if (ptr)
ptr->next = id;
else
dev->id = id;
return 0;
}
EXPORT_SYMBOL(pnp_register_driver);
EXPORT_SYMBOL(pnp_unregister_driver);
[PATCH] drivers/pnp/: cleanups This patch contains the following possible cleanups: - make needlessly global code static - #if 0 the following unused global function: - core.c: pnp_remove_device - #if 0 the following unneeded EXPORT_SYMBOL's: - card.c: pnp_add_card - card.c: pnp_remove_card - card.c: pnp_add_card_device - card.c: pnp_remove_card_device - card.c: pnp_add_card_id - core.c: pnp_register_protocol - core.c: pnp_unregister_protocol - core.c: pnp_add_device - core.c: pnp_remove_device - pnpacpi/core.c: pnpacpi_protocol - driver.c: pnp_add_id - isapnp/core.c: isapnp_read_byte - manager.c: pnp_auto_config_dev - resource.c: pnp_register_dependent_option - resource.c: pnp_register_independent_option - resource.c: pnp_register_irq_resource - resource.c: pnp_register_dma_resource - resource.c: pnp_register_port_resource - resource.c: pnp_register_mem_resource Note that this patch #if 0's exactly one functions and removes no functions. Most it does is the #if 0 of EXPORT_SYMBOL's, so if any modular code will use any of them, re-adding will be trivial. Modular ISAPnP might be interesting in some cases, but this is more legacy code. If someone would work on it to sort all the issues out (starting with the point that most users of __ISAPNP__ will have to be fixed) re-enabling the required EXPORT_SYMBOL's won't be hard for him. Signed-off-by: Adrian Bunk <bunk@stusta.de> Cc: Adam Belay <ambx1@neo.rr.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-07 02:01:48 -07:00
#if 0
EXPORT_SYMBOL(pnp_add_id);
[PATCH] drivers/pnp/: cleanups This patch contains the following possible cleanups: - make needlessly global code static - #if 0 the following unused global function: - core.c: pnp_remove_device - #if 0 the following unneeded EXPORT_SYMBOL's: - card.c: pnp_add_card - card.c: pnp_remove_card - card.c: pnp_add_card_device - card.c: pnp_remove_card_device - card.c: pnp_add_card_id - core.c: pnp_register_protocol - core.c: pnp_unregister_protocol - core.c: pnp_add_device - core.c: pnp_remove_device - pnpacpi/core.c: pnpacpi_protocol - driver.c: pnp_add_id - isapnp/core.c: isapnp_read_byte - manager.c: pnp_auto_config_dev - resource.c: pnp_register_dependent_option - resource.c: pnp_register_independent_option - resource.c: pnp_register_irq_resource - resource.c: pnp_register_dma_resource - resource.c: pnp_register_port_resource - resource.c: pnp_register_mem_resource Note that this patch #if 0's exactly one functions and removes no functions. Most it does is the #if 0 of EXPORT_SYMBOL's, so if any modular code will use any of them, re-adding will be trivial. Modular ISAPnP might be interesting in some cases, but this is more legacy code. If someone would work on it to sort all the issues out (starting with the point that most users of __ISAPNP__ will have to be fixed) re-enabling the required EXPORT_SYMBOL's won't be hard for him. Signed-off-by: Adrian Bunk <bunk@stusta.de> Cc: Adam Belay <ambx1@neo.rr.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-07 02:01:48 -07:00
#endif
EXPORT_SYMBOL(pnp_device_attach);
EXPORT_SYMBOL(pnp_device_detach);