~ubuntu-branches/ubuntu/quantal/linux-lowlatency/quantal

« back to all changes in this revision

Viewing changes to ubuntu/omnibook/init.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-04kado7d1u2er2rl
Tags: 3.2.0-16.25
Add new lowlatency kernel flavour

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
                        if (tbl[i].io_op.backend->init && tbl[i].io_op.backend->init(&tbl[i].io_op)) {
 
220
                                dprintk("Backend %s init failed, skipping entry.\n",
 
221
                                        tbl[i].io_op.backend->name);
 
222
                                continue;
 
223
                        }
 
224
                        matched = &tbl[i].io_op;
 
225
                        dprintk("Returning table entry nr %i.\n", i);
 
226
                        break;
 
227
                }
 
228
        }
 
229
        return matched;
 
230
}
 
231
 
 
232
/* 
 
233
 * Initialise a feature and add it to the linked list of active features
 
234
 */
 
235
static int __init omnibook_init(struct omnibook_feature *feature)
 
236
{
 
237
        int retval = 0;
 
238
        mode_t pmode;
 
239
        struct proc_dir_entry *proc_entry;
 
240
        struct omnibook_operation *op;
 
241
 
 
242
        if (!feature)
 
243
                return -EINVAL;
 
244
 
 
245
/*
 
246
 * Select appropriate backend for feature operations
 
247
 * We copy the io_op field so the tbl can be initdata
 
248
 */
 
249
        if (feature->tbl) {
 
250
                dprintk("Begin table match of %s feature.\n", feature->name);
 
251
                op = omnibook_backend_match(feature->tbl);
 
252
                if (!op) {
 
253
                        dprintk("Match failed: disabling %s.\n", feature->name);
 
254
                        return -ENODEV;
 
255
                }
 
256
                feature->io_op = kmalloc(sizeof(struct omnibook_operation), GFP_KERNEL);
 
257
                if (!feature->io_op)
 
258
                        return -ENOMEM;
 
259
                memcpy(feature->io_op, op, sizeof(struct omnibook_operation));
 
260
        } else
 
261
                dprintk("%s feature has no backend table, io_op not initialized.\n", feature->name);
 
262
 
 
263
/*
 
264
 * Specific feature init code
 
265
 */
 
266
        if (feature->init && (retval = feature->init(feature->io_op))) {
 
267
                printk(O_ERR "Init function of %s failed with error %i.\n", feature->name, retval);
 
268
                goto err;
 
269
        }
 
270
/*
 
271
 * procfs file setup
 
272
 */
 
273
        if (feature->name && feature->read) {
 
274
                pmode = S_IFREG | S_IRUGO;
 
275
                if (feature->write) {
 
276
                        pmode |= S_IWUSR;
 
277
                        if (omnibook_userset)
 
278
                                pmode |= S_IWUGO;
 
279
                }
 
280
 
 
281
                proc_entry = create_proc_entry(feature->name, pmode, omnibook_proc_root);
 
282
 
 
283
                if (!proc_entry) {
 
284
                        printk(O_ERR "Unable to create proc entry %s\n", feature->name);
 
285
                        if (feature->exit)
 
286
                                feature->exit(feature->io_op);
 
287
                        retval = -ENOENT;
 
288
                        goto err;
 
289
                }
 
290
                proc_entry->data = feature;
 
291
                proc_entry->read_proc = &procfile_read_dispatch;
 
292
                if (feature->write)
 
293
                        proc_entry->write_proc = &procfile_write_dispatch;
 
294
                #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
 
295
                        proc_entry->owner = THIS_MODULE;
 
296
                #endif
 
297
        }
 
298
        list_add_tail(&feature->list, &omnibook_available_feature->list);
 
299
        return 0;
 
300
      err:
 
301
        if (feature->io_op && feature->io_op->backend->exit)
 
302
                feature->io_op->backend->exit(feature->io_op);
 
303
        kfree(feature->io_op);
 
304
        return retval;
 
305
}
 
306
 
 
307
/* 
 
308
 * Callback function for driver registering :
 
309
 * Initialize the linked list of enabled features and call omnibook_init to populate it
 
310
 */
 
311
static int __init omnibook_probe(struct platform_device *dev)
 
312
{
 
313
        int i;
 
314
        struct omnibook_feature *feature;
 
315
 
 
316
        /* temporary hack */
 
317
        mutex_init(&kbc_backend.mutex);
 
318
        mutex_init(&pio_backend.mutex);
 
319
        mutex_init(&ec_backend.mutex);
 
320
 
 
321
        omnibook_available_feature = kzalloc(sizeof(struct omnibook_feature), GFP_KERNEL);
 
322
        if (!omnibook_available_feature)
 
323
                return -ENOMEM;
 
324
        INIT_LIST_HEAD(&omnibook_available_feature->list);
 
325
 
 
326
        for (i = 0; i < _end_features_driver - _start_features_driver; i++) {
 
327
 
 
328
                feature = &_start_features_driver[i];
 
329
 
 
330
                if (!feature->enabled)
 
331
                        continue;
 
332
 
 
333
                if ((omnibook_ectype & feature->ectypes) || (!feature->ectypes))
 
334
                        omnibook_init(feature);
 
335
        }
 
336
 
 
337
        printk(O_INFO "Enabled features:");
 
338
        list_for_each_entry(feature, &omnibook_available_feature->list, list) {
 
339
                if (feature->name)
 
340
                        printk(" %s", feature->name);
 
341
        }
 
342
        printk(".\n");
 
343
 
 
344
        return 0;
 
345
}
 
346
 
 
347
/* 
 
348
 * Callback function for driver removal
 
349
 */
 
350
static int __exit omnibook_remove(struct platform_device *dev)
 
351
{
 
352
        struct omnibook_feature *feature, *temp;
 
353
 
 
354
        list_for_each_entry_safe(feature, temp, &omnibook_available_feature->list, list) {
 
355
                list_del(&feature->list);
 
356
                /* Feature specific cleanup */
 
357
                if (feature->exit)
 
358
                        feature->exit(feature->io_op);
 
359
                /* Generic backend cleanup */
 
360
                if (feature->io_op && feature->io_op->backend->exit)
 
361
                        feature->io_op->backend->exit(feature->io_op);
 
362
                if (feature->name)
 
363
                        remove_proc_entry(feature->name, omnibook_proc_root);
 
364
                kfree(feature->io_op);
 
365
        }
 
366
        kfree(omnibook_available_feature);
 
367
 
 
368
        return 0;
 
369
}
 
370
 
 
371
/* 
 
372
 * Callback function for system suspend 
 
373
 */
 
374
static int omnibook_suspend(struct platform_device *dev, pm_message_t state)
 
375
{
 
376
        int retval;
 
377
        struct omnibook_feature *feature;
 
378
 
 
379
        list_for_each_entry(feature, &omnibook_available_feature->list, list) {
 
380
                if (feature->suspend) {
 
381
                        retval = feature->suspend(feature->io_op);
 
382
                        if (retval)
 
383
                                printk(O_ERR "Unable to suspend the %s feature (error %i).\n", feature->name, retval);
 
384
                }
 
385
        }
 
386
        return 0;
 
387
}
 
388
 
 
389
/* 
 
390
 * Callback function for system resume
 
391
 */
 
392
static int omnibook_resume(struct platform_device *dev)
 
393
{
 
394
        int retval;
 
395
        struct omnibook_feature *feature;
 
396
 
 
397
        list_for_each_entry(feature, &omnibook_available_feature->list, list) {
 
398
                if (feature->resume) {
 
399
                        retval = feature->resume(feature->io_op);
 
400
                        if (retval)
 
401
                                printk(O_ERR "Unable to resume the %s feature (error %i).\n", feature->name, retval);
 
402
                }
 
403
        }
 
404
        return 0;
 
405
}
 
406
 
 
407
/* 
 
408
 * Find a given available feature by its name
 
409
 */
 
410
struct omnibook_feature *omnibook_find_feature(char *name)
 
411
{
 
412
        struct omnibook_feature *feature;
 
413
 
 
414
        list_for_each_entry(feature, &omnibook_available_feature->list, list) {
 
415
                if (!strcmp(feature->name, name))
 
416
                        return feature;
 
417
        }
 
418
        return NULL;
 
419
}
 
420
 
 
421
/*
 
422
 * Maintain compatibility with the old ectype numbers:
 
423
 * ex: The user set/get ectype=12 for TSM70=2^(12-1)
 
424
 */
 
425
static int __init set_ectype_param(const char *val, struct kernel_param *kp)
 
426
{
 
427
        char *endp;
 
428
        int value;
 
429
 
 
430
        if (!val)
 
431
                return -EINVAL;
 
432
 
 
433
        value = simple_strtol(val, &endp, 10);
 
434
        if (endp == val)        /* No match */
 
435
                return -EINVAL;
 
436
        omnibook_ectype = 1 << (value - 1);
 
437
        return 0;
 
438
}
 
439
 
 
440
static int get_ectype_param(char *buffer, struct kernel_param *kp)
 
441
{
 
442
        return sprintf(buffer, "%i", ffs(omnibook_ectype));
 
443
}
 
444
 
 
445
static int __init omnibook_module_init(void)
 
446
{
 
447
        int retval;
 
448
 
 
449
        printk(O_INFO "Driver version %s.\n", OMNIBOOK_MODULE_VERSION);
 
450
 
 
451
        if (omnibook_ectype != NONE)
 
452
                printk(O_WARN "Forced load with EC type %i.\n", ffs(omnibook_ectype));
 
453
        else if (dmi_check_system(omnibook_ids))
 
454
                printk(O_INFO "%s detected.\n", laptop_model);
 
455
        else
 
456
                printk(O_INFO "Unknown model.\n");
 
457
 
 
458
        omnibook_proc_root = proc_mkdir(OMNIBOOK_MODULE_NAME, NULL);
 
459
        if (!omnibook_proc_root) {
 
460
                printk(O_ERR "Unable to create /proc/%s.\n", OMNIBOOK_MODULE_NAME);
 
461
                return -ENOENT;
 
462
        }
 
463
 
 
464
/*
 
465
 * The platform_driver interface was added in linux 2.6.15
 
466
 */
 
467
 
 
468
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15))
 
469
 
 
470
        retval = platform_driver_register(&omnibook_driver);
 
471
        if (retval < 0)
 
472
                return retval;
 
473
 
 
474
        omnibook_device = platform_device_alloc(OMNIBOOK_MODULE_NAME, -1);
 
475
        if (!omnibook_device) {
 
476
                platform_driver_unregister(&omnibook_driver);
 
477
                return -ENOMEM;
 
478
        }
 
479
 
 
480
        retval = platform_device_add(omnibook_device);
 
481
        if (retval) {
 
482
                platform_device_put(omnibook_device);
 
483
                platform_driver_unregister(&omnibook_driver);
 
484
                return retval;
 
485
        }
 
486
#else                           /* 2.6.15 */
 
487
 
 
488
        retval = driver_register(&omnibook_driver);
 
489
        if (retval < 0)
 
490
                return retval;
 
491
 
 
492
        retval = platform_device_register(&omnibook_device);
 
493
 
 
494
        if (retval) {
 
495
                driver_unregister(&omnibook_driver);
 
496
                return retval;
 
497
        }
 
498
#endif
 
499
        return 0;
 
500
}
 
501
 
 
502
static void __exit omnibook_module_cleanup(void)
 
503
{
 
504
 
 
505
/*
 
506
 * The platform_driver interface was added in linux 2.6.15
 
507
 */
 
508
 
 
509
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15))
 
510
        platform_device_unregister(omnibook_device);
 
511
        platform_driver_unregister(&omnibook_driver);
 
512
#else
 
513
        platform_device_unregister(&omnibook_device);
 
514
        driver_unregister(&omnibook_driver);
 
515
#endif
 
516
 
 
517
        if (omnibook_proc_root)
 
518
                remove_proc_entry("omnibook", NULL);
 
519
        printk(O_INFO "Module is unloaded.\n");
 
520
}
 
521
 
 
522
module_init(omnibook_module_init);
 
523
module_exit(omnibook_module_cleanup);
 
524
 
 
525
MODULE_AUTHOR("Soós Péter, Mathieu Bérard");
 
526
MODULE_VERSION(OMNIBOOK_MODULE_VERSION);
 
527
MODULE_DESCRIPTION
 
528
    ("Kernel interface for HP OmniBook, HP Pavilion, Toshiba Satellite and Compal ACL00 laptops");
 
529
MODULE_LICENSE("GPL");
 
530
module_param_call(ectype, set_ectype_param, get_ectype_param, NULL, S_IRUGO);
 
531
module_param_named(userset, omnibook_userset, int, S_IRUGO);
 
532
MODULE_PARM_DESC(ectype, "Type of embedded controller firmware");
 
533
MODULE_PARM_DESC(userset, "Use 0 to disable, 1 to enable users to set parameters");
 
534
 
 
535
/* End of file */