1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * driver.c - device id matching, driver model, etc. |
4 | * |
5 | * Copyright 2002 Adam Belay <ambx1@neo.rr.com> |
6 | */ |
7 | |
8 | #include <linux/string.h> |
9 | #include <linux/list.h> |
10 | #include <linux/module.h> |
11 | #include <linux/ctype.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/pnp.h> |
14 | #include "base.h" |
15 | |
16 | static int compare_func(const char *ida, const char *idb) |
17 | { |
18 | int i; |
19 | |
20 | /* we only need to compare the last 4 chars */ |
21 | for (i = 3; i < 7; i++) { |
22 | if (ida[i] != 'X' && |
23 | idb[i] != 'X' && toupper(ida[i]) != toupper(idb[i])) |
24 | return 0; |
25 | } |
26 | return 1; |
27 | } |
28 | |
29 | int compare_pnp_id(struct pnp_id *pos, const char *id) |
30 | { |
31 | if (!pos || !id || (strlen(id) != 7)) |
32 | return 0; |
33 | if (memcmp(p: id, q: "ANYDEVS" , size: 7) == 0) |
34 | return 1; |
35 | while (pos) { |
36 | if (memcmp(p: pos->id, q: id, size: 3) == 0) |
37 | if (compare_func(ida: pos->id, idb: id) == 1) |
38 | return 1; |
39 | pos = pos->next; |
40 | } |
41 | return 0; |
42 | } |
43 | |
44 | static const struct pnp_device_id *match_device(struct pnp_driver *drv, |
45 | struct pnp_dev *dev) |
46 | { |
47 | const struct pnp_device_id *drv_id = drv->id_table; |
48 | |
49 | if (!drv_id) |
50 | return NULL; |
51 | |
52 | while (*drv_id->id) { |
53 | if (compare_pnp_id(pos: dev->id, 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 | mutex_lock(&pnp_lock); |
63 | if (pnp_dev->status != PNP_READY) { |
64 | mutex_unlock(lock: &pnp_lock); |
65 | return -EBUSY; |
66 | } |
67 | pnp_dev->status = PNP_ATTACHED; |
68 | mutex_unlock(lock: &pnp_lock); |
69 | return 0; |
70 | } |
71 | EXPORT_SYMBOL(pnp_device_attach); |
72 | |
73 | void pnp_device_detach(struct pnp_dev *pnp_dev) |
74 | { |
75 | mutex_lock(&pnp_lock); |
76 | if (pnp_dev->status == PNP_ATTACHED) |
77 | pnp_dev->status = PNP_READY; |
78 | mutex_unlock(lock: &pnp_lock); |
79 | } |
80 | EXPORT_SYMBOL(pnp_device_detach); |
81 | |
82 | static int pnp_device_probe(struct device *dev) |
83 | { |
84 | int error; |
85 | struct pnp_driver *pnp_drv; |
86 | struct pnp_dev *pnp_dev; |
87 | const struct pnp_device_id *dev_id = NULL; |
88 | pnp_dev = to_pnp_dev(dev); |
89 | pnp_drv = to_pnp_driver(dev->driver); |
90 | |
91 | error = pnp_device_attach(pnp_dev); |
92 | if (error < 0) |
93 | return error; |
94 | |
95 | if (pnp_dev->active == 0) { |
96 | if (!(pnp_drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE)) { |
97 | error = pnp_activate_dev(dev: pnp_dev); |
98 | if (error < 0) |
99 | return error; |
100 | } |
101 | } else if ((pnp_drv->flags & PNP_DRIVER_RES_DISABLE) |
102 | == PNP_DRIVER_RES_DISABLE) { |
103 | error = pnp_disable_dev(dev: pnp_dev); |
104 | if (error < 0) |
105 | return error; |
106 | } |
107 | error = 0; |
108 | if (pnp_drv->probe) { |
109 | dev_id = match_device(drv: pnp_drv, dev: pnp_dev); |
110 | if (dev_id != NULL) |
111 | error = pnp_drv->probe(pnp_dev, dev_id); |
112 | } |
113 | if (error >= 0) { |
114 | pnp_dev->driver = pnp_drv; |
115 | error = 0; |
116 | } else |
117 | goto fail; |
118 | |
119 | return error; |
120 | |
121 | fail: |
122 | pnp_device_detach(pnp_dev); |
123 | return error; |
124 | } |
125 | |
126 | static void 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 | |
137 | if (pnp_dev->active && |
138 | (!drv || !(drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE))) |
139 | pnp_disable_dev(dev: pnp_dev); |
140 | |
141 | pnp_device_detach(pnp_dev); |
142 | } |
143 | |
144 | static void pnp_device_shutdown(struct device *dev) |
145 | { |
146 | struct pnp_dev *pnp_dev = to_pnp_dev(dev); |
147 | struct pnp_driver *drv = pnp_dev->driver; |
148 | |
149 | if (drv && drv->shutdown) |
150 | drv->shutdown(pnp_dev); |
151 | } |
152 | |
153 | static int pnp_bus_match(struct device *dev, struct device_driver *drv) |
154 | { |
155 | struct pnp_dev *pnp_dev = to_pnp_dev(dev); |
156 | struct pnp_driver *pnp_drv = to_pnp_driver(drv); |
157 | |
158 | if (match_device(drv: pnp_drv, dev: pnp_dev) == NULL) |
159 | return 0; |
160 | return 1; |
161 | } |
162 | |
163 | static int __pnp_bus_suspend(struct device *dev, pm_message_t state) |
164 | { |
165 | struct pnp_dev *pnp_dev = to_pnp_dev(dev); |
166 | struct pnp_driver *pnp_drv = pnp_dev->driver; |
167 | int error; |
168 | |
169 | if (!pnp_drv) |
170 | return 0; |
171 | |
172 | if (pnp_drv->driver.pm && pnp_drv->driver.pm->suspend) { |
173 | error = pnp_drv->driver.pm->suspend(dev); |
174 | suspend_report_result(dev, pnp_drv->driver.pm->suspend, error); |
175 | if (error) |
176 | return error; |
177 | } |
178 | |
179 | if (pnp_drv->suspend) { |
180 | error = pnp_drv->suspend(pnp_dev, state); |
181 | if (error) |
182 | return error; |
183 | } |
184 | |
185 | /* can_write is necessary to be able to re-start the device on resume */ |
186 | if (pnp_can_disable(pnp_dev) && pnp_can_write(pnp_dev)) { |
187 | error = pnp_stop_dev(dev: pnp_dev); |
188 | if (error) |
189 | return error; |
190 | } |
191 | |
192 | if (pnp_can_suspend(pnp_dev)) |
193 | pnp_dev->protocol->suspend(pnp_dev, state); |
194 | return 0; |
195 | } |
196 | |
197 | static int pnp_bus_suspend(struct device *dev) |
198 | { |
199 | return __pnp_bus_suspend(dev, PMSG_SUSPEND); |
200 | } |
201 | |
202 | static int pnp_bus_freeze(struct device *dev) |
203 | { |
204 | return __pnp_bus_suspend(dev, PMSG_FREEZE); |
205 | } |
206 | |
207 | static int pnp_bus_poweroff(struct device *dev) |
208 | { |
209 | return __pnp_bus_suspend(dev, PMSG_HIBERNATE); |
210 | } |
211 | |
212 | static int pnp_bus_resume(struct device *dev) |
213 | { |
214 | struct pnp_dev *pnp_dev = to_pnp_dev(dev); |
215 | struct pnp_driver *pnp_drv = pnp_dev->driver; |
216 | int error; |
217 | |
218 | if (!pnp_drv) |
219 | return 0; |
220 | |
221 | if (pnp_dev->protocol->resume) { |
222 | error = pnp_dev->protocol->resume(pnp_dev); |
223 | if (error) |
224 | return error; |
225 | } |
226 | |
227 | if (pnp_can_write(pnp_dev)) { |
228 | error = pnp_start_dev(dev: pnp_dev); |
229 | if (error) |
230 | return error; |
231 | } |
232 | |
233 | if (pnp_drv->driver.pm && pnp_drv->driver.pm->resume) { |
234 | error = pnp_drv->driver.pm->resume(dev); |
235 | if (error) |
236 | return error; |
237 | } |
238 | |
239 | if (pnp_drv->resume) { |
240 | error = pnp_drv->resume(pnp_dev); |
241 | if (error) |
242 | return error; |
243 | } |
244 | |
245 | return 0; |
246 | } |
247 | |
248 | static const struct dev_pm_ops pnp_bus_dev_pm_ops = { |
249 | /* Suspend callbacks */ |
250 | .suspend = pnp_bus_suspend, |
251 | .resume = pnp_bus_resume, |
252 | /* Hibernate callbacks */ |
253 | .freeze = pnp_bus_freeze, |
254 | .thaw = pnp_bus_resume, |
255 | .poweroff = pnp_bus_poweroff, |
256 | .restore = pnp_bus_resume, |
257 | }; |
258 | |
259 | struct bus_type pnp_bus_type = { |
260 | .name = "pnp" , |
261 | .match = pnp_bus_match, |
262 | .probe = pnp_device_probe, |
263 | .remove = pnp_device_remove, |
264 | .shutdown = pnp_device_shutdown, |
265 | .pm = &pnp_bus_dev_pm_ops, |
266 | .dev_groups = pnp_dev_groups, |
267 | }; |
268 | |
269 | int pnp_register_driver(struct pnp_driver *drv) |
270 | { |
271 | drv->driver.name = drv->name; |
272 | drv->driver.bus = &pnp_bus_type; |
273 | |
274 | return driver_register(drv: &drv->driver); |
275 | } |
276 | EXPORT_SYMBOL(pnp_register_driver); |
277 | |
278 | void pnp_unregister_driver(struct pnp_driver *drv) |
279 | { |
280 | driver_unregister(drv: &drv->driver); |
281 | } |
282 | EXPORT_SYMBOL(pnp_unregister_driver); |
283 | |
284 | /** |
285 | * pnp_add_id - adds an EISA id to the specified device |
286 | * @dev: pointer to the desired device |
287 | * @id: pointer to an EISA id string |
288 | */ |
289 | struct pnp_id *pnp_add_id(struct pnp_dev *dev, const char *id) |
290 | { |
291 | struct pnp_id *dev_id, *ptr; |
292 | |
293 | dev_id = kzalloc(size: sizeof(struct pnp_id), GFP_KERNEL); |
294 | if (!dev_id) |
295 | return NULL; |
296 | |
297 | dev_id->id[0] = id[0]; |
298 | dev_id->id[1] = id[1]; |
299 | dev_id->id[2] = id[2]; |
300 | dev_id->id[3] = tolower(id[3]); |
301 | dev_id->id[4] = tolower(id[4]); |
302 | dev_id->id[5] = tolower(id[5]); |
303 | dev_id->id[6] = tolower(id[6]); |
304 | dev_id->id[7] = '\0'; |
305 | |
306 | dev_id->next = NULL; |
307 | ptr = dev->id; |
308 | while (ptr && ptr->next) |
309 | ptr = ptr->next; |
310 | if (ptr) |
311 | ptr->next = dev_id; |
312 | else |
313 | dev->id = dev_id; |
314 | |
315 | return dev_id; |
316 | } |
317 | |