core.c 4.47 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 * 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>
David Brownell's avatar
David Brownell committed
16
#include <linux/dma-mapping.h>
Linus Torvalds's avatar
Linus Torvalds committed
17 18 19 20 21 22 23

#include "base.h"

static LIST_HEAD(pnp_protocols);
LIST_HEAD(pnp_global);
DEFINE_SPINLOCK(pnp_lock);

24 25 26 27 28 29 30 31
/*
 * 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);

Linus Torvalds's avatar
Linus Torvalds committed
32 33 34 35
void *pnp_alloc(long size)
{
	void *result;

36
	result = kzalloc(size, GFP_KERNEL);
37
	if (!result) {
Linus Torvalds's avatar
Linus Torvalds committed
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
		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;
53
	struct list_head *pos;
Linus Torvalds's avatar
Linus Torvalds committed
54 55 56 57 58 59 60

	INIT_LIST_HEAD(&protocol->devices);
	INIT_LIST_HEAD(&protocol->cards);
	nodenum = 0;
	spin_lock(&pnp_lock);

	/* assign the lowest unused number */
61 62 63
	list_for_each(pos, &pnp_protocols) {
		struct pnp_protocol *cur = to_pnp_protocol(pos);
		if (cur->number == nodenum) {
Linus Torvalds's avatar
Linus Torvalds committed
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
			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)
{
91 92
	struct pnp_id *id;
	struct pnp_id *next;
Bjorn Helgaas's avatar
Bjorn Helgaas committed
93

Linus Torvalds's avatar
Linus Torvalds committed
94 95 96 97 98 99 100 101 102 103
	id = dev->id;
	while (id) {
		next = id->next;
		kfree(id);
		id = next;
	}
}

static void pnp_release_device(struct device *dmdev)
{
104
	struct pnp_dev *dev = to_pnp_dev(dmdev);
Bjorn Helgaas's avatar
Bjorn Helgaas committed
105

Linus Torvalds's avatar
Linus Torvalds committed
106 107 108
	pnp_free_option(dev->independent);
	pnp_free_option(dev->dependent);
	pnp_free_ids(dev);
109
	kfree(dev->res);
Linus Torvalds's avatar
Linus Torvalds committed
110 111 112
	kfree(dev);
}

Bjorn Helgaas's avatar
Bjorn Helgaas committed
113
struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *protocol, int id, char *pnpid)
Linus Torvalds's avatar
Linus Torvalds committed
114
{
Bjorn Helgaas's avatar
Bjorn Helgaas committed
115 116
	struct pnp_dev *dev;
	struct pnp_id *dev_id;
Bjorn Helgaas's avatar
Bjorn Helgaas committed
117

Bjorn Helgaas's avatar
Bjorn Helgaas committed
118 119 120 121
	dev = kzalloc(sizeof(struct pnp_dev), GFP_KERNEL);
	if (!dev)
		return NULL;

122 123 124 125 126 127
	dev->res = kzalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
	if (!dev->res) {
		kfree(dev);
		return NULL;
	}

Bjorn Helgaas's avatar
Bjorn Helgaas committed
128 129 130 131 132
	dev->protocol = protocol;
	dev->number = id;
	dev->dma_mask = DMA_24BIT_MASK;

	dev->dev.parent = &dev->protocol->dev;
Linus Torvalds's avatar
Linus Torvalds committed
133
	dev->dev.bus = &pnp_bus_type;
David Brownell's avatar
David Brownell committed
134
	dev->dev.dma_mask = &dev->dma_mask;
Bjorn Helgaas's avatar
Bjorn Helgaas committed
135
	dev->dev.coherent_dma_mask = dev->dma_mask;
Linus Torvalds's avatar
Linus Torvalds committed
136
	dev->dev.release = &pnp_release_device;
Bjorn Helgaas's avatar
Bjorn Helgaas committed
137 138 139 140 141 142

	sprintf(dev->dev.bus_id, "%02x:%02x", dev->protocol->number,
		dev->number);

	dev_id = pnp_add_id(dev, pnpid);
	if (!dev_id) {
143
		kfree(dev->res);
Bjorn Helgaas's avatar
Bjorn Helgaas committed
144 145 146 147 148 149 150 151 152 153 154 155
		kfree(dev);
		return NULL;
	}

	return dev;
}

int __pnp_add_device(struct pnp_dev *dev)
{
	int ret;

	pnp_fixup_device(dev);
Linus Torvalds's avatar
Linus Torvalds committed
156 157 158 159 160 161 162
	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);
163 164 165 166 167
	if (ret)
		return ret;

	pnp_interface_attach_device(dev);
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
168 169 170 171 172 173 174 175 176 177
}

/*
 * 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)
{
178 179
	int ret;

180
	if (dev->card)
Linus Torvalds's avatar
Linus Torvalds committed
181
		return -EINVAL;
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198

	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;
Linus Torvalds's avatar
Linus Torvalds committed
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
}

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);