/*------------------------------------------------------------------------------------------*\ * * Copyright (C) 2007 AVM GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA \*------------------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include /*--- #include ---*/ #include #include #include #include #include #include #include #if defined(CONFIG_PROC_FS) #include #include #include #include #endif /*--- #if defined(CONFIG_PROC_FS) ---*/ #include "tffs_local.h" #if defined(CONFIG_AVM_WATCHDOG) #include #endif/*--- #if defined(CONFIG_AVM_WATCHDOG) ---*/ #define ARRAY_EL(a) (sizeof(a) / sizeof(a[0])) #if defined(CONFIG_MACH_AR934x) || defined(CONFIG_MACH_QCA953x) extern int ath_restore_log_from_ram(char *txtbuf, unsigned int txtlen); extern void ath_inval_log_from_ram(void); #endif/*--- #if defined(CONFIG_MACH_AR934x) ---*/ unsigned int tffs_spi_mode = 0; unsigned int tffs_panic_log_suppress = 0; EXPORT_SYMBOL(tffs_panic_log_suppress); #if !defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) static struct file tffs_panic_file; static struct inode tffs_panic_inode; #endif/*--- #if !defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) ---*/ static unsigned int tffs_panic_open_flag; /*--------------------------------------------------------------------------------*\ * Schreiben im Panic-Kontext \*--------------------------------------------------------------------------------*/ static void tffs_panic_log_open(void) { int ret __attribute__((unused)); if(tffs2_active() == 0){ #if defined(CONFIG_TFFS3) tffs3_panic_log_open(); #endif return; } if(tffs_panic_open_flag == 1) return; #if !defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) memset(&tffs_panic_file, 0, sizeof(tffs_panic_file)); memset(&tffs_panic_inode, 0, sizeof(tffs_panic_inode)); tffs_panic_file.f_flags = O_WRONLY; tffs_panic_inode.i_rdev = MKDEV(0, FLASH_FS_ID_PANIC_LOG); /*--- major muss 0 sein fuer Kernel Mode ---*/ ret = tffs_open(&tffs_panic_inode, &tffs_panic_file); if(ret) { return; } #endif/*--- #if !defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) ---*/ tffs_panic_open_flag = 1; } /*--------------------------------------------------------------------------------*\ * Schreiben im Panic-Kontext \*--------------------------------------------------------------------------------*/ static void tffs_panic_log_write(char *buffer, unsigned int len) { static loff_t off __attribute__((unused)); if(tffs2_active() == 0){ #if defined(CONFIG_TFFS3) tffs3_panic_log_write(buffer, len); #endif return; } if(tffs_panic_open_flag == 0) return; #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) avm_event_send_log(remote_panic, FLAG_REMOTELOG_APPEND, buffer, len); #else /*--- #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) ---*/ tffs_write(&tffs_panic_file, buffer, len, &off); #endif/*--- #else ---*//*--- #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) ---*/ } /*--------------------------------------------------------------------------------*\ * Schreiben im Panic-Kontext \*--------------------------------------------------------------------------------*/ static void tffs_panic_log_close(void) { if(tffs2_active() == 0){ #if defined(CONFIG_TFFS3) tffs3_panic_log_close(); #endif return; } if(tffs_panic_open_flag == 0) return; #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) avm_event_send_log(remote_panic, FLAG_REMOTELOG_APPEND_FINISHED | FLAG_REMOTELOG_REBOOT, NULL, 0); printk("%s wait on reboot from host-cpu\n", __func__); for(;;) ; /*--- ... der arm soll booten - ansonsten auf Watchdog warten ---*/ #else/*--- #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) ---*/ tffs_release(&tffs_panic_inode, &tffs_panic_file); #endif/*--- #if !defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) ---*/ } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static unsigned int get_act_time(void) { struct timeval now; do_gettimeofday(&now); return (unsigned int)(now.tv_sec); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static char *time_to_ascii(unsigned long local_time, char *buf, int len, char *prefix) { static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static char *day[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; struct rtc_time tm; rtc_time_to_tm(local_time, &tm); snprintf(buf, len, "%s %s %02d %02d:%02d:%02d %02d UTC %s", day[tm.tm_wday], month[tm.tm_mon], tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_year + 1900, prefix ); return buf; } /*--------------------------------------------------------------------------------*\ * maxsize of buf: 14 + 10 + 2 + 2 + 2 = 30 \*--------------------------------------------------------------------------------*/ static char *human_readable_time(char *buf, unsigned long buf_size, unsigned long time) { unsigned long seconds, minutes, hours, days; seconds = time % 60; time /= 60; minutes = time % 60; time /= 60; hours = time % 24; time /= 24; days = time; snprintf(buf, buf_size, "%lu d %lu h %lu min %lu s", days, hours, minutes, seconds); return buf; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void tffs_panic_log_printkbuf(void) { if(tffs_panic_log_suppress == 0) { unsigned long printk_get_buffer(char **p_log_buf, unsigned long *p_log_end, unsigned long *p_anzahl); char *buf; unsigned long end; unsigned long anzahl; unsigned long len = printk_get_buffer(&buf, &end, &anzahl); struct timespec uptime; char htime[32], huptime[128]; char time_stamp_buf[sizeof("UPTIME: \n") + sizeof(htime) + sizeof(huptime) + 10 + sizeof("\n( - panic on )\nPANIC LOG VERSION 2.0\n")]; unsigned long time_stamp_buf_len; tffs_panic_log_suppress = 1; tffs_panic_log_open(); do_posix_clock_monotonic_gettime(&uptime); monotonic_to_bootbased(&uptime); time_stamp_buf_len = snprintf(time_stamp_buf, sizeof(time_stamp_buf), "UPTIME: %lu\n(%s - panic on %s)\nPANIC LOG VERSION 2.0\n", (unsigned long)uptime.tv_sec, human_readable_time(huptime, sizeof(huptime), (unsigned long)uptime.tv_sec), time_to_ascii((unsigned long)get_act_time(), htime, sizeof(htime), "") ); tffs_panic_log_write(time_stamp_buf, time_stamp_buf_len); #if defined(CONFIG_AVM_WATCHDOG) AVM_WATCHDOG_emergency_retrigger(); #endif if(anzahl < len) { /*--- alles im Buffer ---*/ tffs_panic_log_write(buf, anzahl); } else { tffs_panic_log_write(buf + end, len - end); #if defined(CONFIG_AVM_WATCHDOG) AVM_WATCHDOG_emergency_retrigger(); #endif tffs_panic_log_write(buf, end); } tffs_panic_log_close(); } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void tffs_panic_log_register_spi(void) { tffs_spi_mode = 1; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static unsigned int calc_sum_and_correct(unsigned char *buf, int buf_len, int correct) { unsigned int i, sum = 0; for(i = 0; i < buf_len; i++) { unsigned int val = buf[i]; if(val == 0) { val = ' '; if(correct) buf[i] = val; /*--- correct buffer (support null-teminated string) ---*/ } sum += val; } return sum ^ (buf_len << 16); } #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SINK) static void *remote_log_event_sink_handle; /*--------------------------------------------------------------------------------*\ * Schreiben eines Remote-Logfiles \*--------------------------------------------------------------------------------*/ void tffs_write_remote_logfile(unsigned int tffs_id, char *buf, int len) { struct _tffs_open *open_data; loff_t offp = 0; int ret; struct file tffs_file; struct inode tffs_inode; memset(&tffs_file, 0, sizeof(tffs_file)); memset(&tffs_inode, 0, sizeof(tffs_inode)); tffs_file.f_flags = O_WRONLY; tffs_inode.i_rdev = MKDEV(MAJOR(tffs.device), tffs_id); ret = tffs_open(&tffs_inode, &tffs_file); if(ret) { /*--- printk(KERN_ERR"%s tffs_open failed ret=%d\n", __func__, ret); ---*/ return; } open_data = (struct _tffs_open *)tffs_file.private_data; open_data->kernel_context = 1; /*--- write-access in kernel-context ---*/ tffs_write(&tffs_file, buf, len, &offp); /*--- printk(KERN_INFO"%s tffs_read ret=%d\n", __func__, ret); ---*/ tffs_release(&tffs_inode, &tffs_file); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void remote_log_sink(void *private __attribute__((unused)), unsigned char *buf, unsigned int len){ unsigned char *logbufuncached; struct _avm_event_log event; if(len < sizeof(struct _avm_event_log)) { printk(KERN_ERR"%s: incompatible event len=%u sizeof=%u\n", __func__, len, sizeof(struct _avm_event_log)); return; } memcpy(&event, buf, min(len, sizeof(event))); if(event.event_header.id != avm_event_id_log) { printk(KERN_ERR"%s: incompatible event (id=%u)\n", __func__, event.event_header.id); return; } switch(event.logtype) { case local_panic: case local_crash: printk(KERN_ERR"%s: ignore logtype(%u)\n", __func__, event.logtype); break; case remote_panic: case remote_crash: { unsigned int cs; unsigned int tffs_id = (event.logtype == remote_crash) ? FLASH_FS_ID_CRASH2_LOG : FLASH_FS_ID_PANIC2_LOG; logbufuncached = (unsigned char *) ioremap_nocache((unsigned int)event.logpointer + ARM_MEM_OFFSET, event.loglen); #if 0 printk(KERN_ERR"%s: logtype(%u) loglen=%u logpointer=%x(%p) cs=%x rf=%x\n", __func__, event.logtype, event.loglen, event.logpointer, logbufuncached, event.checksum, event.rebootflag ); #endif cs = calc_sum_and_correct(logbufuncached, event.loglen, 0); if(cs != event.checksum) { printk(KERN_ERR"%s: type=%d obscure checksum %x != %x -> ignore\n", __func__, event.logtype, event.checksum, cs); iounmap(logbufuncached); return; } tffs_write_remote_logfile(tffs_id, logbufuncached, event.loglen); /*--- printk(KERN_ERR"%s: print '%s'\n", __func__, logbufuncached); ---*/ iounmap(logbufuncached); if(event.rebootflag & 0x1) { restore_printk(); printk(KERN_ERR"\n\nREMOTE-log with rebootflag received -> machine_restart() in 5 s\n\n"); schedule_timeout(5 * HZ); machine_restart(NULL); } break; } default: printk(KERN_ERR"%s: invalid logtype(%u)\n", __func__, event.logtype); break; } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void remote_log_register_sink(void *private __attribute__((unused)), unsigned int param1 __attribute__((unused)), unsigned int param2 __attribute__((unused))){ #if AVM_DIST_EVENT_VERSION > 0x10000 struct _avm_event_id_mask id_mask; #endif if(remote_log_event_sink_handle) { return; } remote_log_event_sink_handle = avm_event_sink_register("log_remote_sink", #if AVM_DIST_EVENT_VERSION > 0x10000 avm_event_build_id_mask(&id_mask, 1, avm_event_id_log), #else 1ULL << avm_event_id_log, #endif remote_log_sink, NULL ); if(remote_log_event_sink_handle == NULL) { printk(KERN_ERR"%s not registered\n", __func__); } printk(KERN_ERR"%s registered\n", __func__); return; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int remote_log_init(void) { return avm_event_node_established(remote_log_register_sink, NULL, 0, 0); } late_initcall(remote_log_init); #endif/*--- #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SINK) ---*/ #define MAX_LOG_IDS 4 /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int map_log_id_to_index(unsigned int id) { switch(id) { case FLASH_FS_ID_PANIC_LOG: return 0x0; case FLASH_FS_ID_PANIC2_LOG: return 0x1; case FLASH_FS_ID_CRASH_LOG: return 0x2; case FLASH_FS_ID_CRASH2_LOG: return 0x3; } return MAX_LOG_IDS; } static unsigned int log_read_multiple[MAX_LOG_IDS]; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int tffs_panic_log_read(unsigned char *buf, int buf_len, unsigned int id) { struct _tffs_open *open_data; loff_t offp = 0; int ret; struct file tffs_file; struct inode tffs_inode; memset(&tffs_file, 0, sizeof(tffs_file)); memset(&tffs_inode, 0, sizeof(tffs_inode)); tffs_file.f_flags = O_RDONLY; tffs_inode.i_rdev = MKDEV(MAJOR(tffs.device), id); ret = tffs_open(&tffs_inode, &tffs_file); if(ret) { /*--- printk(KERN_ERR"%s tffs_open failed ret=%d\n", __func__, ret); ---*/ return ret; } open_data = (struct _tffs_open *)tffs_file.private_data; open_data->kernel_context = 1; /*--- readaccess in kernel-context ---*/ ret = tffs_read(&tffs_file, buf, buf_len, &offp); /*--- printk(KERN_INFO"%s tffs_read ret=%d\n", __func__, ret); ---*/ tffs_release(&tffs_inode, &tffs_file); return ret; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int paniclog_show(struct seq_file *seq, void *data __attribute__((unused))) { /*--- printk(KERN_INFO"%s %p %p\n", __func__, seq->private, data); ---*/ if(seq->private) { seq_printf(seq, "%s", (char *)seq->private); } return 0; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ struct _time_checksum { unsigned int cs; unsigned int ts; /* timestamp on read */ #define READ_BY_CR 0x1 #define READ_BY_SD 0x2 #define FIRST_READ_BY_SD 0x4 /* first read by SD */ unsigned int readmask; }; #define SKIP_UNTIL_CHAR(p, sign) while(*p && (*p != sign)) p++ /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline unsigned COUNT_UNTIL_CHAR(const char *p, unsigned char sign, unsigned char count_sign) { unsigned int count = 0; while(*p && (*p != sign)) { if(*p == count_sign) { count++; } p++; } return count; } /*--------------------------------------------------------------------------------*\ * return: gefuelltes checksum * [id]cs,ts,readmask,[id2]cs,ts,readmask \*--------------------------------------------------------------------------------*/ void parse_crash_env(const char *crashp, struct _time_checksum checksum[]) { memset(checksum, 0, MAX_LOG_IDS * sizeof(struct _time_checksum)); while(*crashp) { unsigned int id, val, val1, val2; if(*crashp == '[') { sscanf(++crashp,"%x]%x,%x,%x", &id, &val, &val1, &val2); if(id < MAX_LOG_IDS) { unsigned int count = COUNT_UNTIL_CHAR(crashp, '[', ','); checksum[id].cs = val; checksum[id].ts = val1; if(count >= 1) { checksum[id].ts = val1; } if(count >= 2) { checksum[id].readmask = val2; } /*--- printk(KERN_INFO"%s: %d(count=%d): %x %x %x\n", __func__, id, count, checksum[id].cs, checksum[id].ts,checksum[id].readmask); ---*/ } SKIP_UNTIL_CHAR(crashp, '['); } else { return; } } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void set_crash_env(char *crashp, int len, struct _time_checksum checksum[]) { unsigned int written, i; for(i = 0; i < MAX_LOG_IDS; i++) { if(len > 0) { written = snprintf(crashp, len, "[%x]%x,%x,%x", i, checksum[i].cs, checksum[i].ts, checksum[i].readmask); len -= written, crashp += written; } } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int __log_proc_open(struct inode *inode __attribute__((unused)), struct file *file, unsigned int crashreport, int id) { char crashenv[128]; char timebuf[128]; const char *timestart = ""; int buf_len = (0x1 << 17) - 64; /*--- ReserveTimestamp ---*/ int len = buf_len; struct _time_checksum saved_checksum[MAX_LOG_IDS]; unsigned int new_read_mask = 0; unsigned int map_idx = map_log_id_to_index(id); char *buf; /*--- printk(KERN_ERR"%s: id=%d map_idx=%x %s\n", __func__, id, map_idx, crashreport ? "cr" : "sd"); ---*/ if(map_idx >= MAX_LOG_IDS) { /*--- printk("%s: error: id=%d too big\n", __func__, map_idx); ---*/ return -ENODATA; } /*--- printk(KERN_ERR"%s: id=%d map_idx=%x %s f:_flags=%x\n", __func__, id, map_idx, crashreport ? "cr" : "sd", file->f_flags); ---*/ if(file->f_flags & O_WRONLY) { unsigned int mapvalue; mapvalue = crashreport ? READ_BY_CR : READ_BY_SD; mapvalue |= map_idx << 8; file->private_data = (void *)mapvalue; return 0; } memset(saved_checksum, 0, sizeof(saved_checksum)); buf = kmalloc(len, GFP_KERNEL); if(buf == NULL) { return -ENOMEM; } len = sizeof(crashenv); crashenv[0] = 0; if(TFFS_Read(NULL, FLASH_FS_CRASH, crashenv, &len) == 0) { parse_crash_env(crashenv, &saved_checksum[0]); } buf[0] = 0; if((len = tffs_panic_log_read(buf, buf_len, id)) > 0) { unsigned int new_sum = calc_sum_and_correct(buf, len, 1); /*--- printk(KERN_INFO"%s: sum=%x len=%d\n", __func__, new_sum, len); ---*/ if(new_sum != saved_checksum[map_idx].cs) { /*--- new panic-log: set struct ---*/ new_read_mask = crashreport ? READ_BY_CR : (READ_BY_SD | FIRST_READ_BY_SD); saved_checksum[map_idx].readmask = 0; saved_checksum[map_idx].ts = get_act_time(); saved_checksum[map_idx].cs = new_sum; } else { new_read_mask = crashreport ? READ_BY_CR : READ_BY_SD; } } else { len = 0; } #if defined(CONFIG_MACH_AR934x) || defined(CONFIG_MACH_QCA953x) if(id == FLASH_FS_ID_PANIC_LOG) { unsigned int sram_len = ath_restore_log_from_ram(buf, buf_len); if(sram_len) { unsigned int new_sram_sum; map_idx = 1; /*--- ... and prefer sram-log---*/ len = sram_len; new_sram_sum = calc_sum_and_correct(buf, len, 1); /*--- printk(KERN_INFO"%s: prefer sram-trace len=%d", __func__, sram_len); ---*/ if(new_sram_sum != saved_checksum[map_idx].cs) { /*--- new sram-panic-log: clear counter ---*/ new_read_mask = crashreport ? READ_BY_CR : (READ_BY_SD | FIRST_READ_BY_SD); saved_checksum[map_idx].readmask = 0; saved_checksum[map_idx].ts = get_act_time(); saved_checksum[map_idx].cs = new_sram_sum; } else { new_read_mask = crashreport ? READ_BY_CR : READ_BY_SD; } } } #endif/*--- #if defined(CONFIG_MACH_AR934x) ---*/ if((len == 0) || (((saved_checksum[map_idx].readmask & new_read_mask) == new_read_mask) && !((log_read_multiple[map_idx] & new_read_mask) == new_read_mask) )) { /*--- check if sent ---*/ kfree(buf); return -ENODATA; } saved_checksum[map_idx].readmask |= new_read_mask; if(saved_checksum[map_idx].ts){ timestart = time_to_ascii(saved_checksum[map_idx].ts, timebuf, sizeof(timebuf), (saved_checksum[map_idx].readmask & FIRST_READ_BY_SD) ? "by support data" : "by crash report"); } sprintf(&buf[len], "-----\n(first) sent on: %s\n", timestart); set_crash_env(crashenv, sizeof(crashenv), saved_checksum); /*--- printk(KERN_INFO"%s: '%s'\n", __func__, crashenv); ---*/ TFFS_Write(NULL, FLASH_FS_CRASH, crashenv, strlen(crashenv) + 1, 0); return single_open(file, paniclog_show, buf); } #define __generic_proc_log_open(name, type, id) static int name(struct inode *inode, struct file *file) { return __log_proc_open(inode, file, type, id); } /*--- Crashreports ---*/ __generic_proc_log_open(paniclog_open_cr, 1, FLASH_FS_ID_PANIC_LOG); __generic_proc_log_open(crashlog_open_cr, 1, FLASH_FS_ID_CRASH_LOG); /*--- Supportdata read every time possible ---*/ __generic_proc_log_open(paniclog_open_sd, 0, FLASH_FS_ID_PANIC_LOG); __generic_proc_log_open(crashlog_open_sd, 0, FLASH_FS_ID_CRASH_LOG); #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SINK) /*--- Crashreports ---*/ __generic_proc_log_open(panic2log_open_cr, 1, FLASH_FS_ID_PANIC2_LOG); __generic_proc_log_open(crash2log_open_cr, 1, FLASH_FS_ID_CRASH2_LOG); /*--- Supportdata read every time possible ---*/ __generic_proc_log_open(panic2log_open_sd, 0, FLASH_FS_ID_PANIC2_LOG); __generic_proc_log_open(crash2log_open_sd, 0, FLASH_FS_ID_CRASH2_LOG); #endif/*--- #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SINK) ---*/ /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static ssize_t log_proc_write(struct file *file, const char *buffer, size_t count, loff_t *offset __maybe_unused) { unsigned char procfs_buffer[64]; size_t procfs_buffer_size; unsigned int id, report; /*--- von wo kommen wir: ---*/ id = ((unsigned int) file->private_data) >> 8; report = ((unsigned int)file->private_data) & 0xFF; if(id >= MAX_LOG_IDS) { return count; } if (count >= sizeof(procfs_buffer)) { procfs_buffer_size = sizeof(procfs_buffer) - 1; } else { procfs_buffer_size = count; } /* write data to the buffer */ if ( copy_from_user(procfs_buffer, buffer, procfs_buffer_size - 1) ) { return -EFAULT; } procfs_buffer[procfs_buffer_size] = 0; if(strstr(procfs_buffer, "readmultiple")) { log_read_multiple[id] |= report; } else if(strstr(procfs_buffer, "readonce")) { log_read_multiple[id] &= ~report; } else { printk(KERN_INFO"unknown option: use readmultiple or readonce\n"); } /*--- printk(KERN_INFO"%s: '%s' %p log_read_multiple[%d]=%x (report=%x)\n", __func__, procfs_buffer, file->private_data, id, log_read_multiple[id], report); ---*/ return count; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int log_proc_release(struct inode *inode, struct file *file) { if(!(file->f_flags & O_WRONLY)) { struct seq_file *seq = file->private_data; kfree(seq->private); seq->private = NULL; /*--- printk(KERN_INFO"%s: free after read\n", __func__); ---*/ return single_release(inode, file); /*--- gibt txtbuf frei ----*/ } /*--- printk(KERN_INFO"%s no free\n", __func__); ---*/ return 0; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _logproc { const char *dir; const char *name; struct file_operations fops; struct proc_dir_entry *dir_entry; } logproc[] = { { dir: "avm/log_cr", name: "panic", fops: {open: paniclog_open_cr, write: log_proc_write, read: seq_read, llseek: seq_lseek, release: log_proc_release }}, { dir: "avm/log_cr", name: "crash", fops: {open: crashlog_open_cr, write: log_proc_write, read: seq_read, llseek: seq_lseek, release: log_proc_release }}, #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SINK) { dir: "avm/log_cr", name: "panic2", fops: {open: panic2log_open_cr,write: log_proc_write, read: seq_read, llseek: seq_lseek, release: log_proc_release }}, { dir: "avm/log_cr", name: "crash2", fops: {open: crash2log_open_cr,write: log_proc_write, read: seq_read, llseek: seq_lseek, release: log_proc_release }}, #endif/*--- #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SINK) ---*/ { dir: "avm/log_sd", name: "panic", fops: {open: paniclog_open_sd, write: log_proc_write, read: seq_read, llseek: seq_lseek, release: log_proc_release }}, { dir: "avm/log_sd", name: "crash", fops: {open: crashlog_open_sd, write: log_proc_write, read: seq_read, llseek: seq_lseek, release: log_proc_release }}, #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SINK) { dir: "avm/log_sd", name: "panic2", fops: {open: panic2log_open_sd, write: log_proc_write, read: seq_read, llseek: seq_lseek, release: log_proc_release }}, { dir: "avm/log_sd", name: "crash2", fops: {open: crash2log_open_sd, write: log_proc_write, read: seq_read, llseek: seq_lseek, release: log_proc_release }}, #endif/*--- #if defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SINK) ---*/ /*--- compatible to the "old" solution: ---*/ { dir: NULL, name: "avm_panic_cr", fops: {open: paniclog_open_cr, write: log_proc_write, read: seq_read, llseek: seq_lseek, release: seq_release_private /*--- gibt txtbuf frei ---*/}}, { dir: NULL, name: "avm_panic_sd", fops: {open: paniclog_open_sd, write: log_proc_write, read: seq_read, llseek: seq_lseek, release: seq_release_private /*--- gibt txtbuf frei ---*/}}, }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int __init prepare_log_proc(void) { struct proc_dir_entry *dir_entry = NULL; const char *lastdir = ""; unsigned int i; if(tffs2_active() == 0){ pr_info("[%s] TFFS version 2 not running\n", __func__); return 0; } /*--- printk(KERN_ERR"%s\n", __FUNCTION__); ---*/ for(i = 0; i < ARRAY_EL(logproc); i++) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) logproc[i].fops.owner = THIS_MODULE; #endif/*--- #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) ---*/ if(logproc[i].dir && strcmp(lastdir, logproc[i].dir)) { dir_entry = proc_mkdir(logproc[i].dir, NULL); lastdir = logproc[i].dir; } #if !defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) if(logproc[i].dir == NULL) { if(!proc_create(logproc[i].name, 0666, NULL, &logproc[i].fops)) { printk(KERN_ERR"%s can't proc_create(%s)\n", __func__, logproc[i].name); } } else if(dir_entry) { logproc[i].dir_entry = dir_entry; if(!proc_create(logproc[i].name, 0666, dir_entry, &logproc[i].fops)) { printk(KERN_ERR"%s can't proc_create(%s)\n", __func__, logproc[i].name); } } #endif/*--- #if !defined(CRASHPANIC_LOG_PER_REMOTE_EVENT_SOURCE) ---*/ } return 0; } late_initcall(prepare_log_proc);