diff options
author | Robert Love <rlove@google.com> | 2008-12-19 21:02:58 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-01-06 16:52:42 -0500 |
commit | 875f194c48ddae3797f8d61e98aacd8a8ecef927 (patch) | |
tree | 9dd83b571be89cd500dd70b3d38e23b25f6956ef /drivers | |
parent | 9279bcc31698842961a30bb05b00aef7236dd728 (diff) |
Staging: android: add logging driver
Signed-off-by: Robert Love <rlove@google.com>
Signed-off-by: Brian Swetland <swetland@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/staging/android/Kconfig | 4 | ||||
-rw-r--r-- | drivers/staging/android/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/android/logger.c | 607 | ||||
-rw-r--r-- | drivers/staging/android/logger.h | 48 |
4 files changed, 660 insertions, 0 deletions
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 439a9fd81658..c8899581545e 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig | |||
@@ -10,4 +10,8 @@ config ANDROID_BINDER_IPC | |||
10 | tristate "Android Binder IPC Driver" | 10 | tristate "Android Binder IPC Driver" |
11 | default y | 11 | default y |
12 | 12 | ||
13 | config ANDROID_LOGGER | ||
14 | tristate "Android log driver" | ||
15 | default n | ||
16 | |||
13 | endmenu | 17 | endmenu |
diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index 57efdce57c04..b354b5d90143 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile | |||
@@ -1,2 +1,3 @@ | |||
1 | obj-$(CONFIG_ANDROID) += android.o | 1 | obj-$(CONFIG_ANDROID) += android.o |
2 | obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o | 2 | obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o |
3 | obj-$(CONFIG_ANDROID_LOGGER) += logger.o | ||
diff --git a/drivers/staging/android/logger.c b/drivers/staging/android/logger.c new file mode 100644 index 000000000000..ab32003ecd0b --- /dev/null +++ b/drivers/staging/android/logger.c | |||
@@ -0,0 +1,607 @@ | |||
1 | /* | ||
2 | * drivers/misc/logger.c | ||
3 | * | ||
4 | * A Logging Subsystem | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Google, Inc. | ||
7 | * | ||
8 | * Robert Love <rlove@google.com> | ||
9 | * | ||
10 | * This software is licensed under the terms of the GNU General Public | ||
11 | * License version 2, as published by the Free Software Foundation, and | ||
12 | * may be copied, distributed, and modified under those terms. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/fs.h> | ||
22 | #include <linux/miscdevice.h> | ||
23 | #include <linux/uaccess.h> | ||
24 | #include <linux/poll.h> | ||
25 | #include <linux/time.h> | ||
26 | #include "logger.h" | ||
27 | |||
28 | #include <asm/ioctls.h> | ||
29 | |||
30 | /* | ||
31 | * struct logger_log - represents a specific log, such as 'main' or 'radio' | ||
32 | * | ||
33 | * This structure lives from module insertion until module removal, so it does | ||
34 | * not need additional reference counting. The structure is protected by the | ||
35 | * mutex 'mutex'. | ||
36 | */ | ||
37 | struct logger_log { | ||
38 | unsigned char * buffer; /* the ring buffer itself */ | ||
39 | struct miscdevice misc; /* misc device representing the log */ | ||
40 | wait_queue_head_t wq; /* wait queue for readers */ | ||
41 | struct list_head readers; /* this log's readers */ | ||
42 | struct mutex mutex; /* mutex protecting buffer */ | ||
43 | size_t w_off; /* current write head offset */ | ||
44 | size_t head; /* new readers start here */ | ||
45 | size_t size; /* size of the log */ | ||
46 | }; | ||
47 | |||
48 | /* | ||
49 | * struct logger_reader - a logging device open for reading | ||
50 | * | ||
51 | * This object lives from open to release, so we don't need additional | ||
52 | * reference counting. The structure is protected by log->mutex. | ||
53 | */ | ||
54 | struct logger_reader { | ||
55 | struct logger_log * log; /* associated log */ | ||
56 | struct list_head list; /* entry in logger_log's list */ | ||
57 | size_t r_off; /* current read head offset */ | ||
58 | }; | ||
59 | |||
60 | /* logger_offset - returns index 'n' into the log via (optimized) modulus */ | ||
61 | #define logger_offset(n) ((n) & (log->size - 1)) | ||
62 | |||
63 | /* | ||
64 | * file_get_log - Given a file structure, return the associated log | ||
65 | * | ||
66 | * This isn't aesthetic. We have several goals: | ||
67 | * | ||
68 | * 1) Need to quickly obtain the associated log during an I/O operation | ||
69 | * 2) Readers need to maintain state (logger_reader) | ||
70 | * 3) Writers need to be very fast (open() should be a near no-op) | ||
71 | * | ||
72 | * In the reader case, we can trivially go file->logger_reader->logger_log. | ||
73 | * For a writer, we don't want to maintain a logger_reader, so we just go | ||
74 | * file->logger_log. Thus what file->private_data points at depends on whether | ||
75 | * or not the file was opened for reading. This function hides that dirtiness. | ||
76 | */ | ||
77 | static inline struct logger_log * file_get_log(struct file *file) | ||
78 | { | ||
79 | if (file->f_mode & FMODE_READ) { | ||
80 | struct logger_reader *reader = file->private_data; | ||
81 | return reader->log; | ||
82 | } else | ||
83 | return file->private_data; | ||
84 | } | ||
85 | |||
86 | /* | ||
87 | * get_entry_len - Grabs the length of the payload of the next entry starting | ||
88 | * from 'off'. | ||
89 | * | ||
90 | * Caller needs to hold log->mutex. | ||
91 | */ | ||
92 | static __u32 get_entry_len(struct logger_log *log, size_t off) | ||
93 | { | ||
94 | __u16 val; | ||
95 | |||
96 | switch (log->size - off) { | ||
97 | case 1: | ||
98 | memcpy(&val, log->buffer + off, 1); | ||
99 | memcpy(((char *) &val) + 1, log->buffer, 1); | ||
100 | break; | ||
101 | default: | ||
102 | memcpy(&val, log->buffer + off, 2); | ||
103 | } | ||
104 | |||
105 | return sizeof(struct logger_entry) + val; | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * do_read_log_to_user - reads exactly 'count' bytes from 'log' into the | ||
110 | * user-space buffer 'buf'. Returns 'count' on success. | ||
111 | * | ||
112 | * Caller must hold log->mutex. | ||
113 | */ | ||
114 | static ssize_t do_read_log_to_user(struct logger_log *log, | ||
115 | struct logger_reader *reader, | ||
116 | char __user *buf, | ||
117 | size_t count) | ||
118 | { | ||
119 | size_t len; | ||
120 | |||
121 | /* | ||
122 | * We read from the log in two disjoint operations. First, we read from | ||
123 | * the current read head offset up to 'count' bytes or to the end of | ||
124 | * the log, whichever comes first. | ||
125 | */ | ||
126 | len = min(count, log->size - reader->r_off); | ||
127 | if (copy_to_user(buf, log->buffer + reader->r_off, len)) | ||
128 | return -EFAULT; | ||
129 | |||
130 | /* | ||
131 | * Second, we read any remaining bytes, starting back at the head of | ||
132 | * the log. | ||
133 | */ | ||
134 | if (count != len) | ||
135 | if (copy_to_user(buf + len, log->buffer, count - len)) | ||
136 | return -EFAULT; | ||
137 | |||
138 | reader->r_off = logger_offset(reader->r_off + count); | ||
139 | |||
140 | return count; | ||
141 | } | ||
142 | |||
143 | /* | ||
144 | * logger_read - our log's read() method | ||
145 | * | ||
146 | * Behavior: | ||
147 | * | ||
148 | * - O_NONBLOCK works | ||
149 | * - If there are no log entries to read, blocks until log is written to | ||
150 | * - Atomically reads exactly one log entry | ||
151 | * | ||
152 | * Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno to EINVAL if read | ||
153 | * buffer is insufficient to hold next entry. | ||
154 | */ | ||
155 | static ssize_t logger_read(struct file *file, char __user *buf, | ||
156 | size_t count, loff_t *pos) | ||
157 | { | ||
158 | struct logger_reader *reader = file->private_data; | ||
159 | struct logger_log *log = reader->log; | ||
160 | ssize_t ret; | ||
161 | DEFINE_WAIT(wait); | ||
162 | |||
163 | start: | ||
164 | while (1) { | ||
165 | prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE); | ||
166 | |||
167 | mutex_lock(&log->mutex); | ||
168 | ret = (log->w_off == reader->r_off); | ||
169 | mutex_unlock(&log->mutex); | ||
170 | if (!ret) | ||
171 | break; | ||
172 | |||
173 | if (file->f_flags & O_NONBLOCK) { | ||
174 | ret = -EAGAIN; | ||
175 | break; | ||
176 | } | ||
177 | |||
178 | if (signal_pending(current)) { | ||
179 | ret = -EINTR; | ||
180 | break; | ||
181 | } | ||
182 | |||
183 | schedule(); | ||
184 | } | ||
185 | |||
186 | finish_wait(&log->wq, &wait); | ||
187 | if (ret) | ||
188 | return ret; | ||
189 | |||
190 | mutex_lock(&log->mutex); | ||
191 | |||
192 | /* is there still something to read or did we race? */ | ||
193 | if (unlikely(log->w_off == reader->r_off)) { | ||
194 | mutex_unlock(&log->mutex); | ||
195 | goto start; | ||
196 | } | ||
197 | |||
198 | /* get the size of the next entry */ | ||
199 | ret = get_entry_len(log, reader->r_off); | ||
200 | if (count < ret) { | ||
201 | ret = -EINVAL; | ||
202 | goto out; | ||
203 | } | ||
204 | |||
205 | /* get exactly one entry from the log */ | ||
206 | ret = do_read_log_to_user(log, reader, buf, ret); | ||
207 | |||
208 | out: | ||
209 | mutex_unlock(&log->mutex); | ||
210 | |||
211 | return ret; | ||
212 | } | ||
213 | |||
214 | /* | ||
215 | * get_next_entry - return the offset of the first valid entry at least 'len' | ||
216 | * bytes after 'off'. | ||
217 | * | ||
218 | * Caller must hold log->mutex. | ||
219 | */ | ||
220 | static size_t get_next_entry(struct logger_log *log, size_t off, size_t len) | ||
221 | { | ||
222 | size_t count = 0; | ||
223 | |||
224 | do { | ||
225 | size_t nr = get_entry_len(log, off); | ||
226 | off = logger_offset(off + nr); | ||
227 | count += nr; | ||
228 | } while (count < len); | ||
229 | |||
230 | return off; | ||
231 | } | ||
232 | |||
233 | /* | ||
234 | * clock_interval - is a < c < b in mod-space? Put another way, does the line | ||
235 | * from a to b cross c? | ||
236 | */ | ||
237 | static inline int clock_interval(size_t a, size_t b, size_t c) | ||
238 | { | ||
239 | if (b < a) { | ||
240 | if (a < c || b >= c) | ||
241 | return 1; | ||
242 | } else { | ||
243 | if (a < c && b >= c) | ||
244 | return 1; | ||
245 | } | ||
246 | |||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | /* | ||
251 | * fix_up_readers - walk the list of all readers and "fix up" any who were | ||
252 | * lapped by the writer; also do the same for the default "start head". | ||
253 | * We do this by "pulling forward" the readers and start head to the first | ||
254 | * entry after the new write head. | ||
255 | * | ||
256 | * The caller needs to hold log->mutex. | ||
257 | */ | ||
258 | static void fix_up_readers(struct logger_log *log, size_t len) | ||
259 | { | ||
260 | size_t old = log->w_off; | ||
261 | size_t new = logger_offset(old + len); | ||
262 | struct logger_reader *reader; | ||
263 | |||
264 | if (clock_interval(old, new, log->head)) | ||
265 | log->head = get_next_entry(log, log->head, len); | ||
266 | |||
267 | list_for_each_entry(reader, &log->readers, list) | ||
268 | if (clock_interval(old, new, reader->r_off)) | ||
269 | reader->r_off = get_next_entry(log, reader->r_off, len); | ||
270 | } | ||
271 | |||
272 | /* | ||
273 | * do_write_log - writes 'len' bytes from 'buf' to 'log' | ||
274 | * | ||
275 | * The caller needs to hold log->mutex. | ||
276 | */ | ||
277 | static void do_write_log(struct logger_log *log, const void *buf, size_t count) | ||
278 | { | ||
279 | size_t len; | ||
280 | |||
281 | len = min(count, log->size - log->w_off); | ||
282 | memcpy(log->buffer + log->w_off, buf, len); | ||
283 | |||
284 | if (count != len) | ||
285 | memcpy(log->buffer, buf + len, count - len); | ||
286 | |||
287 | log->w_off = logger_offset(log->w_off + count); | ||
288 | |||
289 | } | ||
290 | |||
291 | /* | ||
292 | * do_write_log_user - writes 'len' bytes from the user-space buffer 'buf' to | ||
293 | * the log 'log' | ||
294 | * | ||
295 | * The caller needs to hold log->mutex. | ||
296 | * | ||
297 | * Returns 'count' on success, negative error code on failure. | ||
298 | */ | ||
299 | static ssize_t do_write_log_from_user(struct logger_log *log, | ||
300 | const void __user *buf, size_t count) | ||
301 | { | ||
302 | size_t len; | ||
303 | |||
304 | len = min(count, log->size - log->w_off); | ||
305 | if (len && copy_from_user(log->buffer + log->w_off, buf, len)) | ||
306 | return -EFAULT; | ||
307 | |||
308 | if (count != len) | ||
309 | if (copy_from_user(log->buffer, buf + len, count - len)) | ||
310 | return -EFAULT; | ||
311 | |||
312 | log->w_off = logger_offset(log->w_off + count); | ||
313 | |||
314 | return count; | ||
315 | } | ||
316 | |||
317 | /* | ||
318 | * logger_aio_write - our write method, implementing support for write(), | ||
319 | * writev(), and aio_write(). Writes are our fast path, and we try to optimize | ||
320 | * them above all else. | ||
321 | */ | ||
322 | ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov, | ||
323 | unsigned long nr_segs, loff_t ppos) | ||
324 | { | ||
325 | struct logger_log *log = file_get_log(iocb->ki_filp); | ||
326 | size_t orig = log->w_off; | ||
327 | struct logger_entry header; | ||
328 | struct timespec now; | ||
329 | ssize_t ret = 0; | ||
330 | |||
331 | now = current_kernel_time(); | ||
332 | |||
333 | header.pid = current->tgid; | ||
334 | header.tid = current->pid; | ||
335 | header.sec = now.tv_sec; | ||
336 | header.nsec = now.tv_nsec; | ||
337 | header.len = min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD); | ||
338 | |||
339 | /* null writes succeed, return zero */ | ||
340 | if (unlikely(!header.len)) | ||
341 | return 0; | ||
342 | |||
343 | mutex_lock(&log->mutex); | ||
344 | |||
345 | /* | ||
346 | * Fix up any readers, pulling them forward to the first readable | ||
347 | * entry after (what will be) the new write offset. We do this now | ||
348 | * because if we partially fail, we can end up with clobbered log | ||
349 | * entries that encroach on readable buffer. | ||
350 | */ | ||
351 | fix_up_readers(log, sizeof(struct logger_entry) + header.len); | ||
352 | |||
353 | do_write_log(log, &header, sizeof(struct logger_entry)); | ||
354 | |||
355 | while (nr_segs-- > 0) { | ||
356 | size_t len; | ||
357 | ssize_t nr; | ||
358 | |||
359 | /* figure out how much of this vector we can keep */ | ||
360 | len = min_t(size_t, iov->iov_len, header.len - ret); | ||
361 | |||
362 | /* write out this segment's payload */ | ||
363 | nr = do_write_log_from_user(log, iov->iov_base, len); | ||
364 | if (unlikely(nr < 0)) { | ||
365 | log->w_off = orig; | ||
366 | mutex_unlock(&log->mutex); | ||
367 | return nr; | ||
368 | } | ||
369 | |||
370 | iov++; | ||
371 | ret += nr; | ||
372 | } | ||
373 | |||
374 | mutex_unlock(&log->mutex); | ||
375 | |||
376 | /* wake up any blocked readers */ | ||
377 | wake_up_interruptible(&log->wq); | ||
378 | |||
379 | return ret; | ||
380 | } | ||
381 | |||
382 | static struct logger_log * get_log_from_minor(int); | ||
383 | |||
384 | /* | ||
385 | * logger_open - the log's open() file operation | ||
386 | * | ||
387 | * Note how near a no-op this is in the write-only case. Keep it that way! | ||
388 | */ | ||
389 | static int logger_open(struct inode *inode, struct file *file) | ||
390 | { | ||
391 | struct logger_log *log; | ||
392 | int ret; | ||
393 | |||
394 | ret = nonseekable_open(inode, file); | ||
395 | if (ret) | ||
396 | return ret; | ||
397 | |||
398 | log = get_log_from_minor(MINOR(inode->i_rdev)); | ||
399 | if (!log) | ||
400 | return -ENODEV; | ||
401 | |||
402 | if (file->f_mode & FMODE_READ) { | ||
403 | struct logger_reader *reader; | ||
404 | |||
405 | reader = kmalloc(sizeof(struct logger_reader), GFP_KERNEL); | ||
406 | if (!reader) | ||
407 | return -ENOMEM; | ||
408 | |||
409 | reader->log = log; | ||
410 | INIT_LIST_HEAD(&reader->list); | ||
411 | |||
412 | mutex_lock(&log->mutex); | ||
413 | reader->r_off = log->head; | ||
414 | list_add_tail(&reader->list, &log->readers); | ||
415 | mutex_unlock(&log->mutex); | ||
416 | |||
417 | file->private_data = reader; | ||
418 | } else | ||
419 | file->private_data = log; | ||
420 | |||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | /* | ||
425 | * logger_release - the log's release file operation | ||
426 | * | ||
427 | * Note this is a total no-op in the write-only case. Keep it that way! | ||
428 | */ | ||
429 | static int logger_release(struct inode *ignored, struct file *file) | ||
430 | { | ||
431 | if (file->f_mode & FMODE_READ) { | ||
432 | struct logger_reader *reader = file->private_data; | ||
433 | list_del(&reader->list); | ||
434 | kfree(reader); | ||
435 | } | ||
436 | |||
437 | return 0; | ||
438 | } | ||
439 | |||
440 | /* | ||
441 | * logger_poll - the log's poll file operation, for poll/select/epoll | ||
442 | * | ||
443 | * Note we always return POLLOUT, because you can always write() to the log. | ||
444 | * Note also that, strictly speaking, a return value of POLLIN does not | ||
445 | * guarantee that the log is readable without blocking, as there is a small | ||
446 | * chance that the writer can lap the reader in the interim between poll() | ||
447 | * returning and the read() request. | ||
448 | */ | ||
449 | static unsigned int logger_poll(struct file *file, poll_table *wait) | ||
450 | { | ||
451 | struct logger_reader *reader; | ||
452 | struct logger_log *log; | ||
453 | unsigned int ret = POLLOUT | POLLWRNORM; | ||
454 | |||
455 | if (!(file->f_mode & FMODE_READ)) | ||
456 | return ret; | ||
457 | |||
458 | reader = file->private_data; | ||
459 | log = reader->log; | ||
460 | |||
461 | poll_wait(file, &log->wq, wait); | ||
462 | |||
463 | mutex_lock(&log->mutex); | ||
464 | if (log->w_off != reader->r_off) | ||
465 | ret |= POLLIN | POLLRDNORM; | ||
466 | mutex_unlock(&log->mutex); | ||
467 | |||
468 | return ret; | ||
469 | } | ||
470 | |||
471 | static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
472 | { | ||
473 | struct logger_log *log = file_get_log(file); | ||
474 | struct logger_reader *reader; | ||
475 | long ret = -ENOTTY; | ||
476 | |||
477 | mutex_lock(&log->mutex); | ||
478 | |||
479 | switch (cmd) { | ||
480 | case LOGGER_GET_LOG_BUF_SIZE: | ||
481 | ret = log->size; | ||
482 | break; | ||
483 | case LOGGER_GET_LOG_LEN: | ||
484 | if (!(file->f_mode & FMODE_READ)) { | ||
485 | ret = -EBADF; | ||
486 | break; | ||
487 | } | ||
488 | reader = file->private_data; | ||
489 | if (log->w_off >= reader->r_off) | ||
490 | ret = log->w_off - reader->r_off; | ||
491 | else | ||
492 | ret = (log->size - reader->r_off) + log->w_off; | ||
493 | break; | ||
494 | case LOGGER_GET_NEXT_ENTRY_LEN: | ||
495 | if (!(file->f_mode & FMODE_READ)) { | ||
496 | ret = -EBADF; | ||
497 | break; | ||
498 | } | ||
499 | reader = file->private_data; | ||
500 | if (log->w_off != reader->r_off) | ||
501 | ret = get_entry_len(log, reader->r_off); | ||
502 | else | ||
503 | ret = 0; | ||
504 | break; | ||
505 | case LOGGER_FLUSH_LOG: | ||
506 | if (!(file->f_mode & FMODE_WRITE)) { | ||
507 | ret = -EBADF; | ||
508 | break; | ||
509 | } | ||
510 | list_for_each_entry(reader, &log->readers, list) | ||
511 | reader->r_off = log->w_off; | ||
512 | log->head = log->w_off; | ||
513 | ret = 0; | ||
514 | break; | ||
515 | } | ||
516 | |||
517 | mutex_unlock(&log->mutex); | ||
518 | |||
519 | return ret; | ||
520 | } | ||
521 | |||
522 | static struct file_operations logger_fops = { | ||
523 | .owner = THIS_MODULE, | ||
524 | .read = logger_read, | ||
525 | .aio_write = logger_aio_write, | ||
526 | .poll = logger_poll, | ||
527 | .unlocked_ioctl = logger_ioctl, | ||
528 | .compat_ioctl = logger_ioctl, | ||
529 | .open = logger_open, | ||
530 | .release = logger_release, | ||
531 | }; | ||
532 | |||
533 | /* | ||
534 | * Defines a log structure with name 'NAME' and a size of 'SIZE' bytes, which | ||
535 | * must be a power of two, greater than LOGGER_ENTRY_MAX_LEN, and less than | ||
536 | * LONG_MAX minus LOGGER_ENTRY_MAX_LEN. | ||
537 | */ | ||
538 | #define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) \ | ||
539 | static unsigned char _buf_ ## VAR[SIZE]; \ | ||
540 | static struct logger_log VAR = { \ | ||
541 | .buffer = _buf_ ## VAR, \ | ||
542 | .misc = { \ | ||
543 | .minor = MISC_DYNAMIC_MINOR, \ | ||
544 | .name = NAME, \ | ||
545 | .fops = &logger_fops, \ | ||
546 | .parent = NULL, \ | ||
547 | }, \ | ||
548 | .wq = __WAIT_QUEUE_HEAD_INITIALIZER(VAR .wq), \ | ||
549 | .readers = LIST_HEAD_INIT(VAR .readers), \ | ||
550 | .mutex = __MUTEX_INITIALIZER(VAR .mutex), \ | ||
551 | .w_off = 0, \ | ||
552 | .head = 0, \ | ||
553 | .size = SIZE, \ | ||
554 | }; | ||
555 | |||
556 | DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 64*1024) | ||
557 | DEFINE_LOGGER_DEVICE(log_events, LOGGER_LOG_EVENTS, 256*1024) | ||
558 | DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO, 64*1024) | ||
559 | |||
560 | static struct logger_log * get_log_from_minor(int minor) | ||
561 | { | ||
562 | if (log_main.misc.minor == minor) | ||
563 | return &log_main; | ||
564 | if (log_events.misc.minor == minor) | ||
565 | return &log_events; | ||
566 | if (log_radio.misc.minor == minor) | ||
567 | return &log_radio; | ||
568 | return NULL; | ||
569 | } | ||
570 | |||
571 | static int __init init_log(struct logger_log *log) | ||
572 | { | ||
573 | int ret; | ||
574 | |||
575 | ret = misc_register(&log->misc); | ||
576 | if (unlikely(ret)) { | ||
577 | printk(KERN_ERR "logger: failed to register misc " | ||
578 | "device for log '%s'!\n", log->misc.name); | ||
579 | return ret; | ||
580 | } | ||
581 | |||
582 | printk(KERN_INFO "logger: created %luK log '%s'\n", | ||
583 | (unsigned long) log->size >> 10, log->misc.name); | ||
584 | |||
585 | return 0; | ||
586 | } | ||
587 | |||
588 | static int __init logger_init(void) | ||
589 | { | ||
590 | int ret; | ||
591 | |||
592 | ret = init_log(&log_main); | ||
593 | if (unlikely(ret)) | ||
594 | goto out; | ||
595 | |||
596 | ret = init_log(&log_events); | ||
597 | if (unlikely(ret)) | ||
598 | goto out; | ||
599 | |||
600 | ret = init_log(&log_radio); | ||
601 | if (unlikely(ret)) | ||
602 | goto out; | ||
603 | |||
604 | out: | ||
605 | return ret; | ||
606 | } | ||
607 | device_initcall(logger_init); | ||
diff --git a/drivers/staging/android/logger.h b/drivers/staging/android/logger.h new file mode 100644 index 000000000000..a562434d7419 --- /dev/null +++ b/drivers/staging/android/logger.h | |||
@@ -0,0 +1,48 @@ | |||
1 | /* include/linux/logger.h | ||
2 | * | ||
3 | * Copyright (C) 2007-2008 Google, Inc. | ||
4 | * Author: Robert Love <rlove@android.com> | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #ifndef _LINUX_LOGGER_H | ||
18 | #define _LINUX_LOGGER_H | ||
19 | |||
20 | #include <linux/types.h> | ||
21 | #include <linux/ioctl.h> | ||
22 | |||
23 | struct logger_entry { | ||
24 | __u16 len; /* length of the payload */ | ||
25 | __u16 __pad; /* no matter what, we get 2 bytes of padding */ | ||
26 | __s32 pid; /* generating process's pid */ | ||
27 | __s32 tid; /* generating process's tid */ | ||
28 | __s32 sec; /* seconds since Epoch */ | ||
29 | __s32 nsec; /* nanoseconds */ | ||
30 | char msg[0]; /* the entry's payload */ | ||
31 | }; | ||
32 | |||
33 | #define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */ | ||
34 | #define LOGGER_LOG_EVENTS "log_events" /* system/hardware events */ | ||
35 | #define LOGGER_LOG_MAIN "log_main" /* everything else */ | ||
36 | |||
37 | #define LOGGER_ENTRY_MAX_LEN (4*1024) | ||
38 | #define LOGGER_ENTRY_MAX_PAYLOAD \ | ||
39 | (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry)) | ||
40 | |||
41 | #define __LOGGERIO 0xAE | ||
42 | |||
43 | #define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ | ||
44 | #define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ | ||
45 | #define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ | ||
46 | #define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ | ||
47 | |||
48 | #endif /* _LINUX_LOGGER_H */ | ||