/**************************************************************************** * drivers/note/noteram_driver.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL # ifdef CONFIG_LIB_SYSCALL # include # else # define CONFIG_LIB_SYSCALL # include # undef CONFIG_LIB_SYSCALL # endif #endif /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define NCPUS CONFIG_SMP_NCPUS /* Renumber idle task PIDs * In NuttX, PID number less than NCPUS are idle tasks. * In Linux, there is only one idle task of PID 0. */ #define get_pid(pid) ((pid) < NCPUS ? 0 : (pid)) #define get_task_state(s) \ ((s) == 0 ? 'X' : ((s) <= LAST_READY_TO_RUN_STATE ? 'R' : 'S')) /**************************************************************************** * Private Types ****************************************************************************/ struct noteram_driver_s { struct note_driver_s driver; FAR uint8_t *ni_buffer; size_t ni_bufsize; unsigned int ni_overwrite; volatile unsigned int ni_head; volatile unsigned int ni_tail; volatile unsigned int ni_read; spinlock_t lock; }; /* The structure to hold the context data of trace dump */ struct noteram_dump_cpu_context_s { int intr_nest; /* Interrupt nest level */ bool pendingswitch; /* sched_switch pending flag */ int current_state; /* Task state of the current line */ pid_t current_pid; /* Task PID of the current line */ pid_t next_pid; /* Task PID of the next line */ uint8_t current_priority; /* Task Priority of the current line */ uint8_t next_priority; /* Task Priority of the next line */ }; struct noteram_dump_context_s { struct noteram_dump_cpu_context_s cpu[NCPUS]; }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static int noteram_open(FAR struct file *filep); static int noteram_close(FAR struct file *filep); static ssize_t noteram_read(FAR struct file *filep, FAR char *buffer, size_t buflen); static int noteram_ioctl(struct file *filep, int cmd, unsigned long arg); static void noteram_add(FAR struct note_driver_s *drv, FAR const void *note, size_t len); static void noteram_dump_init_context(FAR struct noteram_dump_context_s *ctx); static int noteram_dump_one(FAR uint8_t *p, FAR struct lib_outstream_s *s, FAR struct noteram_dump_context_s *ctx); /**************************************************************************** * Private Data ****************************************************************************/ static const struct file_operations g_noteram_fops = { noteram_open, /* open */ noteram_close, /* close */ noteram_read, /* read */ NULL, /* write */ NULL, /* seek */ noteram_ioctl, /* ioctl */ }; static uint8_t g_ramnote_buffer[CONFIG_DRIVERS_NOTERAM_BUFSIZE]; static const struct note_driver_ops_s g_noteram_ops = { noteram_add }; /**************************************************************************** * Public Data ****************************************************************************/ struct noteram_driver_s g_noteram_driver = { {&g_noteram_ops}, g_ramnote_buffer, CONFIG_DRIVERS_NOTERAM_BUFSIZE, #ifdef CONFIG_DRIVERS_NOTERAM_DEFAULT_NOOVERWRITE NOTERAM_MODE_OVERWRITE_DISABLE #else NOTERAM_MODE_OVERWRITE_ENABLE #endif }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: noteram_buffer_clear * * Description: * Clear all contents of the circular buffer. * * Input Parameters: * None. * * Returned Value: * None. * ****************************************************************************/ static void noteram_buffer_clear(FAR struct noteram_driver_s *drv) { drv->ni_tail = drv->ni_head; drv->ni_read = drv->ni_head; if (drv->ni_overwrite == NOTERAM_MODE_OVERWRITE_OVERFLOW) { drv->ni_overwrite = NOTERAM_MODE_OVERWRITE_DISABLE; } } /**************************************************************************** * Name: noteram_next * * Description: * Return the circular buffer index at offset from the specified index * value, handling wraparound * * Input Parameters: * ndx - Old circular buffer index * * Returned Value: * New circular buffer index * ****************************************************************************/ static inline unsigned int noteram_next(FAR struct noteram_driver_s *drv, unsigned int ndx, unsigned int offset) { ndx += offset; if (ndx >= drv->ni_bufsize) { ndx -= drv->ni_bufsize; } return ndx; } /**************************************************************************** * Name: noteram_length * * Description: * Length of data currently in circular buffer. * * Input Parameters: * None * * Returned Value: * Length of data currently in circular buffer. * ****************************************************************************/ static unsigned int noteram_length(FAR struct noteram_driver_s *drv) { unsigned int head = drv->ni_head; unsigned int tail = drv->ni_tail; if (tail > head) { head += drv->ni_bufsize; } return head - tail; } /**************************************************************************** * Name: noteram_unread_length * * Description: * Length of unread data currently in circular buffer. * * Input Parameters: * None * * Returned Value: * Length of unread data currently in circular buffer. * ****************************************************************************/ static unsigned int noteram_unread_length(FAR struct noteram_driver_s *drv) { unsigned int head = drv->ni_head; unsigned int read = drv->ni_read; if (read > head) { head += drv->ni_bufsize; } return head - read; } /**************************************************************************** * Name: noteram_remove * * Description: * Remove the variable length note from the tail of the circular buffer * * Input Parameters: * None * * Returned Value: * None * * Assumptions: * We are within a critical section. * ****************************************************************************/ static void noteram_remove(FAR struct noteram_driver_s *drv) { unsigned int tail; unsigned int length; /* Get the tail index of the circular buffer */ tail = drv->ni_tail; DEBUGASSERT(tail < drv->ni_bufsize); /* Get the length of the note at the tail index */ length = drv->ni_buffer[tail]; DEBUGASSERT(length <= noteram_length(drv)); /* Increment the tail index to remove the entire note from the circular * buffer. */ if (drv->ni_read == drv->ni_tail) { /* The read index also needs increment. */ drv->ni_read = noteram_next(drv, tail, length); } drv->ni_tail = noteram_next(drv, tail, length); } /**************************************************************************** * Name: noteram_get * * Description: * Get the next note from the read index of the circular buffer. * * Input Parameters: * buffer - Location to return the next note * buflen - The length of the user provided buffer. * * Returned Value: * On success, the positive, non-zero length of the return note is * provided. Zero is returned only if the circular buffer is empty. A * negated errno value is returned in the event of any failure. * ****************************************************************************/ static ssize_t noteram_get(FAR struct noteram_driver_s *drv, FAR uint8_t *buffer, size_t buflen) { FAR struct note_common_s *note; unsigned int remaining; unsigned int read; ssize_t notelen; size_t circlen; DEBUGASSERT(buffer != NULL); /* Verify that the circular buffer is not empty */ circlen = noteram_unread_length(drv); if (circlen <= 0) { return 0; } /* Get the read index of the circular buffer */ read = drv->ni_read; DEBUGASSERT(read < drv->ni_bufsize); /* Get the length of the note at the read index */ note = (FAR struct note_common_s *)&drv->ni_buffer[read]; notelen = note->nc_length; DEBUGASSERT(notelen <= circlen); /* Is the user buffer large enough to hold the note? */ if (buflen < notelen) { /* Skip the large note so that we do not get constipated. */ drv->ni_read = noteram_next(drv, read, notelen); /* and return an error */ return -EFBIG; } /* Loop until the note has been transferred to the user buffer */ remaining = (unsigned int)notelen; while (remaining > 0) { /* Copy the next byte at the read index */ *buffer++ = drv->ni_buffer[read]; /* Adjust indices and counts */ read = noteram_next(drv, read, 1); remaining--; } drv->ni_read = read; return notelen; } /**************************************************************************** * Name: noteram_open ****************************************************************************/ static int noteram_open(FAR struct file *filep) { FAR struct noteram_dump_context_s *ctx; FAR struct noteram_driver_s *drv = (FAR struct noteram_driver_s *) filep->f_inode->i_private; /* Reset the read index of the circular buffer */ drv->ni_read = drv->ni_tail; ctx = kmm_zalloc(sizeof(*ctx)); if (ctx == NULL) { return -ENOMEM; } filep->f_priv = ctx; noteram_dump_init_context(ctx); return OK; } int noteram_close(FAR struct file *filep) { FAR struct noteram_dump_context_s *ctx = filep->f_priv; kmm_free(ctx); return OK; } /**************************************************************************** * Name: noteram_read ****************************************************************************/ static ssize_t noteram_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { FAR struct noteram_dump_context_s *ctx = filep->f_priv; FAR struct noteram_driver_s *drv = filep->f_inode->i_private; FAR struct lib_memoutstream_s stream; ssize_t ret; lib_memoutstream(&stream, buffer, buflen); do { irqstate_t flags; uint8_t note[256]; /* Get the next note (removing it from the buffer) */ flags = spin_lock_irqsave_wo_note(&drv->lock); ret = noteram_get(drv, note, sizeof(note)); spin_unlock_irqrestore_wo_note(&drv->lock, flags); if (ret <= 0) { return ret; } /* Parse notes into text format */ ret = noteram_dump_one(note, (FAR struct lib_outstream_s *)&stream, ctx); } while (ret == 0); return ret; } /**************************************************************************** * Name: noteram_ioctl ****************************************************************************/ static int noteram_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { int ret = -ENOSYS; FAR struct noteram_driver_s *drv = filep->f_inode->i_private; irqstate_t flags = spin_lock_irqsave_wo_note(&drv->lock); /* Handle the ioctl commands */ switch (cmd) { /* NOTERAM_CLEAR * - Clear all contents of the circular buffer * Argument: Ignored */ case NOTERAM_CLEAR: noteram_buffer_clear(drv); ret = OK; break; /* NOTERAM_GETMODE * - Get overwrite mode * Argument: A writable pointer to unsigned int */ case NOTERAM_GETMODE: if (arg == 0) { ret = -EINVAL; } else { *(unsigned int *)arg = drv->ni_overwrite; ret = OK; } break; /* NOTERAM_SETMODE * - Set overwrite mode * Argument: A read-only pointer to unsigned int */ case NOTERAM_SETMODE: if (arg == 0) { ret = -EINVAL; } else { drv->ni_overwrite = *(unsigned int *)arg; ret = OK; } break; default: break; } spin_unlock_irqrestore_wo_note(&drv->lock, flags); return ret; } /**************************************************************************** * Name: noteram_add * * Description: * Add the variable length note to the transport layer * * Input Parameters: * note - The note buffer * notelen - The buffer length * * Returned Value: * None * * Assumptions: * We are within a critical section. * ****************************************************************************/ static void noteram_add(FAR struct note_driver_s *driver, FAR const void *note, size_t notelen) { FAR const char *buf = note; FAR struct noteram_driver_s *drv = (FAR struct noteram_driver_s *)driver; unsigned int head; unsigned int remain; unsigned int space; irqstate_t flags; flags = spin_lock_irqsave_wo_note(&drv->lock); if (drv->ni_overwrite == NOTERAM_MODE_OVERWRITE_OVERFLOW) { spin_unlock_irqrestore_wo_note(&drv->lock, flags); return; } DEBUGASSERT(note != NULL && notelen < drv->ni_bufsize); remain = drv->ni_bufsize - noteram_length(drv); if (remain < notelen) { if (drv->ni_overwrite == NOTERAM_MODE_OVERWRITE_DISABLE) { /* Stop recording if not in overwrite mode */ drv->ni_overwrite = NOTERAM_MODE_OVERWRITE_OVERFLOW; spin_unlock_irqrestore_wo_note(&drv->lock, flags); return; } /* Remove the note at the tail index , make sure there is enough space */ do { noteram_remove(drv); remain = drv->ni_bufsize - noteram_length(drv); } while (remain < notelen); } head = drv->ni_head; space = drv->ni_bufsize - head; space = space < notelen ? space : notelen; memcpy(drv->ni_buffer + head, note, space); memcpy(drv->ni_buffer, buf + space, notelen - space); drv->ni_head = noteram_next(drv, head, notelen); spin_unlock_irqrestore_wo_note(&drv->lock, flags); } /**************************************************************************** * Name: noteram_dump_unflatten ****************************************************************************/ static void noteram_dump_unflatten(FAR void *dst, FAR uint8_t *src, size_t len) { #ifdef CONFIG_ENDIAN_BIG FAR uint8_t *end = (FAR uint8_t *)dst + len - 1; while (len-- > 0) { *end-- = *src++; } #else memcpy(dst, src, len); #endif } /**************************************************************************** * Name: noteram_dump_init_context ****************************************************************************/ static void noteram_dump_init_context(FAR struct noteram_dump_context_s *ctx) { int cpu; /* Initialize the trace dump context */ for (cpu = 0; cpu < NCPUS; cpu++) { ctx->cpu[cpu].intr_nest = 0; ctx->cpu[cpu].pendingswitch = false; ctx->cpu[cpu].current_state = TSTATE_TASK_RUNNING; ctx->cpu[cpu].current_pid = -1; ctx->cpu[cpu].next_pid = -1; ctx->cpu[cpu].current_priority = -1; ctx->cpu[cpu].next_priority = -1; } } /**************************************************************************** * Name: get_task_name ****************************************************************************/ static const char *get_task_name(pid_t pid) { FAR const char *taskname; taskname = note_get_taskname(pid); if (taskname != NULL) { return taskname; } return ""; } /**************************************************************************** * Name: noteram_dump_header ****************************************************************************/ static int noteram_dump_header(FAR struct lib_outstream_s *s, FAR struct note_common_s *note, FAR struct noteram_dump_context_s *ctx) { pid_t pid; uint32_t nsec; uint32_t sec; int ret; noteram_dump_unflatten(&nsec, note->nc_systime_nsec, sizeof(nsec)); noteram_dump_unflatten(&sec, note->nc_systime_sec, sizeof(sec)); #ifdef CONFIG_SMP int cpu = note->nc_cpu; #else int cpu = 0; #endif noteram_dump_unflatten(&pid, note->nc_pid, sizeof(pid)); ret = lib_sprintf(s, "%8s-%-3u [%d] %3" PRIu32 ".%09" PRIu32 ": ", get_task_name(pid), get_pid(pid), cpu, sec, nsec); return ret; } #if (defined CONFIG_SCHED_INSTRUMENTATION_SWITCH) \ || (defined CONFIG_SCHED_INSTRUMENTATION_IRQHANDLER) /**************************************************************************** * Name: noteram_dump_sched_switch ****************************************************************************/ static int noteram_dump_sched_switch(FAR struct lib_outstream_s *s, FAR struct note_common_s *note, FAR struct noteram_dump_context_s *ctx) { FAR struct noteram_dump_cpu_context_s *cctx; uint8_t current_priority; uint8_t next_priority; pid_t current_pid; pid_t next_pid; int ret; #ifdef CONFIG_SMP int cpu = note->nc_cpu; #else int cpu = 0; #endif cctx = &ctx->cpu[cpu]; current_pid = cctx->current_pid; next_pid = cctx->next_pid; current_priority = cctx->current_priority; next_priority = cctx->next_priority; ret = lib_sprintf(s, "sched_switch: prev_comm=%s prev_pid=%u " "prev_prio=%u prev_state=%c ==> " "next_comm=%s next_pid=%u next_prio=%u\n", get_task_name(current_pid), get_pid(current_pid), current_priority, get_task_state(cctx->current_state), get_task_name(next_pid), get_pid(next_pid), next_priority); cctx->current_pid = cctx->next_pid; cctx->current_priority = cctx->next_priority; cctx->pendingswitch = false; return ret; } #endif /**************************************************************************** * Name: noteram_dump_one ****************************************************************************/ static int noteram_dump_one(FAR uint8_t *p, FAR struct lib_outstream_s *s, FAR struct noteram_dump_context_s *ctx) { FAR struct note_common_s *note = (FAR struct note_common_s *)p; FAR struct noteram_dump_cpu_context_s *cctx; int ret = 0; pid_t pid; #ifdef CONFIG_SMP int cpu = note->nc_cpu; #else int cpu = 0; #endif cctx = &ctx->cpu[cpu]; noteram_dump_unflatten(&pid, note->nc_pid, sizeof(pid)); if (cctx->current_pid < 0) { cctx->current_pid = pid; } /* Output one note */ switch (note->nc_type) { case NOTE_START: { ret += noteram_dump_header(s, note, ctx); ret += lib_sprintf(s, "sched_wakeup_new: comm=%s pid=%d " "target_cpu=%d\n", get_task_name(pid), get_pid(pid), cpu); } break; case NOTE_STOP: { /* This note informs the task to be stopped. * Change current task state for the succeeding NOTE_RESUME. */ cctx->current_state = 0; } break; #ifdef CONFIG_SCHED_INSTRUMENTATION_SWITCH case NOTE_SUSPEND: { FAR struct note_suspend_s *nsu = (FAR struct note_suspend_s *)p; /* This note informs the task to be suspended. * Preserve the information for the succeeding NOTE_RESUME. */ cctx->current_state = nsu->nsu_state; } break; case NOTE_RESUME: { /* This note informs the task to be resumed. * The task switch timing depends on the running context. */ cctx->next_pid = pid; cctx->next_priority = note->nc_priority; if (cctx->intr_nest == 0) { /* If not in the interrupt context, the task switch is * executed immediately. */ ret += noteram_dump_header(s, note, ctx); ret += noteram_dump_sched_switch(s, note, ctx); } else { /* If in the interrupt context, the task switch is postponed * until leaving the interrupt handler. */ ret += noteram_dump_header(s, note, ctx); ret += lib_sprintf(s, "sched_waking: comm=%s " "pid=%d target_cpu=%d\n", get_task_name(cctx->next_pid), get_pid(cctx->next_pid), cpu); cctx->pendingswitch = true; } } break; #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL case NOTE_SYSCALL_ENTER: { FAR struct note_syscall_enter_s *nsc; int i; int j; uintptr_t arg; nsc = (FAR struct note_syscall_enter_s *)p; if (nsc->nsc_nr < CONFIG_SYS_RESERVED || nsc->nsc_nr >= SYS_maxsyscall) { break; } ret += noteram_dump_header(s, note, ctx); ret += lib_sprintf(s, "sys_%s(", g_funcnames[nsc->nsc_nr - CONFIG_SYS_RESERVED]); for (i = j = 0; i < nsc->nsc_argc; i++) { noteram_dump_unflatten(&arg, nsc->nsc_args, sizeof(arg)); if (i == 0) { ret += lib_sprintf(s, "arg%d: 0x%" PRIxPTR, i, arg); } else { ret += lib_sprintf(s, ", arg%d: 0x%" PRIxPTR, i, arg); } } ret += lib_sprintf(s, ")\n"); } break; case NOTE_SYSCALL_LEAVE: { FAR struct note_syscall_leave_s *nsc; uintptr_t result; nsc = (FAR struct note_syscall_leave_s *)p; if (nsc->nsc_nr < CONFIG_SYS_RESERVED || nsc->nsc_nr >= SYS_maxsyscall) { break; } ret += noteram_dump_header(s, note, ctx); noteram_dump_unflatten(&result, nsc->nsc_result, sizeof(result)); ret += lib_sprintf(s, "sys_%s -> 0x%" PRIxPTR "\n", g_funcnames[nsc->nsc_nr - CONFIG_SYS_RESERVED], result); } break; #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_IRQHANDLER case NOTE_IRQ_ENTER: { FAR struct note_irqhandler_s *nih; nih = (FAR struct note_irqhandler_s *)p; ret += noteram_dump_header(s, note, ctx); ret += lib_sprintf(s, "irq_handler_entry: irq=%u name=%pS\n", nih->nih_irq, (FAR void *)nih->nih_handler); cctx->intr_nest++; } break; case NOTE_IRQ_LEAVE: { FAR struct note_irqhandler_s *nih; nih = (FAR struct note_irqhandler_s *)p; ret += noteram_dump_header(s, note, ctx); ret += lib_sprintf(s, "irq_handler_exit: irq=%u ret=handled\n", nih->nih_irq); cctx->intr_nest--; if (cctx->intr_nest <= 0) { cctx->intr_nest = 0; if (cctx->pendingswitch) { /* If the pending task switch exists, it is executed here */ ret += noteram_dump_header(s, note, ctx); ret += noteram_dump_sched_switch(s, note, ctx); } } } break; #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_CSECTION case NOTE_CSECTION_ENTER: case NOTE_CSECTION_LEAVE: { struct note_csection_s *ncs; ncs = (FAR struct note_csection_s *)p; ret += noteram_dump_header(s, &ncs->ncs_cmn, ctx); ret += lib_sprintf(s, "tracing_mark_write: %c|%d|critical_section\n", note->nc_type == NOTE_CSECTION_ENTER ? 'B' : 'E', pid); } break; #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_PREEMPTION case NOTE_PREEMPT_LOCK: case NOTE_PREEMPT_UNLOCK: { struct note_preempt_s *npr; int16_t count; npr = (FAR struct note_preempt_s *)p; noteram_dump_unflatten(&count, npr->npr_count, sizeof(count)); ret += noteram_dump_header(s, &npr->npr_cmn, ctx); ret += lib_sprintf(s, "tracing_mark_write: " "%c|%d|sched_lock:%d\n", note->nc_type == NOTE_PREEMPT_LOCK ? 'B' : 'E', pid, count); } break; #endif #ifdef CONFIG_SCHED_INSTRUMENTATION_DUMP case NOTE_DUMP_STRING: { FAR struct note_string_s *nst; uintptr_t ip; nst = (FAR struct note_string_s *)p; ret += noteram_dump_header(s, note, ctx); noteram_dump_unflatten(&ip, nst->nst_ip, sizeof(ip)); if (nst->nst_data[1] == '\0' && (nst->nst_data[0] == 'B' || nst->nst_data[0] == 'E')) { ret += lib_sprintf(s, "tracing_mark_write: %c|%d|%pS\n", nst->nst_data[0], pid, (FAR void *)ip); } else { ret += lib_sprintf(s, "tracing_mark_write: %s\n", nst->nst_data); } } break; case NOTE_DUMP_BINARY: { FAR struct note_binary_s *nbi; uint8_t count; uintptr_t ip; int i; nbi = (FAR struct note_binary_s *)p; ret += noteram_dump_header(s, note, ctx); count = note->nc_length - sizeof(struct note_binary_s) + 1; noteram_dump_unflatten(&ip, nbi->nbi_ip, sizeof(ip)); ret += lib_sprintf(s, "0x%" PRIdPTR ": event=%u count=%u", ip, nbi->nbi_event, count); for (i = 0; i < count; i++) { ret += lib_sprintf(s, " 0x%x", nbi->nbi_data[i]); } ret += lib_sprintf(s, "\n"); } break; #endif default: break; } /* Return the length of the processed note */ return ret; } #ifdef CONFIG_DRIVERS_NOTERAM_CRASH_DUMP /**************************************************************************** * Name: noteram_dump ****************************************************************************/ static void noteram_dump(FAR struct noteram_driver_s *drv) { struct noteram_dump_context_s ctx; struct lib_syslograwstream_s stream; uint8_t note[64]; lib_syslograwstream_open(&stream); lib_sprintf(&stream.public, "# tracer:nop\n#\n"); while (1) { ssize_t ret; ret = noteram_get(drv, note, sizeof(note)); if (ret <= 0) { break; } noteram_dump_one(note, &stream.public, &ctx); } lib_syslograwstream_close(&stream); } /**************************************************************************** * Name: noteram_crash_dump ****************************************************************************/ static int noteram_crash_dump(FAR struct notifier_block *nb, unsigned long action, FAR void *data) { if (action == PANIC_KERNEL) { noteram_dump(&g_noteram_driver); } return 0; } static void noteram_crash_dump_register(void) { static struct notifier_block nb; nb.notifier_call = noteram_crash_dump; panic_notifier_chain_register(&nb); } #endif /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: noteram_register * * Description: * Register a serial driver at /dev/note/ram that can be used by an * application to read data from the circular note buffer. * * Input Parameters: * None. * * Returned Value: * Zero on succress. A negated errno value is returned on a failure. * ****************************************************************************/ int noteram_register(void) { #ifdef CONFIG_DRIVERS_NOTERAM_CRASH_DUMP noteram_crash_dump_register(); #endif return register_driver("/dev/note/ram", &g_noteram_fops, 0666, &g_noteram_driver); } /**************************************************************************** * Name: noteram_initialize * * Description: * Register a serial driver at /dev/note/ram that can be used by an * application to read data from the circular note buffer. * * Input Parameters: * devpath: The path of the Noteram device * bufsize: The size of the circular buffer * overwrite: The overwrite mode * * Returned Value: * Zero on succress. A negated errno value is returned on a failure. * ****************************************************************************/ FAR struct note_driver_s * noteram_initialize(FAR const char *devpath, size_t bufsize, bool overwrite) { FAR struct noteram_driver_s *drv; int ret; drv = (FAR struct noteram_driver_s *)kmm_malloc(sizeof(*drv) + bufsize); if (drv == NULL) { return NULL; } drv->driver.ops = &g_noteram_ops; drv->ni_bufsize = bufsize; drv->ni_buffer = (FAR uint8_t *)(drv + 1); drv->ni_overwrite = overwrite; drv->ni_head = 0; drv->ni_tail = 0; drv->ni_read = 0; ret = note_driver_register(&drv->driver); if (ret < 0) { kmm_free(drv); return NULL; } if (devpath == NULL) { return &drv->driver; } ret = register_driver(devpath, &g_noteram_fops, 0666, drv); if (ret < 0) { kmm_free(drv); return NULL; } return &drv->driver; }