acrn-hypervisor/misc/services/life_mngr/uart_channel.c

358 lines
10 KiB
C

/*
* Copyright (C)2021 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <pthread.h>
#include <limits.h>
#include <stdint.h>
#include "uart_channel.h"
#include "log.h"
#include "config.h"
#include "command.h"
#define SYNC_FMT "sync:%s"
static void parse_channel_dev_id(struct channel_dev *c_dev)
{
char *saveptr;
(void) strtok_r(c_dev->buf, ":", &saveptr);
if (strlen(saveptr) > 0) {
strncpy(c_dev->name, saveptr, CHANNEL_DEV_NAME_MAX - 1U);
LOG_PRINTF("Device fd:%d, VM name:%s\n",
get_uart_dev_fd(c_dev->uart_device), c_dev->name);
}
}
static void add_uart_channel_dev_connection_list(struct channel_dev *c_dev)
{
struct uart_channel *c = c_dev->channel;
pthread_mutex_lock(&c->tty_conn_list_lock);
LIST_INSERT_HEAD(&c->tty_conn_head, c_dev, list);
pthread_mutex_unlock(&c->tty_conn_list_lock);
}
/**
* @brief Wait to connect device in uart channel
*
* Wait sync message from slave channel device, parse slave channel device
* indentifier from sync message, then add channel device into uart channel
* device connection list.
*/
void *listen_uart_channel_dev(void *arg)
{
ssize_t num;
struct channel_dev *c_dev = (struct channel_dev *)arg;
LOG_PRINTF("Lifecycle manager in service VM fd=%d tty node=%s\n",
get_uart_dev_fd(c_dev->uart_device), get_uart_dev_path(c_dev->uart_device));
memset(c_dev->buf, 0, sizeof(c_dev->buf));
while (c_dev->listening) {
num = receive_message_by_uart(c_dev->uart_device, (void *)c_dev->buf,
sizeof(c_dev->buf));
if (num == 0) {
usleep(LISTEN_INTERVAL);
continue;
}
parse_channel_dev_id(c_dev);
if (strncmp(SYNC_CMD, c_dev->buf, sizeof(SYNC_CMD)) == 0) {
/** Add channel device instance into UART connection list */
add_uart_channel_dev_connection_list(c_dev);
c_dev->listening = false;
LOG_PRINTF("Receive sync message from user VM (%s), start to talk.\n",
c_dev->name);
usleep(2 * WAIT_RECV);
(void)send_message_by_uart(c_dev->uart_device, ACK_SYNC, strlen(ACK_SYNC));
sem_post(&c_dev->dev_sem);
}
}
LOG_PRINTF("Lifecycle manager stops to listen device:%s\n",
get_uart_dev_path(c_dev->uart_device));
return NULL;
}
/**
* @brief Wait to connect device in the uart channel
* and poll message
*
* Send sync message every 5 second and wait acked sync message,
* if acked sync message is received, add uart channel device instance
* into uart connection list. It acked sync message is not received, the lifecycle
* manager will exit.
*/
void *connect_uart_channel_dev(void *arg)
{
ssize_t ret;
struct channel_dev *c_dev = (struct channel_dev *)arg;
struct uart_channel *c = c_dev->channel;
char buf[CHANNEL_DEV_NAME_MAX + SYNC_LEN];
snprintf(buf, sizeof(buf), SYNC_FMT, c->conf.identifier);
/* TODO: will add SYNC resending */
LOG_PRINTF("Send sync command:%s identifier=%s\n", buf, c->conf.identifier);
ret = send_message_by_uart(c_dev->uart_device, (void *)buf, strlen(buf));
if (ret < 0) {
LOG_WRITE("Send sync command to service VM fail\n");
} else {
usleep(LISTEN_INTERVAL);
memset(c_dev->buf, 0, sizeof(c_dev->buf));
(void) receive_message_by_uart(c_dev->uart_device, (void *)c_dev->buf, sizeof(c_dev->buf));
if (strncmp(ACK_SYNC, c_dev->buf, sizeof(ACK_SYNC)) == 0) {
/** Add channel device instance into UART connection list */
add_uart_channel_dev_connection_list(c_dev);
LOG_WRITE("Lifecycle manager: connected\n");
} else {
ret = -1;
LOG_PRINTF("Device in the (%s): failed to connect\n", c->conf.identifier);
}
}
if (ret < 0)
c_dev->polling = false;
c_dev->listening = false;
sem_post(&c_dev->dev_sem);
return NULL;
}
void *poll_and_dispatch_uart_channel_events(void *arg)
{
struct channel_dev *c_dev = (struct channel_dev *)arg;
ssize_t num, ret;
struct uart_channel *c;
c = c_dev->channel;
sem_wait(&c_dev->dev_sem);
LOG_PRINTF("UART polling fd=%d...\n", get_uart_dev_fd(c_dev->uart_device));
while (c_dev->polling) {
memset(c_dev->buf, 0, sizeof(c_dev->buf));
num = receive_message_by_uart(c_dev->uart_device, (void *)c_dev->buf,
sizeof(c_dev->buf));
/**
* Resend message if resend_time is set.
*/
if (num == 0) {
if (c_dev->resend_time > 1) {
usleep(LISTEN_INTERVAL + SECOND_TO_US);
LOG_PRINTF("Resend (%s) to (%s)\n", c_dev->resend_buf, c_dev->name);
ret = send_message_by_uart(c_dev->uart_device, (void *)c_dev->resend_buf,
strlen(c_dev->resend_buf));
if (ret < 0)
LOG_WRITE("Send poweroff message to user VM fail\n");
c_dev->resend_time--;
} else if (c_dev->resend_time == 1) {
memcpy(c_dev->buf, ACK_TIMEOUT, strlen(ACK_TIMEOUT));
num = strlen(ACK_TIMEOUT);
} else {
/* No action if resend_time is 0 */
}
}
if (num > 0) {
parse_channel_dev_id(c_dev);
c->data_handler((const char *)c_dev->buf, get_uart_dev_fd(c_dev->uart_device));
}
}
LOG_PRINTF("Lifecycle manager stops to poll device:%s\n",
get_uart_dev_path(c_dev->uart_device));
return NULL;
}
struct channel_dev *find_uart_channel_dev(struct uart_channel *c, int fd)
{
struct channel_dev *c_dev = NULL;
LIST_FOREACH(c_dev, &c->tty_conn_head, list) {
if (get_uart_dev_fd(c_dev->uart_device) == fd)
break;
}
return c_dev;
}
struct channel_dev *find_uart_channel_dev_by_name(struct uart_channel *c, char *name)
{
struct channel_dev *c_dev = NULL;
LIST_FOREACH(c_dev, &c->tty_conn_head, list) {
if (strncmp(name, c_dev->name, sizeof(c_dev->name)) == 0)
break;
}
return c_dev;
}
/**
* @brief Set message polling loop flag as flase and remove channel device instance from
* the connection list
*
* @param c_dev point to uart channel device instance
* @param c point to uart channel instance
*/
void disconnect_uart_channel_dev(struct channel_dev *c_dev, struct uart_channel *c)
{
c_dev->listening = false;
c_dev->polling = false;
pthread_mutex_lock(&c->tty_conn_list_lock);
LIST_REMOVE(c_dev, list);
pthread_mutex_unlock(&c->tty_conn_list_lock);
}
/**
* @brief Traverse uart channel open list to set listening loop flag and polling loop flag
* to false for each channel device which is in listening state.
*
* @param c point to uart channel instance
*/
void stop_listen_uart_channel_dev(struct uart_channel *c)
{
struct channel_dev *c_dev;
LIST_FOREACH(c_dev, &c->tty_open_head, open_list) {
if (c_dev->listening) {
LOG_PRINTF("Stop to listen uart device (%s)\n",
get_uart_dev_path(c_dev->uart_device));
c_dev->listening = false;
c_dev->polling = false;
sem_post(&c_dev->dev_sem);
}
}
}
void start_uart_channel_dev_resend(struct channel_dev *c_dev, char *resend_buf, unsigned int resend_time)
{
if (resend_time < MIN_RESEND_TIME)
resend_time = MIN_RESEND_TIME;
strncpy(c_dev->resend_buf, resend_buf, CHANNEL_DEV_BUF_LEN - 1);
c_dev->resend_time = resend_time + 1;
}
void start_all_uart_channel_dev_resend(struct uart_channel *c, char *msg, unsigned int resend_time)
{
struct channel_dev *c_dev;
/* Enable resend for all connected uart channel devices */
pthread_mutex_lock(&c->tty_conn_list_lock);
LIST_FOREACH(c_dev, &c->tty_conn_head, list) {
start_uart_channel_dev_resend(c_dev, msg, resend_time);
}
pthread_mutex_unlock(&c->tty_conn_list_lock);
}
void stop_uart_channel_dev_resend(struct channel_dev *c_dev)
{
if (c_dev->resend_time == 1U)
LOG_PRINTF("Timeout of receiving ACK message from (%s)\n", c_dev->name);
c_dev->resend_time = 0U;
memset(c_dev->resend_buf, 0x0, CHANNEL_DEV_BUF_LEN);
}
/**
* @brief Send message to each connected uart channel device
*
* @param c uart channel instance
* @param msg pointer which points to the message to be sent
*/
void notify_all_connected_uart_channel_dev(struct uart_channel *c, char *msg)
{
struct channel_dev *c_dev;
/* Send message to all tty connected devices*/
pthread_mutex_lock(&c->tty_conn_list_lock);
LIST_FOREACH(c_dev, &c->tty_conn_head, list) {
LOG_PRINTF("Send (%s) to (%s)\n", msg, c_dev->name);
(void) send_message_by_uart(c_dev->uart_device, msg, strlen(msg));
}
pthread_mutex_unlock(&c->tty_conn_list_lock);
}
bool is_uart_channel_connection_list_empty(struct uart_channel *c)
{
bool ret = false;
pthread_mutex_lock(&c->tty_conn_list_lock);
if (LIST_EMPTY(&c->tty_conn_head))
ret = true;
pthread_mutex_unlock(&c->tty_conn_list_lock);
return ret;
}
struct channel_dev *create_uart_channel_dev(struct uart_channel *c, char *path, data_handler_f *fn)
{
struct uart_dev *dev;
struct channel_dev *c_dev;
if (c == NULL || path == NULL || fn == NULL)
return NULL;
c->data_handler = fn;
dev = init_uart_dev(path);
if (dev == NULL) {
LOG_PRINTF("Failed to initialize UART device %s\n", path);
return NULL;
}
c_dev = calloc(1, sizeof(*c_dev));
if (!c_dev) {
LOG_PRINTF("%s: Failed to allocate memory for UART channel device\n", __func__);
deinit_uart_dev(dev);
return NULL;
}
memset(c_dev, 0x0, sizeof(*c_dev));
c_dev->uart_device = dev;
c_dev->channel = c;
c_dev->listening = true;
c_dev->polling = true;
sem_init(&c_dev->dev_sem, 0, 0);
/** Add channel device instance into open list */
pthread_mutex_lock(&c->tty_conn_list_lock);
LIST_INSERT_HEAD(&c->tty_open_head, c_dev, open_list);
pthread_mutex_unlock(&c->tty_conn_list_lock);
return c_dev;
}
static void destroy_uart_channel_devs(struct uart_channel *c)
{
struct channel_dev *c_dev;
LIST_FOREACH(c_dev, &c->tty_open_head, open_list) {
pthread_mutex_lock(&c->tty_conn_list_lock);
LIST_REMOVE(c_dev, open_list);
pthread_mutex_unlock(&c->tty_conn_list_lock);
deinit_uart_dev(c_dev->uart_device);
free(c_dev);
}
}
void wait_uart_channel_devs_threads(struct uart_channel *c)
{
struct channel_dev *c_dev;
LIST_FOREACH(c_dev, &c->tty_open_head, open_list) {
pthread_join(c_dev->listen_thread, NULL);
pthread_join(c_dev->pool_thread, NULL);
}
}
struct uart_channel *init_uart_channel(char *id)
{
struct uart_channel *c;
if (id == NULL) {
LOG_PRINTF("%s:invlid parameter\n", __func__);
return NULL;
}
c = calloc(1, sizeof(*c));
if (!c) {
LOG_PRINTF("%s: Failed to allocate memory for UART channel\n", __func__);
return NULL;
}
c->data_handler = NULL;
LIST_INIT(&c->tty_conn_head);
LIST_INIT(&c->tty_open_head);
pthread_mutex_init(&c->tty_conn_list_lock, NULL);
memcpy(c->conf.identifier, id, strlen(id));
return c;
}
void deinit_uart_channel(struct uart_channel *c)
{
if (c != NULL) {
destroy_uart_channel_devs(c);
free(c);
}
}