aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLen Brown <len.brown@intel.com>2007-02-03 01:12:39 -0500
committerLen Brown <len.brown@intel.com>2007-02-03 01:12:39 -0500
commit1fcb71b84b05ff3bfd5b5b2eca9a9b3d13a76e3a (patch)
treedc38e6c01154a3f19f92f9c114ba4b956c5a80a2 /drivers
parente8bdc5a9c56c140c732246a298922c3cf3777460 (diff)
parent0ed1e38d513ea683ce125e698dd41d31441e0e8c (diff)
Pull bay into test branch
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/Kconfig7
-rw-r--r--drivers/acpi/Makefile3
-rw-r--r--drivers/acpi/bay.c506
-rw-r--r--drivers/acpi/dock.c16
4 files changed, 527 insertions, 5 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 50e295b08c94..1422a11b5bbd 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -139,6 +139,13 @@ config ACPI_DOCK
139 help 139 help
140 This driver adds support for ACPI controlled docking stations 140 This driver adds support for ACPI controlled docking stations
141 141
142config ACPI_BAY
143 tristate "Removable Drive Bay (EXPERIMENTAL)"
144 depends on EXPERIMENTAL
145 help
146 This driver adds support for ACPI controlled removable drive
147 bays such as the IBM ultrabay or the Dell Module Bay.
148
142config ACPI_PROCESSOR 149config ACPI_PROCESSOR
143 tristate "Processor" 150 tristate "Processor"
144 default y 151 default y
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index bce7ca27b429..1a738056558f 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -43,7 +43,8 @@ obj-$(CONFIG_ACPI_BUTTON) += button.o
43obj-$(CONFIG_ACPI_EC) += ec.o 43obj-$(CONFIG_ACPI_EC) += ec.o
44obj-$(CONFIG_ACPI_FAN) += fan.o 44obj-$(CONFIG_ACPI_FAN) += fan.o
45obj-$(CONFIG_ACPI_DOCK) += dock.o 45obj-$(CONFIG_ACPI_DOCK) += dock.o
46obj-$(CONFIG_ACPI_VIDEO) += video.o 46obj-$(CONFIG_ACPI_BAY) += bay.o
47obj-$(CONFIG_ACPI_VIDEO) += video.o
47obj-$(CONFIG_ACPI_HOTKEY) += hotkey.o 48obj-$(CONFIG_ACPI_HOTKEY) += hotkey.o
48obj-y += pci_root.o pci_link.o pci_irq.o pci_bind.o 49obj-y += pci_root.o pci_link.o pci_irq.o pci_bind.o
49obj-$(CONFIG_ACPI_POWER) += power.o 50obj-$(CONFIG_ACPI_POWER) += power.o
diff --git a/drivers/acpi/bay.c b/drivers/acpi/bay.c
new file mode 100644
index 000000000000..aa1b131c0fce
--- /dev/null
+++ b/drivers/acpi/bay.c
@@ -0,0 +1,506 @@
1/*
2 * bay.c - ACPI removable drive bay driver
3 *
4 * Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com>
5 *
6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or (at
11 * your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 *
22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 */
24#include <linux/kernel.h>
25#include <linux/module.h>
26#include <linux/init.h>
27#include <linux/types.h>
28#include <linux/notifier.h>
29#include <acpi/acpi_bus.h>
30#include <acpi/acpi_drivers.h>
31#include <linux/seq_file.h>
32#include <asm/uaccess.h>
33#include <linux/platform_device.h>
34
35#define ACPI_BAY_DRIVER_NAME "ACPI Removable Drive Bay Driver"
36
37ACPI_MODULE_NAME("bay")
38MODULE_AUTHOR("Kristen Carlson Accardi");
39MODULE_DESCRIPTION(ACPI_BAY_DRIVER_NAME);
40MODULE_LICENSE("GPL");
41#define ACPI_BAY_CLASS "bay"
42#define ACPI_BAY_COMPONENT 0x10000000
43#define _COMPONENT ACPI_BAY_COMPONENT
44#define bay_dprintk(h,s) {\
45 char prefix[80] = {'\0'};\
46 struct acpi_buffer buffer = {sizeof(prefix), prefix};\
47 acpi_get_name(h, ACPI_FULL_PATHNAME, &buffer);\
48 printk(KERN_DEBUG PREFIX "%s: %s\n", prefix, s); }
49static void bay_notify(acpi_handle handle, u32 event, void *data);
50static int acpi_bay_add(struct acpi_device *device);
51static int acpi_bay_remove(struct acpi_device *device, int type);
52static int acpi_bay_match(struct acpi_device *device,
53 struct acpi_driver *driver);
54
55static struct acpi_driver acpi_bay_driver = {
56 .name = ACPI_BAY_DRIVER_NAME,
57 .class = ACPI_BAY_CLASS,
58 .ops = {
59 .add = acpi_bay_add,
60 .remove = acpi_bay_remove,
61 .match = acpi_bay_match,
62 },
63};
64
65struct bay {
66 acpi_handle handle;
67 char *name;
68 struct list_head list;
69 struct platform_device *pdev;
70};
71
72static LIST_HEAD(drive_bays);
73
74
75/*****************************************************************************
76 * Drive Bay functions *
77 *****************************************************************************/
78/**
79 * is_ejectable - see if a device is ejectable
80 * @handle: acpi handle of the device
81 *
82 * If an acpi object has a _EJ0 method, then it is ejectable
83 */
84static int is_ejectable(acpi_handle handle)
85{
86 acpi_status status;
87 acpi_handle tmp;
88
89 status = acpi_get_handle(handle, "_EJ0", &tmp);
90 if (ACPI_FAILURE(status))
91 return 0;
92 return 1;
93}
94
95/**
96 * bay_present - see if the bay device is present
97 * @bay: the drive bay
98 *
99 * execute the _STA method.
100 */
101static int bay_present(struct bay *bay)
102{
103 unsigned long sta;
104 acpi_status status;
105
106 if (bay) {
107 status = acpi_evaluate_integer(bay->handle, "_STA", NULL, &sta);
108 if (ACPI_SUCCESS(status) && sta)
109 return 1;
110 }
111 return 0;
112}
113
114/**
115 * eject_device - respond to an eject request
116 * @handle - the device to eject
117 *
118 * Call this devices _EJ0 method.
119 */
120static void eject_device(acpi_handle handle)
121{
122 struct acpi_object_list arg_list;
123 union acpi_object arg;
124
125 bay_dprintk(handle, "Ejecting device");
126
127 arg_list.count = 1;
128 arg_list.pointer = &arg;
129 arg.type = ACPI_TYPE_INTEGER;
130 arg.integer.value = 1;
131
132 if (ACPI_FAILURE(acpi_evaluate_object(handle, "_EJ0",
133 &arg_list, NULL)))
134 pr_debug("Failed to evaluate _EJ0!\n");
135}
136
137/*
138 * show_present - read method for "present" file in sysfs
139 */
140static ssize_t show_present(struct device *dev,
141 struct device_attribute *attr, char *buf)
142{
143 struct bay *bay = dev_get_drvdata(dev);
144 return snprintf(buf, PAGE_SIZE, "%d\n", bay_present(bay));
145
146}
147DEVICE_ATTR(present, S_IRUGO, show_present, NULL);
148
149/*
150 * write_eject - write method for "eject" file in sysfs
151 */
152static ssize_t write_eject(struct device *dev, struct device_attribute *attr,
153 const char *buf, size_t count)
154{
155 struct bay *bay = dev_get_drvdata(dev);
156
157 if (!count)
158 return -EINVAL;
159
160 eject_device(bay->handle);
161 return count;
162}
163DEVICE_ATTR(eject, S_IWUSR, NULL, write_eject);
164
165/**
166 * is_ata - see if a device is an ata device
167 * @handle: acpi handle of the device
168 *
169 * If an acpi object has one of 4 ATA ACPI methods defined,
170 * then it is an ATA device
171 */
172static int is_ata(acpi_handle handle)
173{
174 acpi_handle tmp;
175
176 if ((ACPI_SUCCESS(acpi_get_handle(handle, "_GTF", &tmp))) ||
177 (ACPI_SUCCESS(acpi_get_handle(handle, "_GTM", &tmp))) ||
178 (ACPI_SUCCESS(acpi_get_handle(handle, "_STM", &tmp))) ||
179 (ACPI_SUCCESS(acpi_get_handle(handle, "_SDD", &tmp))))
180 return 1;
181
182 return 0;
183}
184
185/**
186 * parent_is_ata(acpi_handle handle)
187 *
188 */
189static int parent_is_ata(acpi_handle handle)
190{
191 acpi_handle phandle;
192
193 if (acpi_get_parent(handle, &phandle))
194 return 0;
195
196 return is_ata(phandle);
197}
198
199/**
200 * is_ejectable_bay - see if a device is an ejectable drive bay
201 * @handle: acpi handle of the device
202 *
203 * If an acpi object is ejectable and has one of the ACPI ATA
204 * methods defined, then we can safely call it an ejectable
205 * drive bay
206 */
207static int is_ejectable_bay(acpi_handle handle)
208{
209 if ((is_ata(handle) || parent_is_ata(handle)) && is_ejectable(handle))
210 return 1;
211 return 0;
212}
213
214/**
215 * eject_removable_drive - try to eject this drive
216 * @dev : the device structure of the drive
217 *
218 * If a device is a removable drive that requires an _EJ0 method
219 * to be executed in order to safely remove from the system, do
220 * it. ATM - always returns success
221 */
222int eject_removable_drive(struct device *dev)
223{
224 acpi_handle handle = DEVICE_ACPI_HANDLE(dev);
225
226 if (handle) {
227 bay_dprintk(handle, "Got device handle");
228 if (is_ejectable_bay(handle))
229 eject_device(handle);
230 } else {
231 printk("No acpi handle for device\n");
232 }
233
234 /* should I return an error code? */
235 return 0;
236}
237EXPORT_SYMBOL_GPL(eject_removable_drive);
238
239static int acpi_bay_add(struct acpi_device *device)
240{
241 bay_dprintk(device->handle, "adding bay device");
242 strcpy(acpi_device_name(device), "Dockable Bay");
243 strcpy(acpi_device_class(device), "bay");
244 return 0;
245}
246
247static int acpi_bay_add_fs(struct bay *bay)
248{
249 int ret;
250 struct device *dev = &bay->pdev->dev;
251
252 ret = device_create_file(dev, &dev_attr_present);
253 if (ret)
254 goto add_fs_err;
255 ret = device_create_file(dev, &dev_attr_eject);
256 if (ret) {
257 device_remove_file(dev, &dev_attr_present);
258 goto add_fs_err;
259 }
260 return 0;
261
262 add_fs_err:
263 bay_dprintk(bay->handle, "Error adding sysfs files\n");
264 return ret;
265}
266
267static void acpi_bay_remove_fs(struct bay *bay)
268{
269 struct device *dev = &bay->pdev->dev;
270
271 /* cleanup sysfs */
272 device_remove_file(dev, &dev_attr_present);
273 device_remove_file(dev, &dev_attr_eject);
274}
275
276static int bay_is_dock_device(acpi_handle handle)
277{
278 acpi_handle parent;
279
280 acpi_get_parent(handle, &parent);
281
282 /* if the device or it's parent is dependent on the
283 * dock, then we are a dock device
284 */
285 return (is_dock_device(handle) || is_dock_device(parent));
286}
287
288static int bay_add(acpi_handle handle, int id)
289{
290 acpi_status status;
291 struct bay *new_bay;
292 struct platform_device *pdev;
293 struct acpi_buffer nbuffer = {ACPI_ALLOCATE_BUFFER, NULL};
294 acpi_get_name(handle, ACPI_FULL_PATHNAME, &nbuffer);
295
296 bay_dprintk(handle, "Adding notify handler");
297
298 /*
299 * Initialize bay device structure
300 */
301 new_bay = kzalloc(GFP_ATOMIC, sizeof(*new_bay));
302 INIT_LIST_HEAD(&new_bay->list);
303 new_bay->handle = handle;
304 new_bay->name = (char *)nbuffer.pointer;
305
306 /* initialize platform device stuff */
307 pdev = platform_device_register_simple(ACPI_BAY_CLASS, id, NULL, 0);
308 if (pdev == NULL) {
309 printk(KERN_ERR PREFIX "Error registering bay device\n");
310 goto bay_add_err;
311 }
312 new_bay->pdev = pdev;
313 platform_set_drvdata(pdev, new_bay);
314
315 if (acpi_bay_add_fs(new_bay)) {
316 platform_device_unregister(new_bay->pdev);
317 goto bay_add_err;
318 }
319
320 /* register for events on this device */
321 status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
322 bay_notify, new_bay);
323 if (ACPI_FAILURE(status)) {
324 printk(KERN_ERR PREFIX "Error installing bay notify handler\n");
325 }
326
327 /* if we are on a dock station, we should register for dock
328 * notifications.
329 */
330 if (bay_is_dock_device(handle)) {
331 bay_dprintk(handle, "Is dependent on dock\n");
332 register_hotplug_dock_device(handle, bay_notify, new_bay);
333 }
334 list_add(&new_bay->list, &drive_bays);
335 printk(KERN_INFO PREFIX "Bay [%s] Added\n", new_bay->name);
336 return 0;
337
338bay_add_err:
339 kfree(new_bay->name);
340 kfree(new_bay);
341 return -ENODEV;
342}
343
344static int acpi_bay_remove(struct acpi_device *device, int type)
345{
346 /*** FIXME: do something here */
347 return 0;
348}
349
350static int acpi_bay_match(struct acpi_device *device,
351 struct acpi_driver *driver)
352{
353 if (!device || !driver)
354 return -EINVAL;
355
356 if (is_ejectable_bay(device->handle)) {
357 bay_dprintk(device->handle, "matching bay device");
358 return 0;
359 }
360
361 return -ENODEV;
362}
363
364/**
365 * bay_create_acpi_device - add new devices to acpi
366 * @handle - handle of the device to add
367 *
368 * This function will create a new acpi_device for the given
369 * handle if one does not exist already. This should cause
370 * acpi to scan for drivers for the given devices, and call
371 * matching driver's add routine.
372 *
373 * Returns a pointer to the acpi_device corresponding to the handle.
374 */
375static struct acpi_device * bay_create_acpi_device(acpi_handle handle)
376{
377 struct acpi_device *device = NULL;
378 struct acpi_device *parent_device;
379 acpi_handle parent;
380 int ret;
381
382 bay_dprintk(handle, "Trying to get device");
383 if (acpi_bus_get_device(handle, &device)) {
384 /*
385 * no device created for this object,
386 * so we should create one.
387 */
388 bay_dprintk(handle, "No device for handle");
389 acpi_get_parent(handle, &parent);
390 if (acpi_bus_get_device(parent, &parent_device))
391 parent_device = NULL;
392
393 ret = acpi_bus_add(&device, parent_device, handle,
394 ACPI_BUS_TYPE_DEVICE);
395 if (ret) {
396 pr_debug("error adding bus, %x\n",
397 -ret);
398 return NULL;
399 }
400 }
401 return device;
402}
403
404/**
405 * bay_notify - act upon an acpi bay notification
406 * @handle: the bay handle
407 * @event: the acpi event
408 * @data: our driver data struct
409 *
410 */
411static void bay_notify(acpi_handle handle, u32 event, void *data)
412{
413 struct acpi_device *dev;
414
415 bay_dprintk(handle, "Bay event");
416
417 switch(event) {
418 case ACPI_NOTIFY_BUS_CHECK:
419 printk("Bus Check\n");
420 case ACPI_NOTIFY_DEVICE_CHECK:
421 printk("Device Check\n");
422 dev = bay_create_acpi_device(handle);
423 if (dev)
424 acpi_bus_generate_event(dev, event, 0);
425 else
426 printk("No device for generating event\n");
427 /* wouldn't it be a good idea to just rescan SATA
428 * right here?
429 */
430 break;
431 case ACPI_NOTIFY_EJECT_REQUEST:
432 printk("Eject request\n");
433 dev = bay_create_acpi_device(handle);
434 if (dev)
435 acpi_bus_generate_event(dev, event, 0);
436 else
437 printk("No device for generating eventn");
438
439 /* wouldn't it be a good idea to just call the
440 * eject_device here if we were a SATA device?
441 */
442 break;
443 default:
444 printk("unknown event %d\n", event);
445 }
446}
447
448static acpi_status
449find_bay(acpi_handle handle, u32 lvl, void *context, void **rv)
450{
451 int *count = (int *)context;
452
453 /*
454 * there could be more than one ejectable bay.
455 * so, just return AE_OK always so that every object
456 * will be checked.
457 */
458 if (is_ejectable_bay(handle)) {
459 bay_dprintk(handle, "found ejectable bay");
460 if (!bay_add(handle, *count))
461 (*count)++;
462 }
463 return AE_OK;
464}
465
466static int __init bay_init(void)
467{
468 int bays = 0;
469
470 INIT_LIST_HEAD(&drive_bays);
471
472 /* look for dockable drive bays */
473 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
474 ACPI_UINT32_MAX, find_bay, &bays, NULL);
475
476 if (bays)
477 if ((acpi_bus_register_driver(&acpi_bay_driver) < 0))
478 printk(KERN_ERR "Unable to register bay driver\n");
479
480 if (!bays)
481 return -ENODEV;
482
483 return 0;
484}
485
486static void __exit bay_exit(void)
487{
488 struct bay *bay, *tmp;
489
490 list_for_each_entry_safe(bay, tmp, &drive_bays, list) {
491 if (is_dock_device(bay->handle))
492 unregister_hotplug_dock_device(bay->handle);
493 acpi_bay_remove_fs(bay);
494 acpi_remove_notify_handler(bay->handle, ACPI_SYSTEM_NOTIFY,
495 bay_notify);
496 platform_device_unregister(bay->pdev);
497 kfree(bay->name);
498 kfree(bay);
499 }
500
501 acpi_bus_unregister_driver(&acpi_bay_driver);
502}
503
504postcore_initcall(bay_init);
505module_exit(bay_exit);
506
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
index 90990a4b6526..688e83a16906 100644
--- a/drivers/acpi/dock.c
+++ b/drivers/acpi/dock.c
@@ -615,20 +615,28 @@ static acpi_status
615find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv) 615find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
616{ 616{
617 acpi_status status; 617 acpi_status status;
618 acpi_handle tmp; 618 acpi_handle tmp, parent;
619 struct dock_station *ds = context; 619 struct dock_station *ds = context;
620 struct dock_dependent_device *dd; 620 struct dock_dependent_device *dd;
621 621
622 status = acpi_bus_get_ejd(handle, &tmp); 622 status = acpi_bus_get_ejd(handle, &tmp);
623 if (ACPI_FAILURE(status)) 623 if (ACPI_FAILURE(status)) {
624 return AE_OK; 624 /* try the parent device as well */
625 status = acpi_get_parent(handle, &parent);
626 if (ACPI_FAILURE(status))
627 goto fdd_out;
628 /* see if parent is dependent on dock */
629 status = acpi_bus_get_ejd(parent, &tmp);
630 if (ACPI_FAILURE(status))
631 goto fdd_out;
632 }
625 633
626 if (tmp == ds->handle) { 634 if (tmp == ds->handle) {
627 dd = alloc_dock_dependent_device(handle); 635 dd = alloc_dock_dependent_device(handle);
628 if (dd) 636 if (dd)
629 add_dock_dependent_device(ds, dd); 637 add_dock_dependent_device(ds, dd);
630 } 638 }
631 639fdd_out:
632 return AE_OK; 640 return AE_OK;
633} 641}
634 642