CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_LOGGER_PTI=y
# CONFIG_ANDROID_RAM_CONSOLE is not set
CONFIG_ANDROID_TIMED_OUTPUT=y
# CONFIG_ANDROID_TIMED_GPIO is not set
* in 32 byte chunks.
*/
-static void pti_control_frame_built_and_sent(struct pti_masterchannel *mc)
+static void pti_control_frame_built_and_sent(struct pti_masterchannel *mc,
+ const char *thread_name)
{
struct pti_masterchannel mccontrol = {.master = CONTROL_ID,
.channel = 0};
* we only need to be as large as what 'comm' in that
* structure is.
*/
- char comm[TASK_COMM_LEN];
+ char comm[TASK_COMM_LEN];
/* task information */
- if (in_irq())
+ if (thread_name)
+ strncpy(comm, thread_name, sizeof(comm));
+ else if (in_irq())
strncpy(comm, "hardirq", sizeof(comm));
else if (in_softirq())
strncpy(comm, "softirq", sizeof(comm));
const unsigned char *buf,
int len)
{
- pti_control_frame_built_and_sent(mc);
+ pti_control_frame_built_and_sent(mc, NULL);
pti_write_to_aperture(mc, (u8 *)buf, len);
}
* channel id. The bit is one if the id is taken and 0 if free. For
* every master there are 128 channel id's.
*/
-static struct pti_masterchannel *get_id(u8 *id_array, int max_ids, int base_id)
+static struct pti_masterchannel *get_id(u8 *id_array, int max_ids,
+ int base_id, const char *thread_name)
{
struct pti_masterchannel *mc;
int i, j, mask;
mc->master = base_id;
mc->channel = ((i & 0xf)<<3) + j;
/* write new master Id / channel Id allocation to channel control */
- pti_control_frame_built_and_sent(mc);
+ pti_control_frame_built_and_sent(mc, thread_name);
return mc;
}
/*
* The following three functions:
- * pti_request_mastercahannel(), mipi_release_masterchannel()
+ * pti_request_masterchannel(), mipi_release_masterchannel()
* and pti_writedata() are an API for other kernel drivers to
* access PTI.
*/
* pti_masterchannel struct
* 0 for error
*/
-struct pti_masterchannel *pti_request_masterchannel(u8 type)
+struct pti_masterchannel *pti_request_masterchannel(u8 type, const char *name)
{
struct pti_masterchannel *mc;
switch (type) {
case 0:
- mc = get_id(drv_data->ia_app, MAX_APP_IDS, APP_BASE_ID);
+ mc = get_id(drv_data->ia_app, MAX_APP_IDS, APP_BASE_ID, name);
break;
case 1:
- mc = get_id(drv_data->ia_os, MAX_OS_IDS, OS_BASE_ID);
+ mc = get_id(drv_data->ia_os, MAX_OS_IDS, OS_BASE_ID, name);
break;
case 2:
- mc = get_id(drv_data->ia_modem, MAX_MODEM_IDS, MODEM_BASE_ID);
+ mc = get_id(drv_data->ia_modem, MAX_MODEM_IDS,
+ MODEM_BASE_ID, name);
break;
default:
mc = NULL;
return -ENOMEM;
if (idx == PTITTY_MINOR_START)
- pti_tty_data->mc = pti_request_masterchannel(0);
+ pti_tty_data->mc = pti_request_masterchannel(0, NULL);
else
- pti_tty_data->mc = pti_request_masterchannel(2);
+ pti_tty_data->mc = pti_request_masterchannel(2, NULL);
if (pti_tty_data->mc == NULL) {
kfree(pti_tty_data);
* before assigning the value to filp->private_data.
* Slightly easier to debug if this driver needs debugging.
*/
- mc = pti_request_masterchannel(0);
+ mc = pti_request_masterchannel(0, NULL);
if (mc == NULL)
return -ENOMEM;
filp->private_data = mc;
tristate "Android log driver"
default n
+config ANDROID_LOGGER_PTI
+ tristate "Android log driber on PTI"
+ default n
+
config ANDROID_RAM_CONSOLE
bool "Android RAM buffer console"
default n
obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o
obj-$(CONFIG_ANDROID_LOGGER) += logger.o
+obj-$(CONFIG_ANDROID_LOGGER_PTI) += logger_pti.o
obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o
obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o
obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o
* GNU General Public License for more details.
*/
+#include <linux/console.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/fs.h>
-#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/time.h>
-#include "logger.h"
-
#include <asm/ioctls.h>
+#include "logger.h"
+#include "logger_pti.h"
+
+static DEFINE_SPINLOCK(log_lock);
+static struct work_struct write_console_wq;
/*
* file_get_log - Given a file structure, return the associated log
*
*
* Caller needs to hold log->mutex.
*/
-static __u32 get_entry_len(struct logger_log *log, size_t off)
+__u32 get_entry_len(struct logger_log *log, size_t off)
{
__u16 val;
}
/*
+ * do_read_log - reads exactly 'count' bytes from 'log' into the
+ * kernel buffer 'buf'.
+ *
+ * Caller must hold log->mutex.
+ */
+void do_read_log(struct logger_log *log,
+ struct logger_reader *reader,
+ char *buf,
+ size_t count)
+{
+ size_t len;
+
+ /*
+ * We read from the log in two disjoint operations. First, we read from
+ * the current read head offset up to 'count' bytes or to the end of
+ * the log, whichever comes first.
+ */
+ len = min(count, log->size - reader->r_off);
+ memcpy(buf, log->buffer + reader->r_off, len);
+
+ /*
+ * Second, we read any remaining bytes, starting back at the head of
+ * the log.
+ */
+ if (count != len)
+ memcpy(buf + len, log->buffer, count - len);
+
+ reader->r_off = logger_offset(reader->r_off + count);
+}
+
+/*
* logger_read - our log's read() method
*
* Behavior:
size_t orig = log->w_off;
struct logger_entry header;
struct timespec now;
- ssize_t ret = 0;
+ ssize_t len, ret = 0;
now = current_kernel_time();
header.tid = current->pid;
header.sec = now.tv_sec;
header.nsec = now.tv_nsec;
- header.len = min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD);
+ header.len = min_t(size_t, iocb->ki_left,
+ LOGGER_ENTRY_MAX_PAYLOAD);
/* null writes succeed, return zero */
if (unlikely(!header.len))
do_write_log(log, &header, sizeof(struct logger_entry));
while (nr_segs-- > 0) {
- size_t len;
ssize_t nr;
/* figure out how much of this vector we can keep */
ret += nr;
}
+ log_write_to_pti(log);
+
mutex_unlock(&log->mutex);
/* wake up any blocked readers */
return ret;
}
-static struct logger_log *get_log_from_minor(int);
-
/*
* logger_open - the log's open() file operation
*
.size = SIZE, \
};
-DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 256*1024)
+DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 64*1024)
DEFINE_LOGGER_DEVICE(log_events, LOGGER_LOG_EVENTS, 256*1024)
-DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO, 256*1024)
-DEFINE_LOGGER_DEVICE(log_system, LOGGER_LOG_SYSTEM, 256*1024)
+DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO, 64*1024)
+DEFINE_LOGGER_DEVICE(log_kernel, LOGGER_LOG_KERNEL, 256*1024)
+DEFINE_LOGGER_DEVICE(log_system, LOGGER_LOG_SYSTEM, 64*1024)
+
+DEFINE_LOGGER_DEVICE(log_kernel_bottom, LOGGER_LOG_KERNEL_BOT, 64*1024)
static struct logger_log *log_list[] = { \
&log_main, \
&log_events, \
&log_radio, \
+ &log_kernel, \
&log_system, \
};
+static void flush_to_bottom_log(struct logger_log *log,
+ const char *buf, unsigned int count)
+{
+ struct logger_entry header;
+ char extendedtag[8] = "\4KERNEL";
+ struct timespec now;
+ unsigned long flags;
+
+ now = current_kernel_time();
+
+ header.pid = pid_nr(task_pid(current));
+ header.tid = current->tgid;
+ header.sec = now.tv_sec;
+ header.nsec = now.tv_nsec;
+
+ /* length is computed like this:
+ * 1 byte for the log priority (harcoded to 4 meaning INFO)
+ * 6 bytes for the tag string (harcoded to KERNEL)
+ * 1 byte added at the end of the tag required by logcat
+ * the length of the buf added into the kernel log buffer
+ * 1 byte added at the end of the buf required by logcat
+ */
+ header.len = min_t(size_t, sizeof(extendedtag) + count + 1,
+ LOGGER_ENTRY_MAX_PAYLOAD);
+
+ /* null writes succeed, return zero */
+ if (unlikely(!header.len))
+ return;
+
+ spin_lock_irqsave(&log_lock, flags);
+
+ fix_up_readers(log, sizeof(struct logger_entry) + header.len);
+
+ do_write_log(log, &header, sizeof(struct logger_entry));
+ do_write_log(log, &extendedtag, sizeof(extendedtag));
+ do_write_log(log, buf, header.len - (sizeof(extendedtag)) - 1);
+
+ /* the write offset is updated to add the final extra byte */
+ log->w_off = logger_offset(log->w_off + 1);
+ spin_unlock_irqrestore(&log_lock, flags);
+};
+
+
+/*
+ * update_log_from_bottom - copy bottom log buffer into a log buffer
+ */
+static void update_log_from_bottom(struct logger_log *log_dst,
+ struct logger_log *log)
+{
+ struct logger_reader *reader;
+ size_t len, ret;
+ unsigned long flags;
+
+ mutex_lock(&log_dst->mutex);
+ spin_lock_irqsave(&log_lock, flags);
+
+ list_for_each_entry(reader, &log->readers, list)
+ while (log->w_off != reader->r_off) {
+
+ ret = get_entry_len(log, reader->r_off);
+
+ fix_up_readers(log_dst, ret);
+
+ /*
+ * We read from the log in two disjoint operations.
+ * First, we read from the current read head offset
+ * up to 'count' bytes or to the end of the log,
+ * whichever comes first.
+ */
+ len = min(ret, log->size - reader->r_off);
+ do_write_log(log_dst, log->buffer + reader->r_off, len);
+
+ /*
+ * Second, we read any remaining bytes, starting back at
+ * the head of the log.
+ */
+ if (ret != len)
+ do_write_log(log_dst, log->buffer, ret - len);
+
+ reader->r_off = logger_offset(reader->r_off + ret);
+ }
+ spin_unlock_irqrestore(&log_lock, flags);
+ mutex_unlock(&log_dst->mutex);
+
+ /* wake up any blocked readers */
+ wake_up_interruptible(&log_dst->wq);
+}
+
+/*
+ * write_console - a write method for kernel logs
+ */
+static void write_console(struct work_struct *work)
+{
+ struct logger_log *log_bottom = &log_kernel_bottom;
+ struct logger_log *log = &log_kernel;
+
+ update_log_from_bottom(log, log_bottom);
+}
+
struct logger_log *get_log_from_minor(int minor)
{
int i;
return log_list;
}
-static int __init init_log(struct logger_log *log)
+static int init_log_kernel_bottom(void)
+{
+ struct logger_log *log = &log_kernel_bottom;
+ struct logger_reader *reader;
+
+ reader = kmalloc(sizeof(struct logger_reader), GFP_KERNEL);
+ if (!reader)
+ return -ENOMEM;
+
+ reader->log = log;
+ INIT_LIST_HEAD(&reader->list);
+
+ mutex_lock(&log->mutex);
+ reader->r_off = log->head;
+ list_add_tail(&reader->list, &log->readers);
+ mutex_unlock(&log->mutex);
+ return 0;
+}
+
+static int init_log(struct logger_log *log)
{
int ret;
ret = init_log(log_list[i]);
if (unlikely(ret))
goto out;
+ ret = init_pti(log_list[i]);
+ if (unlikely(ret))
+ goto out;
}
+ ret = init_log_kernel_bottom();
+ if (unlikely(ret))
+ goto out;
+
out:
return ret;
}
device_initcall(logger_init);
+
+static void
+logger_console_write(struct console *console, const char *s, unsigned int count)
+{
+ struct logger_log *log = &log_kernel_bottom;
+ struct logger_log *log_dst = &log_kernel;
+
+ flush_to_bottom_log(log, s, count);
+ log_kernel_write_to_pti(log_dst, s, count);
+
+ if (unlikely(!keventd_up()))
+ return;
+ schedule_work(&write_console_wq);
+}
+
+static struct console logger_console = {
+ .name = "logk",
+ .write = logger_console_write,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+
+static int __init logger_console_init(void)
+{
+ INIT_WORK(&write_console_wq, write_console);
+
+ printk(KERN_INFO "register logcat console\n");
+ register_console(&logger_console);
+ return 0;
+}
+
+console_initcall(logger_console_init);
size_t w_off; /* current write head offset */
size_t head; /* new readers start here */
size_t size; /* size of the log */
+#ifdef CONFIG_ANDROID_LOGGER_PTI
+ bool ptienable;
+ struct pti_reader *pti_reader;
+#endif
};
/*
char msg[0]; /* the entry's payload */
};
+void do_read_log(struct logger_log *log,
+ struct logger_reader *reader,
+ char *buf,
+ size_t count);
+__u32 get_entry_len(struct logger_log *log, size_t off);
+struct logger_log *get_log_from_minor(int minor);
struct logger_log **get_log_list(void);
/* logger_offset - returns index 'n' into the log via (optimized) modulus */
#define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */
#define LOGGER_LOG_EVENTS "log_events" /* system/hardware events */
#define LOGGER_LOG_SYSTEM "log_system" /* system/framework messages */
+#define LOGGER_LOG_KERNEL "log_kernel" /* kernel */
+#define LOGGER_LOG_KERNEL_BOT "log_kernel_bottom" /* kernel bottom */
#define LOGGER_LOG_MAIN "log_main" /* everything else */
-#define LOGGER_LIST_SIZE 4
+#define LOGGER_LIST_SIZE 5
#define LOGGER_ENTRY_MAX_LEN (4*1024)
#define LOGGER_ENTRY_MAX_PAYLOAD \
--- /dev/null
+/*
+ * logger_pti.c - logger messages redirection to PTI
+ *
+ * Copyright (C) Intel 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * To active logger to PTI messages, configure 'out' parameter
+ * of 'logger_pti' module in sysfs with one or more values
+ * # echo "main,system" > /sys/module/logger_pti/parameters/out
+ *
+ * To active logger to PTI messages from boot, add this
+ * commandline parameter to the boot commandline
+ * logger_pti.out=main,system
+ *
+ * Possible log buffers are : main, system, radio, events, kernel
+ * See logger.h if others.
+ */
+
+#include <linux/slab.h>
+#include "logger_pti.h"
+
+/*
+ * init_pti - initialize the pti members with the 'log' passed in
+ * argument
+ */
+int init_pti(struct logger_log *log)
+{
+ struct pti_reader *pti_reader;
+ struct logger_reader *reader;
+ int ret = 0;
+
+ pti_reader = kmalloc(sizeof(struct pti_reader), GFP_KERNEL);
+
+ if (!pti_reader) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ reader = kmalloc(sizeof(struct logger_reader), GFP_KERNEL);
+
+ if (!reader) {
+ kfree(pti_reader);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* entry init */
+ pti_reader->entry = kmalloc(sizeof(struct queued_entry), GFP_KERNEL);
+ if (!pti_reader->entry) {
+ kfree(pti_reader);
+ kfree(reader);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * masterchannel init
+ * request OS channel type : 1, see drivers/misc/pti.c
+ */
+ pti_reader->mc = pti_request_masterchannel(1, log->misc.name);
+ if (!pti_reader->mc) {
+ kfree(pti_reader);
+ kfree(reader);
+ ret = -ENODEV;
+ goto out;
+ }
+
+ pti_reader->reader = reader;
+ reader->r_off = log->w_off;
+ mutex_lock(&log->mutex);
+ log->pti_reader = pti_reader;
+ mutex_unlock(&log->mutex);
+
+ pr_info("logger_pti: %s, mc : %d %d\n", log->misc.name,
+ pti_reader->mc->master, pti_reader->mc->channel);
+
+out:
+ if (unlikely(ret)) {
+ log->pti_reader = NULL;
+ pr_err("logger_pti: failed to init %s\n",
+ log->misc.name);
+ }
+ return ret;
+}
+
+/*
+ * log_kernel_write_to_pti - write a kernel log to the pti master/channel
+ * Can be called in any context
+ *
+ */
+void log_kernel_write_to_pti(struct logger_log *log,
+ const char *buf, size_t count)
+{
+ struct pti_reader *pti_reader = log->pti_reader;
+
+ /* kernel logs are not buffered */
+ if (log->ptienable && pti_reader && pti_reader->mc)
+ pti_writedata(pti_reader->mc, (unsigned char *) buf, count);
+}
+
+/*
+ * log_write_to_pti - write a buffer to the pti master/channel
+ * Can be called in any context
+ *
+ */
+void log_write_to_pti(struct logger_log *log)
+{
+ struct pti_reader *pti_reader;
+ struct logger_reader *reader;
+ size_t count;
+
+ /* other logs are buffered to add the tag from the header*/
+ pti_reader = log->pti_reader;
+ if (unlikely(!pti_reader))
+ return;
+
+ reader = pti_reader->reader;
+
+ /* get the size of the next entry */
+ count = get_entry_len(log, reader->r_off);
+
+ if (log->ptienable) {
+ /* copy exactly one entry from the log */
+ do_read_log(log, reader, pti_reader->entry->buf, count);
+
+ pti_writedata(pti_reader->mc,
+ pti_reader->entry->entry.msg,
+ pti_reader->entry->entry.len);
+ } else {
+ reader->r_off = logger_offset(reader->r_off + count);
+ }
+}
+
+/*
+ * set_out - 'out' parameter set function from 'logger_pti' module
+ *
+ * called when writing to 'out' parameter from 'logger_pti' module in sysfs
+ */
+static int set_out(const char *val, struct kernel_param *kp)
+{
+ int i;
+ const char *log_name;
+ struct logger_log *log;
+ struct logger_log **log_list;
+
+ log_list = get_log_list();
+ for (i = 0; i < LOGGER_LIST_SIZE; i++) {
+ log = log_list[i];
+ log_name = log->misc.name;
+ /* remove "log_" in the log_name string */
+ log_name += 4;
+ if (strstr(val, log_name))
+ log->ptienable = true;
+ else
+ log->ptienable = false;
+ }
+
+ return 0;
+}
+
+/*
+ * get_out - 'out' parameter get function from 'logger_pti' module
+ *
+ * called when reading 'out' parameter from 'logger_pti' module in sysfs
+ */
+static int get_out(char *buffer, struct kernel_param *kp)
+{
+ int i;
+ const char *log_name;
+ const char *k = ",";
+ struct logger_log *log;
+ struct logger_log **log_list;
+
+ log_list = get_log_list();
+ for (i = 0; i < LOGGER_LIST_SIZE; i++) {
+ if (log_list[i]->ptienable) {
+ log = log_list[i];
+ log_name = log->misc.name;
+ /* remove "log_" in the log_name string */
+ log_name += 4;
+ strcat(buffer, log_name);
+ strcat(buffer, k);
+ }
+ }
+ buffer[strlen(buffer)-1] = '\0';
+
+ return strlen(buffer);
+}
+
+module_param_call(out, set_out, get_out, NULL, 0644);
+MODULE_PARM_DESC(out, "configure logger to pti [main|events|radio|system|kernel]");
+
--- /dev/null
+/*
+ * logger_pti.h - logger messages redirection to PTI
+ *
+ * Copyright (C) Intel 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * To active logger to PTI messages, configure 'out' parameter
+ * of 'logger_pti' module in sysfs with one or more values
+ * # echo "main,system" > /sys/module/logger_pti/parameters/out
+ *
+ * To active logger to PTI messages from boot, add this
+ * commandline parameter to the boot commandline
+ * logger_pti.out=main,system
+ *
+ * Possible log buffers are : main, system, radio, events
+ * See logger.h if others.
+ */
+
+#ifndef _LINUX_LOGGER_PTI_H
+#define _LINUX_LOGGER_PTI_H
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pti.h>
+#include <linux/string.h>
+#include "logger.h"
+
+#ifdef CONFIG_ANDROID_LOGGER_PTI
+/*
+ * struct queued_entry - a logger entry with maximum space allocated
+ *
+ * This structure is necessary to retrieve a full logger entry
+ */
+struct queued_entry {
+ union {
+ unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]
+ __attribute__((aligned(4)));
+ struct logger_entry entry __attribute__((aligned(4)));
+ };
+};
+
+/*
+ * struct pti_reader - a specific reader associate to a pti master/channel
+ *
+ * This structure lives from module insertion until module removal, so it does
+ * not need additional reference counting.
+ */
+struct pti_reader {
+ struct logger_reader *reader;
+ struct pti_masterchannel *mc;
+ struct queued_entry *entry;
+};
+
+extern int init_pti(struct logger_log *log);
+extern void log_write_to_pti(struct logger_log *log);
+extern void log_kernel_write_to_pti(struct logger_log *log,
+ const char *buf, size_t count);
+#else
+static inline int init_pti(struct logger_log *log) { return 0; }
+static inline int log_kernel_write_to_pti(struct logger_log *log,
+ const char *buf, size_t count) { return 0; }
+static inline int log_write_to_pti(struct logger_log *log) { return 0; }
+#endif
+
+#endif /* _LINUX_LOGGER_PTI_H */
/* the following functions are defined in misc/pti.c */
void pti_writedata(struct pti_masterchannel *mc, u8 *buf, int count);
-struct pti_masterchannel *pti_request_masterchannel(u8 type);
+struct pti_masterchannel *pti_request_masterchannel(u8 type, const char *name);
void pti_release_masterchannel(struct pti_masterchannel *mc);
#endif /*PTI_H_*/