xref: /linux/drivers/pnp/driver.c (revision 9ce7677cfd7cd871adb457c80bea3b581b839641)
1 /*
2  * driver.c - device id matching, driver model, etc.
3  *
4  * Copyright 2002 Adam Belay <ambx1@neo.rr.com>
5  *
6  */
7 
8 #include <linux/config.h>
9 #include <linux/string.h>
10 #include <linux/list.h>
11 #include <linux/module.h>
12 #include <linux/ctype.h>
13 #include <linux/slab.h>
14 #include <linux/pnp.h>
15 #include "base.h"
16 
17 static int compare_func(const char *ida, const char *idb)
18 {
19 	int i;
20 	/* we only need to compare the last 4 chars */
21 	for (i=3; i<7; i++)
22 	{
23 		if (ida[i] != 'X' &&
24 		    idb[i] != 'X' &&
25 		    toupper(ida[i]) != toupper(idb[i]))
26 			return 0;
27 	}
28 	return 1;
29 }
30 
31 int compare_pnp_id(struct pnp_id *pos, const char *id)
32 {
33 	if (!pos || !id || (strlen(id) != 7))
34 		return 0;
35 	if (memcmp(id,"ANYDEVS",7)==0)
36 		return 1;
37 	while (pos){
38 		if (memcmp(pos->id,id,3)==0)
39 			if (compare_func(pos->id,id)==1)
40 				return 1;
41 		pos = pos->next;
42 	}
43 	return 0;
44 }
45 
46 static const struct pnp_device_id * match_device(struct pnp_driver *drv, struct pnp_dev *dev)
47 {
48 	const struct pnp_device_id *drv_id = drv->id_table;
49 	if (!drv_id)
50 		return NULL;
51 
52 	while (*drv_id->id) {
53 		if (compare_pnp_id(dev->id, drv_id->id))
54 			return drv_id;
55 		drv_id++;
56 	}
57 	return NULL;
58 }
59 
60 int pnp_device_attach(struct pnp_dev *pnp_dev)
61 {
62 	spin_lock(&pnp_lock);
63 	if(pnp_dev->status != PNP_READY){
64 		spin_unlock(&pnp_lock);
65 		return -EBUSY;
66 	}
67 	pnp_dev->status = PNP_ATTACHED;
68 	spin_unlock(&pnp_lock);
69 	return 0;
70 }
71 
72 void pnp_device_detach(struct pnp_dev *pnp_dev)
73 {
74 	spin_lock(&pnp_lock);
75 	if (pnp_dev->status == PNP_ATTACHED)
76 		pnp_dev->status = PNP_READY;
77 	spin_unlock(&pnp_lock);
78 	pnp_disable_dev(pnp_dev);
79 }
80 
81 static int pnp_device_probe(struct device *dev)
82 {
83 	int error;
84 	struct pnp_driver *pnp_drv;
85 	struct pnp_dev *pnp_dev;
86 	const struct pnp_device_id *dev_id = NULL;
87 	pnp_dev = to_pnp_dev(dev);
88 	pnp_drv = to_pnp_driver(dev->driver);
89 
90 	pnp_dbg("match found with the PnP device '%s' and the driver '%s'", dev->bus_id,pnp_drv->name);
91 
92 	error = pnp_device_attach(pnp_dev);
93 	if (error < 0)
94 		return error;
95 
96 	if (pnp_dev->active == 0) {
97 		if (!(pnp_drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE)) {
98 			error = pnp_activate_dev(pnp_dev);
99 			if (error < 0)
100 				return error;
101 		}
102 	} else if ((pnp_drv->flags & PNP_DRIVER_RES_DISABLE)
103 		    == PNP_DRIVER_RES_DISABLE) {
104 		error = pnp_disable_dev(pnp_dev);
105 		if (error < 0)
106 			return error;
107 	}
108 	error = 0;
109 	if (pnp_drv->probe) {
110 		dev_id = match_device(pnp_drv, pnp_dev);
111 		if (dev_id != NULL)
112 			error = pnp_drv->probe(pnp_dev, dev_id);
113 	}
114 	if (error >= 0){
115 		pnp_dev->driver = pnp_drv;
116 		error = 0;
117 	} else
118 		goto fail;
119 	return error;
120 
121 fail:
122 	pnp_device_detach(pnp_dev);
123 	return error;
124 }
125 
126 static int pnp_device_remove(struct device *dev)
127 {
128 	struct pnp_dev * pnp_dev = to_pnp_dev(dev);
129 	struct pnp_driver * drv = pnp_dev->driver;
130 
131 	if (drv) {
132 		if (drv->remove)
133 			drv->remove(pnp_dev);
134 		pnp_dev->driver = NULL;
135 	}
136 	pnp_device_detach(pnp_dev);
137 	return 0;
138 }
139 
140 static int pnp_bus_match(struct device *dev, struct device_driver *drv)
141 {
142 	struct pnp_dev * pnp_dev = to_pnp_dev(dev);
143 	struct pnp_driver * pnp_drv = to_pnp_driver(drv);
144 	if (match_device(pnp_drv, pnp_dev) == NULL)
145 		return 0;
146 	return 1;
147 }
148 
149 
150 struct bus_type pnp_bus_type = {
151 	.name	= "pnp",
152 	.match	= pnp_bus_match,
153 };
154 
155 
156 static int count_devices(struct device * dev, void * c)
157 {
158 	int * count = c;
159 	(*count)++;
160 	return 0;
161 }
162 
163 int pnp_register_driver(struct pnp_driver *drv)
164 {
165 	int count;
166 
167 	pnp_dbg("the driver '%s' has been registered", drv->name);
168 
169 	drv->driver.name = drv->name;
170 	drv->driver.bus = &pnp_bus_type;
171 	drv->driver.probe = pnp_device_probe;
172 	drv->driver.remove = pnp_device_remove;
173 
174 	count = driver_register(&drv->driver);
175 
176 	/* get the number of initial matches */
177 	if (count >= 0){
178 		count = 0;
179 		driver_for_each_device(&drv->driver, NULL, &count, count_devices);
180 	}
181 	return count;
182 }
183 
184 void pnp_unregister_driver(struct pnp_driver *drv)
185 {
186 	driver_unregister(&drv->driver);
187 	pnp_dbg("the driver '%s' has been unregistered", drv->name);
188 }
189 
190 /**
191  * pnp_add_id - adds an EISA id to the specified device
192  * @id: pointer to a pnp_id structure
193  * @dev: pointer to the desired device
194  *
195  */
196 
197 int pnp_add_id(struct pnp_id *id, struct pnp_dev *dev)
198 {
199 	struct pnp_id *ptr;
200 	if (!id)
201 		return -EINVAL;
202 	if (!dev)
203 		return -EINVAL;
204 	id->next = NULL;
205 	ptr = dev->id;
206 	while (ptr && ptr->next)
207 		ptr = ptr->next;
208 	if (ptr)
209 		ptr->next = id;
210 	else
211 		dev->id = id;
212 	return 0;
213 }
214 
215 EXPORT_SYMBOL(pnp_register_driver);
216 EXPORT_SYMBOL(pnp_unregister_driver);
217 #if 0
218 EXPORT_SYMBOL(pnp_add_id);
219 #endif
220 EXPORT_SYMBOL(pnp_device_attach);
221 EXPORT_SYMBOL(pnp_device_detach);
222