Blame view

drivers/pnp/driver.c 5.02 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
  /*
   * driver.c - device id matching, driver model, etc.
   *
   * Copyright 2002 Adam Belay <ambx1@neo.rr.com>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
7
8
9
10
  #include <linux/string.h>
  #include <linux/list.h>
  #include <linux/module.h>
  #include <linux/ctype.h>
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
13
14
15
16
  #include <linux/pnp.h>
  #include "base.h"
  
  static int compare_func(const char *ida, const char *idb)
  {
  	int i;
07d4e9af1   Bjorn Helgaas   PNP: fix up after...
17

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
  	/* we only need to compare the last 4 chars */
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
19
  	for (i = 3; i < 7; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
  		if (ida[i] != 'X' &&
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
21
  		    idb[i] != 'X' && toupper(ida[i]) != toupper(idb[i]))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
24
25
26
27
28
29
30
  			return 0;
  	}
  	return 1;
  }
  
  int compare_pnp_id(struct pnp_id *pos, const char *id)
  {
  	if (!pos || !id || (strlen(id) != 7))
  		return 0;
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
31
  	if (memcmp(id, "ANYDEVS", 7) == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
  		return 1;
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
33
34
35
  	while (pos) {
  		if (memcmp(pos->id, id, 3) == 0)
  			if (compare_func(pos->id, id) == 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
38
39
40
  				return 1;
  		pos = pos->next;
  	}
  	return 0;
  }
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
41
42
  static const struct pnp_device_id *match_device(struct pnp_driver *drv,
  						struct pnp_dev *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
  {
  	const struct pnp_device_id *drv_id = drv->id_table;
07d4e9af1   Bjorn Helgaas   PNP: fix up after...
45

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  	if (!drv_id)
  		return NULL;
  
  	while (*drv_id->id) {
  		if (compare_pnp_id(dev->id, drv_id->id))
  			return drv_id;
  		drv_id++;
  	}
  	return NULL;
  }
  
  int pnp_device_attach(struct pnp_dev *pnp_dev)
  {
  	spin_lock(&pnp_lock);
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
60
  	if (pnp_dev->status != PNP_READY) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
  		spin_unlock(&pnp_lock);
  		return -EBUSY;
  	}
  	pnp_dev->status = PNP_ATTACHED;
  	spin_unlock(&pnp_lock);
  	return 0;
  }
  
  void pnp_device_detach(struct pnp_dev *pnp_dev)
  {
  	spin_lock(&pnp_lock);
  	if (pnp_dev->status == PNP_ATTACHED)
  		pnp_dev->status = PNP_READY;
  	spin_unlock(&pnp_lock);
  	pnp_disable_dev(pnp_dev);
  }
  
  static int pnp_device_probe(struct device *dev)
  {
  	int error;
  	struct pnp_driver *pnp_drv;
  	struct pnp_dev *pnp_dev;
  	const struct pnp_device_id *dev_id = NULL;
  	pnp_dev = to_pnp_dev(dev);
  	pnp_drv = to_pnp_driver(dev->driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
88
89
90
91
92
93
94
95
96
  	error = pnp_device_attach(pnp_dev);
  	if (error < 0)
  		return error;
  
  	if (pnp_dev->active == 0) {
  		if (!(pnp_drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE)) {
  			error = pnp_activate_dev(pnp_dev);
  			if (error < 0)
  				return error;
  		}
  	} else if ((pnp_drv->flags & PNP_DRIVER_RES_DISABLE)
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
97
  		   == PNP_DRIVER_RES_DISABLE) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
100
101
102
103
104
105
106
107
  		error = pnp_disable_dev(pnp_dev);
  		if (error < 0)
  			return error;
  	}
  	error = 0;
  	if (pnp_drv->probe) {
  		dev_id = match_device(pnp_drv, pnp_dev);
  		if (dev_id != NULL)
  			error = pnp_drv->probe(pnp_dev, dev_id);
  	}
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
108
  	if (error >= 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
112
  		pnp_dev->driver = pnp_drv;
  		error = 0;
  	} else
  		goto fail;
a05d07816   Bjorn Helgaas   PNP: use dev_info...
113
114
115
  
  	dev_dbg(dev, "driver attached
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
  	return error;
1e0aa9ad7   Bjorn Helgaas   PNP: fix up after...
117
  fail:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
120
121
122
123
  	pnp_device_detach(pnp_dev);
  	return error;
  }
  
  static int pnp_device_remove(struct device *dev)
  {
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
124
125
  	struct pnp_dev *pnp_dev = to_pnp_dev(dev);
  	struct pnp_driver *drv = pnp_dev->driver;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
127
128
129
130
131
132
133
134
135
136
137
  
  	if (drv) {
  		if (drv->remove)
  			drv->remove(pnp_dev);
  		pnp_dev->driver = NULL;
  	}
  	pnp_device_detach(pnp_dev);
  	return 0;
  }
  
  static int pnp_bus_match(struct device *dev, struct device_driver *drv)
  {
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
138
139
  	struct pnp_dev *pnp_dev = to_pnp_dev(dev);
  	struct pnp_driver *pnp_drv = to_pnp_driver(drv);
07d4e9af1   Bjorn Helgaas   PNP: fix up after...
140

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
143
144
  	if (match_device(pnp_drv, pnp_dev) == NULL)
  		return 0;
  	return 1;
  }
4c98cfef2   Takashi Iwai   [ALSA] PATCH] Add...
145
146
  static int pnp_bus_suspend(struct device *dev, pm_message_t state)
  {
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
147
148
  	struct pnp_dev *pnp_dev = to_pnp_dev(dev);
  	struct pnp_driver *pnp_drv = pnp_dev->driver;
68094e325   Pierre Ossman   [ALSA] [PATCH] al...
149
150
151
152
153
154
155
156
157
158
159
160
161
  	int error;
  
  	if (!pnp_drv)
  		return 0;
  
  	if (pnp_drv->suspend) {
  		error = pnp_drv->suspend(pnp_dev, state);
  		if (error)
  			return error;
  	}
  
  	if (!(pnp_drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE) &&
  	    pnp_can_disable(pnp_dev)) {
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
162
163
164
  		error = pnp_stop_dev(pnp_dev);
  		if (error)
  			return error;
68094e325   Pierre Ossman   [ALSA] [PATCH] al...
165
  	}
4c98cfef2   Takashi Iwai   [ALSA] PATCH] Add...
166

fc30e68e8   Shaohua Li   ACPI, PNP: hook A...
167
168
  	if (pnp_dev->protocol && pnp_dev->protocol->suspend)
  		pnp_dev->protocol->suspend(pnp_dev, state);
4c98cfef2   Takashi Iwai   [ALSA] PATCH] Add...
169
170
  	return 0;
  }
68094e325   Pierre Ossman   [ALSA] [PATCH] al...
171
  static int pnp_bus_resume(struct device *dev)
4c98cfef2   Takashi Iwai   [ALSA] PATCH] Add...
172
  {
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
173
174
  	struct pnp_dev *pnp_dev = to_pnp_dev(dev);
  	struct pnp_driver *pnp_drv = pnp_dev->driver;
68094e325   Pierre Ossman   [ALSA] [PATCH] al...
175
176
177
178
  	int error;
  
  	if (!pnp_drv)
  		return 0;
fc30e68e8   Shaohua Li   ACPI, PNP: hook A...
179
180
  	if (pnp_dev->protocol && pnp_dev->protocol->resume)
  		pnp_dev->protocol->resume(pnp_dev);
68094e325   Pierre Ossman   [ALSA] [PATCH] al...
181
182
183
184
185
  	if (!(pnp_drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE)) {
  		error = pnp_start_dev(pnp_dev);
  		if (error)
  			return error;
  	}
4c98cfef2   Takashi Iwai   [ALSA] PATCH] Add...
186

68094e325   Pierre Ossman   [ALSA] [PATCH] al...
187
188
189
190
  	if (pnp_drv->resume)
  		return pnp_drv->resume(pnp_dev);
  
  	return 0;
4c98cfef2   Takashi Iwai   [ALSA] PATCH] Add...
191
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
  
  struct bus_type pnp_bus_type = {
07d4e9af1   Bjorn Helgaas   PNP: fix up after...
194
195
196
197
  	.name    = "pnp",
  	.match   = pnp_bus_match,
  	.probe   = pnp_device_probe,
  	.remove  = pnp_device_remove,
4c98cfef2   Takashi Iwai   [ALSA] PATCH] Add...
198
  	.suspend = pnp_bus_suspend,
07d4e9af1   Bjorn Helgaas   PNP: fix up after...
199
  	.resume  = pnp_bus_resume,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
202
  int pnp_register_driver(struct pnp_driver *drv)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
204
205
206
  	pnp_dbg("the driver '%s' has been registered", drv->name);
  
  	drv->driver.name = drv->name;
  	drv->driver.bus = &pnp_bus_type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207

982c60944   Bjorn Helgaas   [PATCH] pnp: PNP:...
208
  	return driver_register(&drv->driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
211
212
213
214
215
216
217
218
219
220
  }
  
  void pnp_unregister_driver(struct pnp_driver *drv)
  {
  	driver_unregister(&drv->driver);
  	pnp_dbg("the driver '%s' has been unregistered", drv->name);
  }
  
  /**
   * pnp_add_id - adds an EISA id to the specified device
   * @id: pointer to a pnp_id structure
   * @dev: pointer to the desired device
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
224
  int pnp_add_id(struct pnp_id *id, struct pnp_dev *dev)
  {
  	struct pnp_id *ptr;
07d4e9af1   Bjorn Helgaas   PNP: fix up after...
225

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
227
228
229
230
231
232
233
234
235
236
237
238
  	id->next = NULL;
  	ptr = dev->id;
  	while (ptr && ptr->next)
  		ptr = ptr->next;
  	if (ptr)
  		ptr->next = id;
  	else
  		dev->id = id;
  	return 0;
  }
  
  EXPORT_SYMBOL(pnp_register_driver);
  EXPORT_SYMBOL(pnp_unregister_driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
  EXPORT_SYMBOL(pnp_device_attach);
  EXPORT_SYMBOL(pnp_device_detach);