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/persistent_ram.h> 20#include <linux/platform_device.h> 21#include <linux/proc_fs.h> 22#include <linux/string.h> 23#include <linux/uaccess.h> 24#include <linux/io.h> 25#include "ram_console.h" 26 27static struct persistent_ram_zone *ram_console_zone; 28static const char *bootinfo; 29static size_t bootinfo_size; 30 31static void 32ram_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 38static 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 45void 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 53static int __devinit 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 77static struct platform_driver ram_console_driver = { 78 .driver = { 79 .name = "ram_console", 80 }, 81 .probe = ram_console_probe, 82}; 83 84static int __init ram_console_module_init(void) 85{ 86 return platform_driver_register(&ram_console_driver); 87} 88 89#ifndef CONFIG_PRINTK 90#define dmesg_restrict 0 91#endif 92 93static ssize_t ram_console_read_old(struct file *file, char __user *buf, 94 size_t len, loff_t *offset) 95{ 96 loff_t pos = *offset; 97 ssize_t count; 98 struct persistent_ram_zone *prz = ram_console_zone; 99 size_t old_log_size = persistent_ram_old_size(prz); 100 const char *old_log = persistent_ram_old(prz); 101 char *str; 102 int ret; 103 104 /* Main last_kmsg log */ 105 if (pos < old_log_size) { 106 count = min(len, (size_t)(old_log_size - pos)); 107 if (copy_to_user(buf, old_log + pos, count)) 108 return -EFAULT; 109 goto out; 110 } 111 112 /* ECC correction notice */ 113 pos -= old_log_size; 114 count = persistent_ram_ecc_string(prz, NULL, 0); 115 if (pos < count) { 116 str = kmalloc(count, GFP_KERNEL); 117 if (!str) 118 return -ENOMEM; 119 persistent_ram_ecc_string(prz, str, count + 1); 120 count = min(len, (size_t)(count - pos)); 121 ret = copy_to_user(buf, str + pos, count); 122 kfree(str); 123 if (ret) 124 return -EFAULT; 125 goto out; 126 } 127 128 /* Boot info passed through pdata */ 129 pos -= count; 130 if (pos < bootinfo_size) { 131 count = min(len, (size_t)(bootinfo_size - pos)); 132 if (copy_to_user(buf, bootinfo + pos, count)) 133 return -EFAULT; 134 goto out; 135 } 136 137 /* EOF */ 138 return 0; 139 140out: 141 *offset += count; 142 return count; 143} 144 145static const struct file_operations ram_console_file_ops = { 146 .owner = THIS_MODULE, 147 .read = ram_console_read_old, 148}; 149 150static int __init ram_console_late_init(void) 151{ 152 struct proc_dir_entry *entry; 153 struct persistent_ram_zone *prz = ram_console_zone; 154 155 if (!prz) 156 return 0; 157 158 if (persistent_ram_old_size(prz) == 0) 159 return 0; 160 161 entry = create_proc_entry("last_kmsg", S_IFREG | S_IRUGO, NULL); 162 if (!entry) { 163 printk(KERN_ERR "ram_console: failed to create proc entry\n"); 164 persistent_ram_free_old(prz); 165 return 0; 166 } 167 168 entry->proc_fops = &ram_console_file_ops; 169 entry->size = persistent_ram_old_size(prz) + 170 persistent_ram_ecc_string(prz, NULL, 0) + 171 bootinfo_size; 172 173 return 0; 174} 175 176late_initcall(ram_console_late_init); 177postcore_initcall(ram_console_module_init); 178