xref: /openbmc/linux/drivers/pnp/driver.c (revision 87c2ce3b)
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 static int pnp_bus_suspend(struct device *dev, pm_message_t state)
150 {
151 	struct pnp_dev * pnp_dev = to_pnp_dev(dev);
152 	struct pnp_driver * pnp_drv = pnp_dev->driver;
153 	int error;
154 
155 	if (!pnp_drv)
156 		return 0;
157 
158 	if (pnp_drv->suspend) {
159 		error = pnp_drv->suspend(pnp_dev, state);
160 		if (error)
161 			return error;
162 	}
163 
164 	if (!(pnp_drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE) &&
165 	    pnp_can_disable(pnp_dev)) {
166 	    	error = pnp_stop_dev(pnp_dev);
167 	    	if (error)
168 	    		return error;
169 	}
170 
171 	return 0;
172 }
173 
174 static int pnp_bus_resume(struct device *dev)
175 {
176 	struct pnp_dev * pnp_dev = to_pnp_dev(dev);
177 	struct pnp_driver * pnp_drv = pnp_dev->driver;
178 	int error;
179 
180 	if (!pnp_drv)
181 		return 0;
182 
183 	if (!(pnp_drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE)) {
184 		error = pnp_start_dev(pnp_dev);
185 		if (error)
186 			return error;
187 	}
188 
189 	if (pnp_drv->resume)
190 		return pnp_drv->resume(pnp_dev);
191 
192 	return 0;
193 }
194 
195 struct bus_type pnp_bus_type = {
196 	.name	= "pnp",
197 	.match	= pnp_bus_match,
198 	.suspend = pnp_bus_suspend,
199 	.resume = pnp_bus_resume,
200 };
201 
202 
203 static int count_devices(struct device * dev, void * c)
204 {
205 	int * count = c;
206 	(*count)++;
207 	return 0;
208 }
209 
210 int pnp_register_driver(struct pnp_driver *drv)
211 {
212 	int count;
213 
214 	pnp_dbg("the driver '%s' has been registered", drv->name);
215 
216 	drv->driver.name = drv->name;
217 	drv->driver.bus = &pnp_bus_type;
218 	drv->driver.probe = pnp_device_probe;
219 	drv->driver.remove = pnp_device_remove;
220 
221 	count = driver_register(&drv->driver);
222 
223 	/* get the number of initial matches */
224 	if (count >= 0){
225 		count = 0;
226 		driver_for_each_device(&drv->driver, NULL, &count, count_devices);
227 	}
228 	return count;
229 }
230 
231 void pnp_unregister_driver(struct pnp_driver *drv)
232 {
233 	driver_unregister(&drv->driver);
234 	pnp_dbg("the driver '%s' has been unregistered", drv->name);
235 }
236 
237 /**
238  * pnp_add_id - adds an EISA id to the specified device
239  * @id: pointer to a pnp_id structure
240  * @dev: pointer to the desired device
241  *
242  */
243 
244 int pnp_add_id(struct pnp_id *id, struct pnp_dev *dev)
245 {
246 	struct pnp_id *ptr;
247 	if (!id)
248 		return -EINVAL;
249 	if (!dev)
250 		return -EINVAL;
251 	id->next = NULL;
252 	ptr = dev->id;
253 	while (ptr && ptr->next)
254 		ptr = ptr->next;
255 	if (ptr)
256 		ptr->next = id;
257 	else
258 		dev->id = id;
259 	return 0;
260 }
261 
262 EXPORT_SYMBOL(pnp_register_driver);
263 EXPORT_SYMBOL(pnp_unregister_driver);
264 #if 0
265 EXPORT_SYMBOL(pnp_add_id);
266 #endif
267 EXPORT_SYMBOL(pnp_device_attach);
268 EXPORT_SYMBOL(pnp_device_detach);
269