aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
authorKristen Carlson Accardi <kristen.c.accardi@intel.com>2006-10-20 17:30:25 -0400
committerLen Brown <len.brown@intel.com>2007-02-03 01:11:30 -0500
commit01b57e73728880b787c85e27ad06c249412813b1 (patch)
tree7199a7f7581c2c0127ba9907db6e1303962d9cdc /drivers/acpi
parentfe9a2f77e5ad508b18671571c0b3f6f79ea709a8 (diff)
ACPI: bay: new driver adding removable drive bay support
Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/Kconfig7
-rw-r--r--drivers/acpi/Makefile3
-rw-r--r--drivers/acpi/bay.c592
3 files changed, 601 insertions, 1 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index f4f000abc4e9..2caef098379e 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"
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..1cfc0b74b58c
--- /dev/null
+++ b/drivers/acpi/bay.c
@@ -0,0 +1,592 @@
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/proc_fs.h>
32#include <linux/seq_file.h>
33#include <asm/uaccess.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); }
49
50static void bay_notify(acpi_handle handle, u32 event, void *data);
51static int acpi_bay_add(struct acpi_device *device);
52static int acpi_bay_remove(struct acpi_device *device, int type);
53static int acpi_bay_match(struct acpi_device *device,
54 struct acpi_driver *driver);
55
56static struct acpi_driver acpi_bay_driver = {
57 .name = ACPI_BAY_DRIVER_NAME,
58 .class = ACPI_BAY_CLASS,
59 .ops = {
60 .add = acpi_bay_add,
61 .remove = acpi_bay_remove,
62 .match = acpi_bay_match,
63 },
64};
65
66struct bay {
67 acpi_handle handle;
68 char *name;
69 struct list_head list;
70 struct proc_dir_entry *proc;
71};
72
73LIST_HEAD(drive_bays);
74
75static struct proc_dir_entry *acpi_bay_dir;
76
77/*****************************************************************************
78 * Drive Bay functions *
79 *****************************************************************************/
80/**
81 * is_ejectable - see if a device is ejectable
82 * @handle: acpi handle of the device
83 *
84 * If an acpi object has a _EJ0 method, then it is ejectable
85 */
86static int is_ejectable(acpi_handle handle)
87{
88 acpi_status status;
89 acpi_handle tmp;
90
91 status = acpi_get_handle(handle, "_EJ0", &tmp);
92 if (ACPI_FAILURE(status))
93 return 0;
94 return 1;
95}
96
97/**
98 * bay_present - see if the bay device is present
99 * @bay: the drive bay
100 *
101 * execute the _STA method.
102 */
103static int bay_present(struct bay *bay)
104{
105 unsigned long sta;
106 acpi_status status;
107
108 if (bay) {
109 status = acpi_evaluate_integer(bay->handle, "_STA", NULL, &sta);
110 if (ACPI_SUCCESS(status) && sta)
111 return 1;
112 }
113 return 0;
114}
115
116/**
117 * eject_device - respond to an eject request
118 * @handle - the device to eject
119 *
120 * Call this devices _EJ0 method.
121 */
122static void eject_device(acpi_handle handle)
123{
124 struct acpi_object_list arg_list;
125 union acpi_object arg;
126
127 bay_dprintk(handle, "Ejecting device");
128
129 arg_list.count = 1;
130 arg_list.pointer = &arg;
131 arg.type = ACPI_TYPE_INTEGER;
132 arg.integer.value = 1;
133
134 if (ACPI_FAILURE(acpi_evaluate_object(handle, "_EJ0",
135 &arg_list, NULL)))
136 pr_debug("Failed to evaluate _EJ0!\n");
137}
138
139
140/**
141 * is_ata - see if a device is an ata device
142 * @handle: acpi handle of the device
143 *
144 * If an acpi object has one of 4 ATA ACPI methods defined,
145 * then it is an ATA device
146 */
147static int is_ata(acpi_handle handle)
148{
149 acpi_handle tmp;
150
151 if ((ACPI_SUCCESS(acpi_get_handle(handle, "_GTF", &tmp))) ||
152 (ACPI_SUCCESS(acpi_get_handle(handle, "_GTM", &tmp))) ||
153 (ACPI_SUCCESS(acpi_get_handle(handle, "_STM", &tmp))) ||
154 (ACPI_SUCCESS(acpi_get_handle(handle, "_SDD", &tmp))))
155 return 1;
156
157 return 0;
158}
159
160/**
161 * parent_is_ata(acpi_handle handle)
162 *
163 */
164static int parent_is_ata(acpi_handle handle)
165{
166 acpi_handle phandle;
167
168 if (acpi_get_parent(handle, &phandle))
169 return 0;
170
171 return is_ata(phandle);
172}
173
174/**
175 * is_ejectable_bay - see if a device is an ejectable drive bay
176 * @handle: acpi handle of the device
177 *
178 * If an acpi object is ejectable and has one of the ACPI ATA
179 * methods defined, then we can safely call it an ejectable
180 * drive bay
181 */
182static int is_ejectable_bay(acpi_handle handle)
183{
184 if ((is_ata(handle) || parent_is_ata(handle)) && is_ejectable(handle))
185 return 1;
186 return 0;
187}
188
189/**
190 * eject_removable_drive - try to eject this drive
191 * @dev : the device structure of the drive
192 *
193 * If a device is a removable drive that requires an _EJ0 method
194 * to be executed in order to safely remove from the system, do
195 * it. ATM - always returns success
196 */
197int eject_removable_drive(struct device *dev)
198{
199 acpi_handle handle = DEVICE_ACPI_HANDLE(dev);
200
201 if (handle) {
202 bay_dprintk(handle, "Got device handle");
203 if (is_ejectable_bay(handle))
204 eject_device(handle);
205 } else {
206 printk("No acpi handle for device\n");
207 }
208
209 /* should I return an error code? */
210 return 0;
211}
212EXPORT_SYMBOL_GPL(eject_removable_drive);
213
214static int acpi_bay_add(struct acpi_device *device)
215{
216 bay_dprintk(device->handle, "adding bay device");
217 strcpy(acpi_device_name(device), "Dockable Bay");
218 strcpy(acpi_device_class(device), "bay");
219 return 0;
220}
221
222static int acpi_bay_status_seq_show(struct seq_file *seq, void *offset)
223{
224 struct bay *bay = (struct bay *)seq->private;
225
226 if (!bay)
227 return 0;
228
229 if (bay_present(bay))
230 seq_printf(seq, "present\n");
231 else
232 seq_printf(seq, "removed\n");
233
234 return 0;
235}
236
237static ssize_t
238acpi_bay_write_eject(struct file *file,
239 const char __user * buffer,
240 size_t count, loff_t * data)
241{
242 struct seq_file *m = (struct seq_file *)file->private_data;
243 struct bay *bay = (struct bay *)m->private;
244 char str[12] = { 0 };
245 u32 state = 0;
246
247 /* FIXME - our only valid value here is 1 */
248 if (!bay || count + 1 > sizeof str)
249 return -EINVAL;
250
251 if (copy_from_user(str, buffer, count))
252 return -EFAULT;
253
254 str[count] = 0;
255 state = simple_strtoul(str, NULL, 0);
256 if (state)
257 eject_device(bay->handle);
258
259 return count;
260}
261
262static int
263acpi_bay_status_open_fs(struct inode *inode, struct file *file)
264{
265 return single_open(file, acpi_bay_status_seq_show,
266 PDE(inode)->data);
267}
268
269static int
270acpi_bay_eject_open_fs(struct inode *inode, struct file *file)
271{
272 return single_open(file, acpi_bay_status_seq_show,
273 PDE(inode)->data);
274}
275
276static struct file_operations acpi_bay_status_fops = {
277 .open = acpi_bay_status_open_fs,
278 .read = seq_read,
279 .llseek = seq_lseek,
280 .release = single_release,
281};
282
283static struct file_operations acpi_bay_eject_fops = {
284 .open = acpi_bay_eject_open_fs,
285 .read = seq_read,
286 .write = acpi_bay_write_eject,
287 .llseek = seq_lseek,
288 .release = single_release,
289};
290#if 0
291static struct file_operations acpi_bay_insert_fops = {
292 .open = acpi_bay_insert_open_fs,
293 .read = seq_read,
294 .llseek = seq_lseek,
295 .release = single_release,
296};
297#endif
298static int acpi_bay_add_fs(struct bay *bay)
299{
300 struct proc_dir_entry *entry = NULL;
301
302 if (!bay)
303 return -EINVAL;
304
305 /*
306 * create a proc entry for this device
307 * we need to do this a little bit differently than normal
308 * acpi device drivers because our device may not be present
309 * at the moment, and therefore we have no acpi_device struct
310 */
311
312 bay->proc = proc_mkdir(bay->name, acpi_bay_dir);
313
314 /* 'status' [R] */
315 entry = create_proc_entry("status",
316 S_IRUGO, bay->proc);
317 if (!entry)
318 return -EIO;
319 else {
320 entry->proc_fops = &acpi_bay_status_fops;
321 entry->data = bay;
322 entry->owner = THIS_MODULE;
323 }
324 /* 'eject' [W] */
325 entry = create_proc_entry("eject",
326 S_IWUGO, bay->proc);
327 if (!entry)
328 return -EIO;
329 else {
330 entry->proc_fops = &acpi_bay_eject_fops;
331 entry->data = bay;
332 entry->owner = THIS_MODULE;
333 }
334#if 0
335 /* 'insert' [W] */
336 entry = create_proc_entry("insert",
337 S_IWUGO, bay->proc);
338 if (!entry)
339 return -EIO;
340 else {
341 entry->proc_fops = &acpi_bay_insert_fops;
342 entry->data = bay;
343 entry->owner = THIS_MODULE;
344 }
345#endif
346 return 0;
347}
348
349static void acpi_bay_remove_fs(struct bay *bay)
350{
351 if (!bay)
352 return;
353
354 if (bay->proc) {
355 remove_proc_entry("status", bay->proc);
356 remove_proc_entry("eject", bay->proc);
357#if 0
358 remove_proc_entry("insert", bay->proc);
359#endif
360 remove_proc_entry(bay->name, acpi_bay_dir);
361 bay->proc = NULL;
362 }
363}
364
365static int bay_is_dock_device(acpi_handle handle)
366{
367 acpi_handle parent;
368
369 acpi_get_parent(handle, &parent);
370
371 /* if the device or it's parent is dependent on the
372 * dock, then we are a dock device
373 */
374 return (is_dock_device(handle) || is_dock_device(parent));
375}
376
377static int bay_add(acpi_handle handle)
378{
379 acpi_status status;
380 struct bay *new_bay;
381 struct acpi_buffer nbuffer = {ACPI_ALLOCATE_BUFFER, NULL};
382 acpi_get_name(handle, ACPI_FULL_PATHNAME, &nbuffer);
383
384 bay_dprintk(handle, "Adding notify handler");
385
386 /*
387 * if this is the first bay device found, make the root
388 * proc entry
389 */
390 if (acpi_bay_dir == NULL)
391 acpi_bay_dir = proc_mkdir(ACPI_BAY_CLASS, acpi_root_dir);
392
393 /*
394 * Initialize bay device structure
395 */
396 new_bay = kmalloc(GFP_ATOMIC, sizeof(*new_bay));
397 INIT_LIST_HEAD(&new_bay->list);
398 new_bay->handle = handle;
399 new_bay->name = (char *)nbuffer.pointer;
400 list_add(&new_bay->list, &drive_bays);
401 acpi_bay_add_fs(new_bay);
402
403 /* register for events on this device */
404 status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
405 bay_notify, new_bay);
406 if (ACPI_FAILURE(status)) {
407 printk(KERN_ERR PREFIX "Error installing bay notify handler\n");
408 }
409
410 /* if we are on a dock station, we should register for dock
411 * notifications.
412 */
413 if (bay_is_dock_device(handle)) {
414 bay_dprintk(handle, "Is dependent on dock\n");
415 register_hotplug_dock_device(handle, bay_notify, new_bay);
416 }
417 printk(KERN_INFO PREFIX "Bay [%s] Added\n", new_bay->name);
418 return 0;
419}
420
421static int acpi_bay_remove(struct acpi_device *device, int type)
422{
423 /*** FIXME: do something here */
424 return 0;
425}
426
427static int acpi_bay_match(struct acpi_device *device,
428 struct acpi_driver *driver)
429{
430 if (!device || !driver)
431 return -EINVAL;
432
433 if (is_ejectable_bay(device->handle)) {
434 bay_dprintk(device->handle, "matching bay device");
435 return 0;
436 }
437
438 return -ENODEV;
439}
440
441/**
442 * bay_create_acpi_device - add new devices to acpi
443 * @handle - handle of the device to add
444 *
445 * This function will create a new acpi_device for the given
446 * handle if one does not exist already. This should cause
447 * acpi to scan for drivers for the given devices, and call
448 * matching driver's add routine.
449 *
450 * Returns a pointer to the acpi_device corresponding to the handle.
451 */
452static struct acpi_device * bay_create_acpi_device(acpi_handle handle)
453{
454 struct acpi_device *device = NULL;
455 struct acpi_device *parent_device;
456 acpi_handle parent;
457 int ret;
458
459 bay_dprintk(handle, "Trying to get device");
460 if (acpi_bus_get_device(handle, &device)) {
461 /*
462 * no device created for this object,
463 * so we should create one.
464 */
465 bay_dprintk(handle, "No device for handle");
466 acpi_get_parent(handle, &parent);
467 if (acpi_bus_get_device(parent, &parent_device))
468 parent_device = NULL;
469
470 ret = acpi_bus_add(&device, parent_device, handle,
471 ACPI_BUS_TYPE_DEVICE);
472 if (ret) {
473 pr_debug("error adding bus, %x\n",
474 -ret);
475 return NULL;
476 }
477 }
478 return device;
479}
480
481/**
482 * bay_notify - act upon an acpi bay notification
483 * @handle: the bay handle
484 * @event: the acpi event
485 * @data: our driver data struct
486 *
487 */
488static void bay_notify(acpi_handle handle, u32 event, void *data)
489{
490 struct acpi_device *dev;
491 struct bay *bay = data;
492
493 bay_dprintk(handle, "Bay event");
494
495 switch(event) {
496 case ACPI_NOTIFY_BUS_CHECK:
497 printk("Bus Check\n");
498 case ACPI_NOTIFY_DEVICE_CHECK:
499 printk("Device Check\n");
500 dev = bay_create_acpi_device(handle);
501 if (dev)
502 acpi_bus_generate_event(dev, event, 0);
503 else
504 printk("No device for generating event\n");
505 /* wouldn't it be a good idea to just rescan SATA
506 * right here?
507 */
508 break;
509 case ACPI_NOTIFY_EJECT_REQUEST:
510 printk("Eject request\n");
511 dev = bay_create_acpi_device(handle);
512 if (dev)
513 acpi_bus_generate_event(dev, event, 0);
514 else
515 printk("No device for generating eventn");
516
517 /* wouldn't it be a good idea to just call the
518 * eject_device here if we were a SATA device?
519 */
520 break;
521 default:
522 printk("unknown event %d\n", event);
523 }
524}
525
526static acpi_status
527find_bay(acpi_handle handle, u32 lvl, void *context, void **rv)
528{
529 int *count = (int *)context;
530
531 /*
532 * there could be more than one ejectable bay.
533 * so, just return AE_OK always so that every object
534 * will be checked.
535 */
536 if (is_ejectable_bay(handle)) {
537 bay_dprintk(handle, "found ejectable bay");
538 bay_add(handle);
539 (*count)++;
540 }
541 return AE_OK;
542}
543
544static int __init bay_init(void)
545{
546 int bays = 0;
547
548 acpi_bay_dir = NULL;
549 INIT_LIST_HEAD(&drive_bays);
550
551 /* look for dockable drive bays */
552 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
553 ACPI_UINT32_MAX, find_bay, &bays, NULL);
554
555 if (bays) {
556 if ((acpi_bus_register_driver(&acpi_bay_driver) < 0)) {
557 printk(KERN_ERR "Unable to register bay driver\n");
558 if (acpi_bay_dir)
559 remove_proc_entry(ACPI_BAY_CLASS,
560 acpi_root_dir);
561 }
562 }
563
564 if (!bays)
565 return -ENODEV;
566
567 return 0;
568}
569
570static void __exit bay_exit(void)
571{
572 struct bay *bay, *tmp;
573
574 list_for_each_entry_safe(bay, tmp, &drive_bays, list) {
575 if (is_dock_device(bay->handle))
576 unregister_hotplug_dock_device(bay->handle);
577 acpi_bay_remove_fs(bay);
578 acpi_remove_notify_handler(bay->handle, ACPI_SYSTEM_NOTIFY,
579 bay_notify);
580 kfree(bay->name);
581 kfree(bay);
582 }
583
584 if (acpi_bay_dir)
585 remove_proc_entry(ACPI_BAY_CLASS, acpi_root_dir);
586
587 acpi_bus_unregister_driver(&acpi_bay_driver);
588}
589
590postcore_initcall(bay_init);
591module_exit(bay_exit);
592