Merge branch 'stable-3.2' into pandora-3.2
[pandora-kernel.git] / drivers / staging / android / ram_console.c
1 /* drivers/android/ram_console.c
2  *
3  * Copyright (C) 2007-2008 Google, Inc.
4  *
5  * This software is licensed under the terms of the GNU General Public
6  * License version 2, as published by the Free Software Foundation, and
7  * may be copied, distributed, and modified under those terms.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  */
15
16 #include <linux/console.h>
17 #include <linux/init.h>
18 #include <linux/module.h>
19 #include <linux/platform_device.h>
20 #include <linux/proc_fs.h>
21 #include <linux/string.h>
22 #include <linux/uaccess.h>
23 #include <linux/io.h>
24 #include "persistent_ram.h"
25 #include "ram_console.h"
26
27 static struct persistent_ram_zone *ram_console_zone;
28 static const char *bootinfo;
29 static size_t bootinfo_size;
30
31 static void
32 ram_console_write(struct console *console, const char *s, unsigned int count)
33 {
34         struct persistent_ram_zone *prz = console->data;
35         persistent_ram_write(prz, s, count);
36 }
37
38 static struct console ram_console = {
39         .name   = "ram",
40         .write  = ram_console_write,
41         .flags  = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME,
42         .index  = -1,
43 };
44
45 void ram_console_enable_console(int enabled)
46 {
47         if (enabled)
48                 ram_console.flags |= CON_ENABLED;
49         else
50                 ram_console.flags &= ~CON_ENABLED;
51 }
52
53 static int __init ram_console_probe(struct platform_device *pdev)
54 {
55         struct ram_console_platform_data *pdata = pdev->dev.platform_data;
56         struct persistent_ram_zone *prz;
57
58         prz = persistent_ram_init_ringbuffer(&pdev->dev, true);
59         if (IS_ERR(prz))
60                 return PTR_ERR(prz);
61
62
63         if (pdata) {
64                 bootinfo = kstrdup(pdata->bootinfo, GFP_KERNEL);
65                 if (bootinfo)
66                         bootinfo_size = strlen(bootinfo);
67         }
68
69         ram_console_zone = prz;
70         ram_console.data = prz;
71
72         register_console(&ram_console);
73
74         return 0;
75 }
76
77 static struct platform_driver ram_console_driver = {
78         .driver         = {
79                 .name   = "ram_console",
80         },
81 };
82
83 static int __init ram_console_module_init(void)
84 {
85         return platform_driver_probe(&ram_console_driver, ram_console_probe);
86 }
87
88 #ifndef CONFIG_PRINTK
89 #define dmesg_restrict  0
90 #endif
91
92 static ssize_t ram_console_read_old(struct file *file, char __user *buf,
93                                     size_t len, loff_t *offset)
94 {
95         loff_t pos = *offset;
96         ssize_t count;
97         struct persistent_ram_zone *prz = ram_console_zone;
98         size_t old_log_size = persistent_ram_old_size(prz);
99         const char *old_log = persistent_ram_old(prz);
100         char *str;
101         int ret;
102
103         if (dmesg_restrict && !capable(CAP_SYSLOG))
104                 return -EPERM;
105
106         /* Main last_kmsg log */
107         if (pos < old_log_size) {
108                 count = min(len, (size_t)(old_log_size - pos));
109                 if (copy_to_user(buf, old_log + pos, count))
110                         return -EFAULT;
111                 goto out;
112         }
113
114         /* ECC correction notice */
115         pos -= old_log_size;
116         count = persistent_ram_ecc_string(prz, NULL, 0);
117         if (pos < count) {
118                 str = kmalloc(count, GFP_KERNEL);
119                 if (!str)
120                         return -ENOMEM;
121                 persistent_ram_ecc_string(prz, str, count + 1);
122                 count = min(len, (size_t)(count - pos));
123                 ret = copy_to_user(buf, str + pos, count);
124                 kfree(str);
125                 if (ret)
126                         return -EFAULT;
127                 goto out;
128         }
129
130         /* Boot info passed through pdata */
131         pos -= count;
132         if (pos < bootinfo_size) {
133                 count = min(len, (size_t)(bootinfo_size - pos));
134                 if (copy_to_user(buf, bootinfo + pos, count))
135                         return -EFAULT;
136                 goto out;
137         }
138
139         /* EOF */
140         return 0;
141
142 out:
143         *offset += count;
144         return count;
145 }
146
147 static const struct file_operations ram_console_file_ops = {
148         .owner = THIS_MODULE,
149         .read = ram_console_read_old,
150 };
151
152 static int __init ram_console_late_init(void)
153 {
154         struct proc_dir_entry *entry;
155         struct persistent_ram_zone *prz = ram_console_zone;
156
157         if (!prz)
158                 return 0;
159
160         if (persistent_ram_old_size(prz) == 0)
161                 return 0;
162
163         entry = create_proc_entry("last_kmsg", S_IFREG | S_IRUGO, NULL);
164         if (!entry) {
165                 printk(KERN_ERR "ram_console: failed to create proc entry\n");
166                 persistent_ram_free_old(prz);
167                 return 0;
168         }
169
170         entry->proc_fops = &ram_console_file_ops;
171         entry->size = persistent_ram_old_size(prz) +
172                 persistent_ram_ecc_string(prz, NULL, 0) +
173                 bootinfo_size;
174
175         return 0;
176 }
177
178 late_initcall(ram_console_late_init);
179 postcore_initcall(ram_console_module_init);