Update DEP-5 uri
[debian/omnibook.git] / init.c
1 /*
2  * init.c -- module initialization code
3  * 
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2, or (at your option) any
7  * later version.
8  * 
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * Written by Soós Péter <sp@osb.hu>, 2002-2004
15  * Modified by Mathieu Bérard <mathieu.berard@crans.org>, 2006
16  */
17
18 #include "omnibook.h"
19
20 #include <linux/proc_fs.h>
21 #include <linux/dmi.h>
22 #include <linux/version.h>
23 #include <asm/uaccess.h>
24
25 #include "hardware.h"
26 #include "laptop.h"
27
28 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15))
29 #include <linux/platform_device.h>
30 #else
31 #include <linux/device.h>
32 #endif
33
34 /*
35  * For compatibility with kernel older than 2.6.11
36  */
37
38 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11))
39 typedef u32 pm_message_t;
40 #endif
41
42 static int __init omnibook_probe(struct platform_device *dev);
43 static int __exit omnibook_remove(struct platform_device *dev);
44 static int omnibook_suspend(struct platform_device *dev, pm_message_t state);
45 static int omnibook_resume(struct platform_device *dev);
46
47 /*
48  * For compatibility with kernel older than 2.6.15
49  */
50
51 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15))
52
53 #define to_platform_device(x) container_of((x), struct platform_device, dev)
54
55 static int __init compat_omnibook_probe(struct device *dev)
56 {
57         struct platform_device *pdev = to_platform_device(dev);
58         return omnibook_probe(pdev);
59 }
60
61 static int __exit compat_omnibook_remove(struct device *dev)
62 {
63         struct platform_device *pdev = to_platform_device(dev);
64         return omnibook_remove(pdev);
65 }
66
67 static int compat_omnibook_suspend(struct device *dev, pm_message_t state, u32 level)
68 {
69         struct platform_device *pdev = to_platform_device(dev);
70         return omnibook_suspend(pdev, state);
71 }
72
73 static int compat_omnibook_resume(struct device *dev, u32 level)
74 {
75         struct platform_device *pdev = to_platform_device(dev);
76         return omnibook_resume(pdev);
77 }
78
79 #endif
80
81 static struct proc_dir_entry *omnibook_proc_root = NULL;
82
83 enum omnibook_ectype_t omnibook_ectype = NONE;
84
85 static const char *laptop_model __initdata;
86
87 static int omnibook_userset = 0;
88
89 /*
90  * The platform_driver interface was added in linux 2.6.15
91  */
92 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15))
93
94 static struct platform_device *omnibook_device;
95
96 static struct platform_driver omnibook_driver = {
97         .probe = omnibook_probe,
98         .remove = omnibook_remove,
99 #ifdef CONFIG_PM
100         .suspend = omnibook_suspend,
101         .resume = omnibook_resume,
102 #endif
103         .driver = {
104                    .name = OMNIBOOK_MODULE_NAME,
105                    .owner = THIS_MODULE,
106                    },
107 };
108
109 #else                           /* 2.6.15 */
110
111 static struct device_driver omnibook_driver = {
112         .name = OMNIBOOK_MODULE_NAME,
113         .bus = &platform_bus_type,
114         .probe = compat_omnibook_probe,
115         .remove = compat_omnibook_remove,
116 #ifdef CONFIG_PM
117         .suspend = compat_omnibook_suspend,
118         .resume = compat_omnibook_resume,
119 #endif
120 };
121
122 static struct platform_device omnibook_device = {
123         .name = OMNIBOOK_MODULE_NAME,
124 };
125
126 #endif                          /* 2.6.15 */
127
128 /* Linked list of all enabled features */
129 static struct omnibook_feature *omnibook_available_feature;
130
131 /* Delimiters of the .features section wich holds all the omnibook_feature structs */
132 extern struct omnibook_feature _start_features_driver[];
133 extern struct omnibook_feature _end_features_driver[];
134
135 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24))
136 static int __init dmi_matched(struct dmi_system_id *dmi)
137 #else
138 static int __init dmi_matched(const struct dmi_system_id *dmi)
139 #endif
140 {
141         omnibook_ectype = (enum omnibook_ectype_t)dmi->driver_data;
142         if (dmi->ident)
143                 laptop_model = (char *)dmi->ident;
144         else
145                 laptop_model = dmi_get_system_info(DMI_PRODUCT_VERSION);
146         return 1;               /* return non zero means we stop the parsing selecting this entry */
147 }
148
149 /* 
150  * Callback function for procfs file reading: the name of the file read was stored in *data 
151  */
152 static int procfile_read_dispatch(char *page, char **start, off_t off, int count, int *eof,
153                                   void *data)
154 {
155         struct omnibook_feature *feature = (struct omnibook_feature *)data;
156         int len = 0;
157
158         if (!feature || !feature->read)
159                 return -EINVAL;
160
161         if(off)
162                 goto out;
163
164         len = feature->read(page, feature->io_op);
165         if (len < 0)
166                 return len;
167
168         out:
169         *eof = 1;
170         return len;
171 }
172
173 /* 
174  * Callback function for procfs file writing: the name of the file written was stored in *data 
175  */
176 static int procfile_write_dispatch(struct file *file, const char __user * userbuf,
177                                    unsigned long count, void *data)
178 {
179         struct omnibook_feature *feature = (struct omnibook_feature *)data;
180         char *kernbuf;
181         int retval;
182
183         if (!feature || !feature->write)
184                 return -EINVAL;
185
186         kernbuf = kmalloc(count + 1, GFP_KERNEL);
187         if (!kernbuf)
188                 return -ENOMEM;
189
190         if (copy_from_user(kernbuf, userbuf, count)) {
191                 kfree(kernbuf);
192                 return -EFAULT;
193         }
194
195         /* Make sure the string is \0 terminated */
196         kernbuf[count] = '\0';
197
198         retval = feature->write(kernbuf, feature->io_op);
199         if (retval == 0)
200                 retval = count;
201
202         kfree(kernbuf);
203
204         return retval;
205 }
206
207 /*
208  * Match an ectype and return pointer to corresponding omnibook_operation.
209  * Also make corresponding backend initialisation if necessary, and skip
210  * to the next entry if it fails.
211  */
212 static struct omnibook_operation *omnibook_backend_match(struct omnibook_tbl *tbl)
213 {
214         int i;
215         struct omnibook_operation *matched = NULL;
216
217         for (i = 0; tbl[i].ectypes; i++) {
218                 if (omnibook_ectype & tbl[i].ectypes) {
219                     dprintk("Attempting backend %s init.\n",
220                             tbl[i].io_op.backend->name);
221                         if (tbl[i].io_op.backend->init && tbl[i].io_op.backend->init(&tbl[i].io_op)) {
222                                 dprintk("Backend %s init failed, skipping entry.\n",
223                                         tbl[i].io_op.backend->name);
224                                 continue;
225                         }
226                         matched = &tbl[i].io_op;
227                         dprintk("Returning table entry nr %i.\n", i);
228                         break;
229                 }
230         }
231         return matched;
232 }
233
234 /* 
235  * Initialise a feature and add it to the linked list of active features
236  */
237 static int __init omnibook_init(struct omnibook_feature *feature)
238 {
239         int retval = 0;
240         mode_t pmode;
241         struct proc_dir_entry *proc_entry;
242         struct omnibook_operation *op;
243
244         if (!feature)
245                 return -EINVAL;
246
247 /*
248  * Select appropriate backend for feature operations
249  * We copy the io_op field so the tbl can be initdata
250  */
251         if (feature->tbl) {
252                 dprintk("Begin table match of %s feature.\n", feature->name);
253                 op = omnibook_backend_match(feature->tbl);
254                 if (!op) {
255                         dprintk("Match failed: disabling %s.\n", feature->name);
256                         return -ENODEV;
257                 }
258                 dprintk("Match succeeded: continuing with %s.\n", feature->name);
259                 feature->io_op = kmalloc(sizeof(struct omnibook_operation), GFP_KERNEL);
260                 if (!feature->io_op)
261                         return -ENOMEM;
262                 memcpy(feature->io_op, op, sizeof(struct omnibook_operation));
263         } else
264                 dprintk("%s feature has no backend table, io_op not initialized.\n", feature->name);
265
266 /*
267  * Specific feature init code
268  */
269         if (feature->init && (retval = feature->init(feature->io_op))) {
270                 printk(O_ERR "Init function of %s failed with error %i.\n", feature->name, retval);
271                 goto err;
272         }
273 /*
274  * procfs file setup
275  */
276         if (feature->name && feature->read) {
277                 pmode = S_IFREG | S_IRUGO;
278                 if (feature->write) {
279                         pmode |= S_IWUSR;
280                         if (omnibook_userset)
281                                 pmode |= S_IWUGO;
282                 }
283
284                 proc_entry = create_proc_entry(feature->name, pmode, omnibook_proc_root);
285
286                 if (!proc_entry) {
287                         printk(O_ERR "Unable to create proc entry %s\n", feature->name);
288                         if (feature->exit)
289                                 feature->exit(feature->io_op);
290                         retval = -ENOENT;
291                         goto err;
292                 }
293                 proc_entry->data = feature;
294                 proc_entry->read_proc = &procfile_read_dispatch;
295                 if (feature->write)
296                         proc_entry->write_proc = &procfile_write_dispatch;
297                 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
298                         proc_entry->owner = THIS_MODULE;
299                 #endif
300         }
301         list_add_tail(&feature->list, &omnibook_available_feature->list);
302         return 0;
303       err:
304         if (feature->io_op && feature->io_op->backend->exit)
305                 feature->io_op->backend->exit(feature->io_op);
306         kfree(feature->io_op);
307         return retval;
308 }
309
310 /* 
311  * Callback function for driver registering :
312  * Initialize the linked list of enabled features and call omnibook_init to populate it
313  */
314 static int __init omnibook_probe(struct platform_device *dev)
315 {
316         int i;
317         struct omnibook_feature *feature;
318
319         /* temporary hack */
320         mutex_init(&kbc_backend.mutex);
321         mutex_init(&pio_backend.mutex);
322         mutex_init(&ec_backend.mutex);
323
324         omnibook_available_feature = kzalloc(sizeof(struct omnibook_feature), GFP_KERNEL);
325         if (!omnibook_available_feature)
326                 return -ENOMEM;
327         INIT_LIST_HEAD(&omnibook_available_feature->list);
328
329         dprintk("Feature range %p - %p\n", _start_features_driver, _end_features_driver);
330
331         for (i = 0; i < _end_features_driver - _start_features_driver; i++) {
332
333                 feature = &_start_features_driver[i];
334                 dprintk("Testing feature %s at address %p\n", feature->name, feature);
335                 if (!feature->name)
336                         continue;
337                 if (!feature->enabled)
338                         continue;
339
340                 if ((omnibook_ectype & feature->ectypes) || (!feature->ectypes))
341                         omnibook_init(feature);
342         }
343
344         printk(O_INFO "Enabled features:");
345         list_for_each_entry(feature, &omnibook_available_feature->list, list) {
346                 if (feature->name)
347                         printk(" %s", feature->name);
348         }
349         printk(".\n");
350
351         return 0;
352 }
353
354 /* 
355  * Callback function for driver removal
356  */
357 static int __exit omnibook_remove(struct platform_device *dev)
358 {
359         struct omnibook_feature *feature, *temp;
360
361         list_for_each_entry_safe(feature, temp, &omnibook_available_feature->list, list) {
362                 list_del(&feature->list);
363                 /* Feature specific cleanup */
364                 if (feature->exit)
365                         feature->exit(feature->io_op);
366                 /* Generic backend cleanup */
367                 if (feature->io_op && feature->io_op->backend->exit)
368                         feature->io_op->backend->exit(feature->io_op);
369                 if (feature->name)
370                         remove_proc_entry(feature->name, omnibook_proc_root);
371                 kfree(feature->io_op);
372         }
373         kfree(omnibook_available_feature);
374
375         return 0;
376 }
377
378 /* 
379  * Callback function for system suspend 
380  */
381 static int omnibook_suspend(struct platform_device *dev, pm_message_t state)
382 {
383         int retval;
384         struct omnibook_feature *feature;
385
386         list_for_each_entry(feature, &omnibook_available_feature->list, list) {
387                 if (feature->suspend) {
388                         retval = feature->suspend(feature->io_op);
389                         if (retval)
390                                 printk(O_ERR "Unable to suspend the %s feature (error %i).\n", feature->name, retval);
391                 }
392         }
393         return 0;
394 }
395
396 /* 
397  * Callback function for system resume
398  */
399 static int omnibook_resume(struct platform_device *dev)
400 {
401         int retval;
402         struct omnibook_feature *feature;
403
404         list_for_each_entry(feature, &omnibook_available_feature->list, list) {
405                 if (feature->resume) {
406                         retval = feature->resume(feature->io_op);
407                         if (retval)
408                                 printk(O_ERR "Unable to resume the %s feature (error %i).\n", feature->name, retval);
409                 }
410         }
411         return 0;
412 }
413
414 /* 
415  * Find a given available feature by its name
416  */
417 struct omnibook_feature *omnibook_find_feature(char *name)
418 {
419         struct omnibook_feature *feature;
420
421         list_for_each_entry(feature, &omnibook_available_feature->list, list) {
422                 if (!strcmp(feature->name, name))
423                         return feature;
424         }
425         return NULL;
426 }
427
428 /*
429  * Maintain compatibility with the old ectype numbers:
430  * ex: The user set/get ectype=12 for TSM70=2^(12-1)
431  */
432 static int __init set_ectype_param(const char *val, struct kernel_param *kp)
433 {
434         char *endp;
435         int value;
436
437         if (!val)
438                 return -EINVAL;
439
440         value = simple_strtol(val, &endp, 10);
441         if (endp == val)        /* No match */
442                 return -EINVAL;
443         omnibook_ectype = 1 << (value - 1);
444         return 0;
445 }
446
447 static int get_ectype_param(char *buffer, struct kernel_param *kp)
448 {
449         return sprintf(buffer, "%i", ffs(omnibook_ectype));
450 }
451
452 static int __init omnibook_module_init(void)
453 {
454         int retval;
455
456         printk(O_INFO "Driver version %s.\n", OMNIBOOK_MODULE_VERSION);
457
458         if (omnibook_ectype != NONE)
459                 printk(O_WARN "Forced load with EC type %i.\n", ffs(omnibook_ectype));
460         else if (dmi_check_system(omnibook_ids))
461                 printk(O_INFO "%s detected.\n", laptop_model);
462         else
463                 printk(O_INFO "Unknown model.\n");
464
465         omnibook_proc_root = proc_mkdir(OMNIBOOK_MODULE_NAME, NULL);
466         if (!omnibook_proc_root) {
467                 printk(O_ERR "Unable to create /proc/%s.\n", OMNIBOOK_MODULE_NAME);
468                 return -ENOENT;
469         }
470
471 /*
472  * The platform_driver interface was added in linux 2.6.15
473  */
474
475 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15))
476
477         retval = platform_driver_register(&omnibook_driver);
478         if (retval < 0)
479                 return retval;
480
481         omnibook_device = platform_device_alloc(OMNIBOOK_MODULE_NAME, -1);
482         if (!omnibook_device) {
483                 platform_driver_unregister(&omnibook_driver);
484                 return -ENOMEM;
485         }
486
487         retval = platform_device_add(omnibook_device);
488         if (retval) {
489                 platform_device_put(omnibook_device);
490                 platform_driver_unregister(&omnibook_driver);
491                 return retval;
492         }
493 #else                           /* 2.6.15 */
494
495         retval = driver_register(&omnibook_driver);
496         if (retval < 0)
497                 return retval;
498
499         retval = platform_device_register(&omnibook_device);
500
501         if (retval) {
502                 driver_unregister(&omnibook_driver);
503                 return retval;
504         }
505 #endif
506         return 0;
507 }
508
509 static void __exit omnibook_module_cleanup(void)
510 {
511
512 /*
513  * The platform_driver interface was added in linux 2.6.15
514  */
515
516 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15))
517         platform_device_unregister(omnibook_device);
518         platform_driver_unregister(&omnibook_driver);
519 #else
520         platform_device_unregister(&omnibook_device);
521         driver_unregister(&omnibook_driver);
522 #endif
523
524         if (omnibook_proc_root)
525                 remove_proc_entry("omnibook", NULL);
526         printk(O_INFO "Module is unloaded.\n");
527 }
528
529 module_init(omnibook_module_init);
530 module_exit(omnibook_module_cleanup);
531
532 MODULE_AUTHOR("Soós Péter, Mathieu Bérard");
533 MODULE_VERSION(OMNIBOOK_MODULE_VERSION);
534 MODULE_DESCRIPTION
535     ("Kernel interface for HP OmniBook, HP Pavilion, Toshiba Satellite and Compal ACL00 laptops");
536 MODULE_LICENSE("GPL");
537 module_param_call(ectype, set_ectype_param, get_ectype_param, NULL, S_IRUGO);
538 module_param_named(userset, omnibook_userset, int, S_IRUGO);
539 MODULE_PARM_DESC(ectype, "Type of embedded controller firmware");
540 MODULE_PARM_DESC(userset, "Use 0 to disable, 1 to enable users to set parameters");
541
542 /* End of file */