[Android Software/Hacking General [Developers Only]] llrraa2010:
Logger driver

kernel/common/drivers/staging/android/logger.h

kernel/common/drivers/staging/android/logger.c

1. data structure

logger.h:

#ifndef _LINUX_LOGGER_H

#define _LINUX_LOGGER_H

#include <linux/types.h>

#include <linux/ioctl.h>

struct logger_entry {

__u16	len;	/* length of the payload */

__u16	__pad;	/* no matter what, we get 2 bytes of padding */

__s32	pid;	/* generating process's pid */

__s32	tid;	/* generating process's tid */

__s32	sec;	/* seconds since Epoch */

__s32	nsec;	/* nanoseconds */

char	msg[0];	/* the entry's payload */

};

#define LOGGER_LOG_RADIO	"log_radio"	/* radio-related messages */

#define LOGGER_LOG_EVENTS	"log_events"	/* system/hardware events */

#define LOGGER_LOG_MAIN	"log_main"	/* everything else */

#define LOGGER_ENTRY_MAX_LEN	(4*1024)

#define LOGGER_ENTRY_MAX_PAYLOAD	\

(LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))

#define __LOGGERIO	0xAE

#define LOGGER_GET_LOG_BUF_SIZE	_IO(__LOGGERIO, 1) /* size of log */

#define LOGGER_GET_LOG_LEN	_IO(__LOGGERIO, 2) /* used log len */

#define LOGGER_GET_NEXT_ENTRY_LEN	_IO(__LOGGERIO, 3) /* next entry len */

#define LOGGER_FLUSH_LOG	_IO(__LOGGERIO, 4) /* flush log */

#endif /* _LINUX_LOGGER_H */

logger.c:

/*

* struct logger_log - represents a specific log, such as 'main' or 'radio'

*

* This structure lives from module insertion until module removal, so it does

* not need additional reference counting. The structure is protected by the

* mutex 'mutex'.

*/

struct logger_log {

unsigned char *	buffer;	/* the ring buffer itself */

struct miscdevice	misc;	/* misc device representing the log */

wait_queue_head_t	wq;	/* wait queue for readers */

struct list_head	readers; /* this log's readers */

struct mutex	mutex;	/* mutex protecting buffer */

size_t	w_off;	/* current write head offset */

size_t	head;	/* new readers start here */

size_t	size;	/* size of the log */

};

/*

* struct logger_reader - a logging device open for reading

*

* This object lives from open to release, so we don't need additional

* reference counting. The structure is protected by log->mutex.

*/

struct logger_reader {

struct logger_log *	log;	/* associated log */

struct list_head	list;	/* entry in logger_log's list */

size_t	r_off;	/* current read head offset */

};

/* logger_offset - returns index 'n' into the log via (optimized) modulus */

#define logger_offset(n)	((n) & (log->size - 1))

2.initialization

/*

* Defines a log structure with name 'NAME' and a size of 'SIZE' bytes, which

* must be a power of two, greater than LOGGER_ENTRY_MAX_LEN, and less than

* LONG_MAX minus LOGGER_ENTRY_MAX_LEN.

*/

#define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) \

static unsigned char _buf_ ## VAR[SIZE]; \

static struct logger_log VAR = { \

.buffer = _buf_ ## VAR, \

.misc = { \

.minor = MISC_DYNAMIC_MINOR, \

.name = NAME, \

.fops = &logger_fops, \

.parent = NULL, \

}, \

.wq = __WAIT_QUEUE_HEAD_INITIALIZER(VAR .wq), \

.readers = LIST_HEAD_INIT(VAR .readers), \

.mutex = __MUTEX_INITIALIZER(VAR .mutex), \

.w_off = 0, \

.head = 0, \

.size = SIZE, \

};

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, 64*1024)

3. read the log

/*

* logger_read - our log's read() method

*

* Behavior:

*

*	- O_NONBLOCK works

*	- If there are no log entries to read, blocks until log is written to

*	- Atomically reads exactly one log entry

*

* Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno to EINVAL if read

* buffer is insufficient to hold next entry.

*/

static ssize_t logger_read(struct file *file, char __user *buf,

size_t count, loff_t *pos)

{

struct logger_reader *reader = file->private_data;

struct logger_log *log = reader->log;

ssize_t ret;

DEFINE_WAIT(wait);

start:

while (1) {

prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE);

mutex_lock(&log->mutex);

ret = (log->w_off == reader->r_off);

mutex_unlock(&log->mutex);

if (!ret)

break;

if (file->f_flags & O_NONBLOCK) {

ret = -EAGAIN;

break;

}

if (signal_pending(current)) {

ret = -EINTR;

break;

}

schedule();

}

finish_wait(&log->wq, &wait);

if (ret)

return ret;

mutex_lock(&log->mutex);

/* is there still something to read or did we race? */

if (unlikely(log->w_off == reader->r_off)) {

mutex_unlock(&log->mutex);

goto start;

}

/* get the size of the next entry */

ret = get_entry_len(log, reader->r_off);

if (count < ret) {

ret = -EINVAL;

goto out;

}

/* get exactly one entry from the log */

ret = do_read_log_to_user(log, reader, buf, ret);

out:

mutex_unlock(&log->mutex);

return ret;

}

4. write the log

/*

* logger_aio_write - our write method, implementing support for write(),

* writev(), and aio_write(). Writes are our fast path, and we try to optimize

* them above all else.

*/

ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov,

unsigned long nr_segs, loff_t ppos)

{

struct logger_log *log = file_get_log(iocb->ki_filp);

size_t orig = log->w_off;

struct logger_entry header;

struct timespec now;

ssize_t ret = 0;

now = current_kernel_time();

header.pid = current->tgid;

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);

/* null writes succeed, return zero */

if (unlikely(!header.len))

return 0;

mutex_lock(&log->mutex);

/*

* Fix up any readers, pulling them forward to the first readable

* entry after (what will be) the new write offset. We do this now

* because if we partially fail, we can end up with clobbered log

* entries that encroach on readable buffer.

*/

fix_up_readers(log, sizeof(struct logger_entry) + 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 */

len = min_t(size_t, iov->iov_len, header.len - ret);

/* write out this segment's payload */

nr = do_write_log_from_user(log, iov->iov_base, len);

if (unlikely(nr < 0)) {

log->w_off = orig;

mutex_unlock(&log->mutex);

return nr;

}

iov++;

ret += nr;

}

mutex_unlock(&log->mutex);

/* wake up any blocked readers */

wake_up_interruptible(&log->wq);

return ret;

}