Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | /* ipm_console.c - Console messages to/from another processor */ /* * Copyright (c) 2015 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include <errno.h> #include <kernel.h> #include <misc/ring_buffer.h> #include <misc/printk.h> #include <stdio.h> #include <ipm.h> #include <console/ipm_console.h> #include <misc/__assert.h> static void ipm_console_thread(void *arg1, void *arg2, void *arg3) { uint8_t size32; uint16_t type; int ret, key; struct device *d; const struct ipm_console_receiver_config_info *config_info; struct ipm_console_receiver_runtime_data *driver_data; int pos; d = (struct device *)arg1; driver_data = d->driver_data; config_info = d->config->config_info; ARG_UNUSED(arg2); size32 = 0; pos = 0; while (1) { k_sem_take(&driver_data->sem, K_FOREVER); ret = sys_ring_buf_get(&driver_data->rb, &type, (uint8_t *)&config_info->line_buf[pos], NULL, &size32); if (ret) { /* Shouldn't ever happen... */ printk("ipm console ring buffer error: %d\n", ret); size32 = 0; continue; } if (config_info->line_buf[pos] == '\n' || pos == config_info->lb_size - 2) { if (pos != config_info->lb_size - 2) { config_info->line_buf[pos] = '\0'; } else { config_info->line_buf[pos + 1] = '\0'; } if (config_info->flags & IPM_CONSOLE_PRINTK) { printk("%s: '%s'\n", d->config->name, config_info->line_buf); } if (config_info->flags & IPM_CONSOLE_STDOUT) { printf("%s: '%s'\n", d->config->name, config_info->line_buf); } pos = 0; } else { ++pos; } /* ISR may have disabled the channel due to full buffer at * some point. If that happened and there is now room, * re-enable it. * * Lock interrupts to avoid pathological scenario where * the buffer fills up in between enabling the channel and * clearing the channel_disabled flag. */ if (driver_data->channel_disabled && sys_ring_buf_space_get(&driver_data->rb)) { key = irq_lock(); ipm_set_enabled(driver_data->ipm_device, 1); driver_data->channel_disabled = 0; irq_unlock(key); } } } static void ipm_console_receive_callback(void *context, uint32_t id, volatile void *data) { struct device *d; struct ipm_console_receiver_runtime_data *driver_data; int ret; ARG_UNUSED(data); d = context; driver_data = d->driver_data; /* Should always be at least one free buffer slot */ ret = sys_ring_buf_put(&driver_data->rb, 0, id, NULL, 0); __ASSERT(ret == 0, "Failed to insert data into ring buffer"); k_sem_give(&driver_data->sem); /* If the buffer is now full, disable future interrupts for this channel * until the thread has a chance to consume characters. * * This works without losing data if the sending side tries to send * more characters because the sending side is making an ipm_send() * call with the wait flag enabled. It blocks until the receiver side * re-enables the channel and consumes the data. */ if (sys_ring_buf_space_get(&driver_data->rb) == 0) { ipm_set_enabled(driver_data->ipm_device, 0); driver_data->channel_disabled = 1; } } int ipm_console_receiver_init(struct device *d) { const struct ipm_console_receiver_config_info *config_info = d->config->config_info; struct ipm_console_receiver_runtime_data *driver_data = d->driver_data; struct device *ipm; ipm = device_get_binding(config_info->bind_to); if (!ipm) { printk("unable to bind IPM console receiver to '%s'\n", config_info->bind_to); return -EINVAL; } if (ipm_max_id_val_get(ipm) < 0xFF) { printk("IPM driver %s doesn't support 8-bit id values", config_info->bind_to); return -EINVAL; } driver_data->ipm_device = ipm; driver_data->channel_disabled = 0; k_sem_init(&driver_data->sem, 0, UINT_MAX); sys_ring_buf_init(&driver_data->rb, config_info->rb_size32, config_info->ring_buf_data); ipm_register_callback(ipm, ipm_console_receive_callback, d); k_thread_spawn(config_info->thread_stack, IPM_CONSOLE_STACK_SIZE, ipm_console_thread, d, NULL, NULL, K_PRIO_COOP(IPM_CONSOLE_PRI), 0, 0); ipm_set_enabled(ipm, 1); return 0; } |