2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* core.c - contains all core device and protocol registration functions
|
|
|
|
*
|
|
|
|
* Copyright 2002 Adam Belay <ambx1@neo.rr.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/pnp.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/errno.h>
|
2007-05-08 09:25:29 +02:00
|
|
|
#include <linux/dma-mapping.h>
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
#include "base.h"
|
|
|
|
|
|
|
|
static LIST_HEAD(pnp_protocols);
|
|
|
|
LIST_HEAD(pnp_global);
|
|
|
|
DEFINE_SPINLOCK(pnp_lock);
|
|
|
|
|
2007-05-08 09:35:54 +02:00
|
|
|
/*
|
|
|
|
* ACPI or PNPBIOS should tell us about all platform devices, so we can
|
|
|
|
* skip some blind probes. ISAPNP typically enumerates only plug-in ISA
|
|
|
|
* devices, not built-in things like COM ports.
|
|
|
|
*/
|
|
|
|
int pnp_platform_devices;
|
|
|
|
EXPORT_SYMBOL(pnp_platform_devices);
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
void *pnp_alloc(long size)
|
|
|
|
{
|
|
|
|
void *result;
|
|
|
|
|
some kmalloc/memset ->kzalloc (tree wide)
Transform some calls to kmalloc/memset to a single kzalloc (or kcalloc).
Here is a short excerpt of the semantic patch performing
this transformation:
@@
type T2;
expression x;
identifier f,fld;
expression E;
expression E1,E2;
expression e1,e2,e3,y;
statement S;
@@
x =
- kmalloc
+ kzalloc
(E1,E2)
... when != \(x->fld=E;\|y=f(...,x,...);\|f(...,x,...);\|x=E;\|while(...) S\|for(e1;e2;e3) S\)
- memset((T2)x,0,E1);
@@
expression E1,E2,E3;
@@
- kzalloc(E1 * E2,E3)
+ kcalloc(E1,E2,E3)
[akpm@linux-foundation.org: get kcalloc args the right way around]
Signed-off-by: Yoann Padioleau <padator@wanadoo.fr>
Cc: Richard Henderson <rth@twiddle.net>
Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Acked-by: Russell King <rmk@arm.linux.org.uk>
Cc: Bryan Wu <bryan.wu@analog.com>
Acked-by: Jiri Slaby <jirislaby@gmail.com>
Cc: Dave Airlie <airlied@linux.ie>
Acked-by: Roland Dreier <rolandd@cisco.com>
Cc: Jiri Kosina <jkosina@suse.cz>
Acked-by: Dmitry Torokhov <dtor@mail.ru>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Acked-by: Pierre Ossman <drzeus-list@drzeus.cx>
Cc: Jeff Garzik <jeff@garzik.org>
Cc: "David S. Miller" <davem@davemloft.net>
Acked-by: Greg KH <greg@kroah.com>
Cc: James Bottomley <James.Bottomley@steeleye.com>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-19 10:49:03 +02:00
|
|
|
result = kzalloc(size, GFP_KERNEL);
|
2007-07-26 19:41:20 +02:00
|
|
|
if (!result) {
|
2005-04-17 00:20:36 +02:00
|
|
|
printk(KERN_ERR "pnp: Out of Memory\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* pnp_protocol_register - adds a pnp protocol to the pnp layer
|
|
|
|
* @protocol: pointer to the corresponding pnp_protocol structure
|
|
|
|
*
|
|
|
|
* Ex protocols: ISAPNP, PNPBIOS, etc
|
|
|
|
*/
|
|
|
|
int pnp_register_protocol(struct pnp_protocol *protocol)
|
|
|
|
{
|
|
|
|
int nodenum;
|
2007-07-26 19:41:20 +02:00
|
|
|
struct list_head *pos;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
INIT_LIST_HEAD(&protocol->devices);
|
|
|
|
INIT_LIST_HEAD(&protocol->cards);
|
|
|
|
nodenum = 0;
|
|
|
|
spin_lock(&pnp_lock);
|
|
|
|
|
|
|
|
/* assign the lowest unused number */
|
2007-07-26 19:41:20 +02:00
|
|
|
list_for_each(pos, &pnp_protocols) {
|
|
|
|
struct pnp_protocol *cur = to_pnp_protocol(pos);
|
|
|
|
if (cur->number == nodenum) {
|
2005-04-17 00:20:36 +02:00
|
|
|
pos = &pnp_protocols;
|
|
|
|
nodenum++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
list_add_tail(&protocol->protocol_list, &pnp_protocols);
|
|
|
|
spin_unlock(&pnp_lock);
|
|
|
|
|
|
|
|
protocol->number = nodenum;
|
|
|
|
sprintf(protocol->dev.bus_id, "pnp%d", nodenum);
|
|
|
|
return device_register(&protocol->dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* pnp_protocol_unregister - removes a pnp protocol from the pnp layer
|
|
|
|
* @protocol: pointer to the corresponding pnp_protocol structure
|
|
|
|
*/
|
|
|
|
void pnp_unregister_protocol(struct pnp_protocol *protocol)
|
|
|
|
{
|
|
|
|
spin_lock(&pnp_lock);
|
|
|
|
list_del(&protocol->protocol_list);
|
|
|
|
spin_unlock(&pnp_lock);
|
|
|
|
device_unregister(&protocol->dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pnp_free_ids(struct pnp_dev *dev)
|
|
|
|
{
|
2007-07-26 19:41:20 +02:00
|
|
|
struct pnp_id *id;
|
|
|
|
struct pnp_id *next;
|
2007-07-26 19:41:21 +02:00
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
id = dev->id;
|
|
|
|
while (id) {
|
|
|
|
next = id->next;
|
|
|
|
kfree(id);
|
|
|
|
id = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pnp_release_device(struct device *dmdev)
|
|
|
|
{
|
2007-07-26 19:41:20 +02:00
|
|
|
struct pnp_dev *dev = to_pnp_dev(dmdev);
|
2007-07-26 19:41:21 +02:00
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
pnp_free_option(dev->independent);
|
|
|
|
pnp_free_option(dev->dependent);
|
|
|
|
pnp_free_ids(dev);
|
|
|
|
kfree(dev);
|
|
|
|
}
|
|
|
|
|
2008-04-29 00:33:54 +02:00
|
|
|
struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *protocol, int id, char *pnpid)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2008-04-29 00:33:54 +02:00
|
|
|
struct pnp_dev *dev;
|
|
|
|
struct pnp_id *dev_id;
|
2007-07-26 19:41:21 +02:00
|
|
|
|
2008-04-29 00:33:54 +02:00
|
|
|
dev = kzalloc(sizeof(struct pnp_dev), GFP_KERNEL);
|
|
|
|
if (!dev)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
dev->protocol = protocol;
|
|
|
|
dev->number = id;
|
|
|
|
dev->dma_mask = DMA_24BIT_MASK;
|
|
|
|
|
|
|
|
dev->dev.parent = &dev->protocol->dev;
|
2005-04-17 00:20:36 +02:00
|
|
|
dev->dev.bus = &pnp_bus_type;
|
2007-05-08 09:25:29 +02:00
|
|
|
dev->dev.dma_mask = &dev->dma_mask;
|
2008-04-29 00:33:54 +02:00
|
|
|
dev->dev.coherent_dma_mask = dev->dma_mask;
|
2005-04-17 00:20:36 +02:00
|
|
|
dev->dev.release = &pnp_release_device;
|
2008-04-29 00:33:54 +02:00
|
|
|
|
|
|
|
sprintf(dev->dev.bus_id, "%02x:%02x", dev->protocol->number,
|
|
|
|
dev->number);
|
|
|
|
|
|
|
|
dev_id = pnp_add_id(dev, pnpid);
|
|
|
|
if (!dev_id) {
|
|
|
|
kfree(dev);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return dev;
|
|
|
|
}
|
|
|
|
|
|
|
|
int __pnp_add_device(struct pnp_dev *dev)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
pnp_fixup_device(dev);
|
2005-04-17 00:20:36 +02:00
|
|
|
dev->status = PNP_READY;
|
|
|
|
spin_lock(&pnp_lock);
|
|
|
|
list_add_tail(&dev->global_list, &pnp_global);
|
|
|
|
list_add_tail(&dev->protocol_list, &dev->protocol->devices);
|
|
|
|
spin_unlock(&pnp_lock);
|
|
|
|
|
|
|
|
ret = device_register(&dev->dev);
|
2007-10-17 08:31:12 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
pnp_interface_attach_device(dev);
|
|
|
|
return 0;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pnp_add_device - adds a pnp device to the pnp layer
|
|
|
|
* @dev: pointer to dev to add
|
|
|
|
*
|
|
|
|
* adds to driver model, name database, fixups, interface, etc.
|
|
|
|
*/
|
|
|
|
int pnp_add_device(struct pnp_dev *dev)
|
|
|
|
{
|
2007-10-17 08:31:12 +02:00
|
|
|
int ret;
|
|
|
|
|
2007-08-15 18:32:13 +02:00
|
|
|
if (dev->card)
|
2005-04-17 00:20:36 +02:00
|
|
|
return -EINVAL;
|
2007-10-17 08:31:12 +02:00
|
|
|
|
|
|
|
ret = __pnp_add_device(dev);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
#ifdef CONFIG_PNP_DEBUG
|
|
|
|
{
|
|
|
|
struct pnp_id *id;
|
|
|
|
|
|
|
|
dev_printk(KERN_DEBUG, &dev->dev, "%s device, IDs",
|
|
|
|
dev->protocol->name);
|
|
|
|
for (id = dev->id; id; id = id->next)
|
|
|
|
printk(" %s", id->id);
|
|
|
|
printk(" (%s)\n", dev->active ? "active" : "disabled");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return 0;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void __pnp_remove_device(struct pnp_dev *dev)
|
|
|
|
{
|
|
|
|
spin_lock(&pnp_lock);
|
|
|
|
list_del(&dev->global_list);
|
|
|
|
list_del(&dev->protocol_list);
|
|
|
|
spin_unlock(&pnp_lock);
|
|
|
|
device_unregister(&dev->dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __init pnp_init(void)
|
|
|
|
{
|
|
|
|
printk(KERN_INFO "Linux Plug and Play Support v0.97 (c) Adam Belay\n");
|
|
|
|
return bus_register(&pnp_bus_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
subsys_initcall(pnp_init);
|