vservices: serial: Add the serial protocol and drivers

Adds the virtual services serial protocol and tty drivers
for virtual serial and console.

Change-Id: I2dd86a677406d6b74b0e02e5fda720eaf9edcd62
Signed-off-by: Carl van Schaik <carl@cog.systems>
Git-commit: 29f5766e09adb6064b01bf20e76c117bcfa94e41
Git-repo: https://github.com/CogSystems/linux-msm/commits/msm-4.9-hyp
Signed-off-by: Murali Nalajala <mnalajal@codeaurora.org>
Signed-off-by: Prakruthi Deepak Heragu <pheragu@codeaurora.org>
This commit is contained in:
Carl van Schaik 2018-07-06 22:22:42 +10:00 committed by Prakruthi Deepak Heragu
parent 05623dfc72
commit 17607ebf91
16 changed files with 3462 additions and 0 deletions

View File

@ -587,3 +587,41 @@ config OKL4_PIPE
Virtual pipe driver for the OKL4 Microvisor. This driver allows
OKL4 Microvisor pipes to be exposed directly to user level as
character devices.
config VSERVICES_SERIAL
tristate
config VSERVICES_SERIAL_SERVER
tristate "Virtual Services serial server"
depends on VSERVICES_SUPPORT && VSERVICES_SERVER
select VSERVICES_SERIAL
select VSERVICES_PROTOCOL_SERIAL_SERVER
default y
help
Select this option if you want support for server side Virtual
Services serial. A virtual serial service behaves similarly to
a UNIX pseudo terminal (pty), and does not require any physical
serial hardware. Virtual serial devices are typically called
/dev/ttyVS0, /dev/ttyVS1, etc.
config VSERVICES_SERIAL_CLIENT
tristate "Virtual Services serial client"
depends on VSERVICES_SUPPORT && VSERVICES_CLIENT
select VSERVICES_SERIAL
select VSERVICES_PROTOCOL_SERIAL_CLIENT
default y
help
Select this option if you want support for client side Virtual
Services serial. A virtual serial service behaves similarly to
a UNIX pseudo terminal (pty), and does not require any physical
serial hardware. Virtual serial devices are typically called
/dev/ttyVS0, /dev/ttyVS1, etc.
config VSERVICES_VTTY_COUNT
int "Maximum number of Virtual Services serial devices"
depends on VSERVICES_SERIAL
range 0 256
default "8"
help
The maximum number of Virtual Services serial devices to support.
This limit applies to both the client and server.

View File

@ -65,3 +65,9 @@ obj-$(CONFIG_ADI) += adi.o
obj-$(CONFIG_DIAG_CHAR) += diag/
obj-$(CONFIG_OKL4_PIPE) += okl4_pipe.o
CFLAGS_okl4_pipe.o += -Werror
obj-$(CONFIG_VSERVICES_SERIAL) += vservices_serial.o
CFLAGS_vservices_serial.o += -Werror
obj-$(CONFIG_VSERVICES_SERIAL_CLIENT) += vs_serial_client.o
CFLAGS_vs_serial_client.o += -Werror
obj-$(CONFIG_VSERVICES_SERIAL_SERVER) += vs_serial_server.o
CFLAGS_vs_serial_server.o += -Werror

View File

@ -0,0 +1,132 @@
/*
* drivers/char/vs_serial_client.c
*
* Copyright (c) 2012-2018 General Dynamics
* Copyright (c) 2014 Open Kernel Labs, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Serial vService client driver
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <vservices/transport.h>
#include <vservices/types.h>
#include <vservices/buffer.h>
#include <vservices/service.h>
#include <vservices/protocol/serial/common.h>
#include <vservices/protocol/serial/types.h>
#include <vservices/protocol/serial/client.h>
#include "vs_serial_common.h"
#define client_state_to_port(state) \
container_of(state, struct vtty_port, u.vs_client)
static struct vs_mbuf *vs_serial_client_alloc_msg_buf(struct vtty_port *port,
struct vs_pbuf *pbuf, gfp_t gfp_flags)
{
return vs_client_serial_serial_alloc_msg(&port->u.vs_client, pbuf,
gfp_flags);
}
static void vs_serial_client_free_msg_buf(struct vtty_port *port,
struct vs_mbuf *mbuf, struct vs_pbuf *pbuf)
{
vs_client_serial_serial_free_msg(&port->u.vs_client, pbuf, mbuf);
}
static int vs_serial_client_send_msg_buf(struct vtty_port *port,
struct vs_mbuf *mbuf, struct vs_pbuf *pbuf)
{
return vs_client_serial_serial_send_msg(&port->u.vs_client, *pbuf,
mbuf);
}
static bool vs_serial_client_is_vservices_running(struct vtty_port *port)
{
return VSERVICE_BASE_STATE_IS_RUNNING(port->u.vs_client.state.base);
}
static struct vtty_port_ops client_port_ops = {
.alloc_msg_buf = vs_serial_client_alloc_msg_buf,
.free_msg_buf = vs_serial_client_free_msg_buf,
.send_msg_buf = vs_serial_client_send_msg_buf,
.is_running = vs_serial_client_is_vservices_running,
};
static struct vs_client_serial_state *
vs_serial_client_alloc(struct vs_service_device *service)
{
struct vtty_port *port;
port = vs_serial_alloc_port(service, &client_port_ops);
if (!port)
return NULL;
dev_set_drvdata(&service->dev, port);
return &port->u.vs_client;
}
static void vs_serial_client_release(struct vs_client_serial_state *_state)
{
vs_serial_release(client_state_to_port(_state));
}
static void vs_serial_client_closed(struct vs_client_serial_state *_state)
{
vs_serial_reset(client_state_to_port(_state));
}
static void vs_serial_client_opened(struct vs_client_serial_state *_state)
{
struct vtty_port *port = client_state_to_port(_state);
dev_dbg(&port->service->dev, "ack_open\n");
port->max_transfer_size = _state->packet_size;
}
static int
vs_serial_client_handle_message(struct vs_client_serial_state *_state,
struct vs_pbuf data, struct vs_mbuf *mbuf)
{
return vs_serial_handle_message(client_state_to_port(_state), mbuf,
&data);
}
static struct vs_client_serial vs_client_serial_driver = {
.rx_atomic = true,
.alloc = vs_serial_client_alloc,
.release = vs_serial_client_release,
.closed = vs_serial_client_closed,
.opened = vs_serial_client_opened,
.serial = {
.msg_msg = vs_serial_client_handle_message,
},
};
static int __init vs_serial_client_init(void)
{
return vservice_serial_client_register(&vs_client_serial_driver,
"vserial");
}
static void __exit vs_serial_client_exit(void)
{
vservice_serial_client_unregister(&vs_client_serial_driver);
}
module_init(vs_serial_client_init);
module_exit(vs_serial_client_exit);
MODULE_DESCRIPTION("OKL4 Virtual Services Serial Client Driver");
MODULE_AUTHOR("Open Kernel Labs, Inc");

View File

@ -0,0 +1,91 @@
/*
* drivers/char/vs_serial_common.h
*
* Copyright (c) 2012-2018 General Dynamics
* Copyright (c) 2014 Open Kernel Labs, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#ifndef _VS_SERIAL_COMMON_H
#define _VS_SERIAL_COMMON_H
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/mutex.h>
#include <linux/completion.h>
#include <linux/console.h>
#include <vservices/protocol/serial/common.h>
#include <vservices/protocol/serial/types.h>
#include <vservices/protocol/serial/server.h>
#include <vservices/protocol/serial/client.h>
#define OUTBUFFER_SIZE 1024
#define vtty_list_last_entry(ptr, type, member) \
list_entry((ptr)->prev, type, member)
struct vtty_port;
struct vs_service_device;
struct vtty_port_ops {
struct vs_mbuf *(*alloc_msg_buf)(struct vtty_port *port,
struct vs_pbuf *pbuf, gfp_t gfp_flags);
void (*free_msg_buf)(struct vtty_port *port,
struct vs_mbuf *mbuf, struct vs_pbuf *pbuf);
int (*send_msg_buf)(struct vtty_port *port,
struct vs_mbuf *mbuf, struct vs_pbuf *pbuf);
bool (*is_running)(struct vtty_port *port);
};
struct vtty_port {
union {
struct vs_client_serial_state vs_client;
struct vs_server_serial_state vs_server;
} u;
struct vs_service_device *service;
int port_num;
struct tty_driver *vtty_driver;
struct vtty_port_ops ops;
/* output data */
bool doing_release;
int max_transfer_size;
/* Tracks if tty layer can receive data from driver */
bool tty_canrecv;
/*
* List of pending incoming buffers from the vServices stack. If we
* receive a buffer, but cannot write it to the tty layer then we
* queue it on this list to handle later. in_lock protects access to
* the pending_in_packets list and the tty_canrecv field.
*/
struct list_head pending_in_packets;
spinlock_t in_lock;
#ifdef CONFIG_OKL4_VTTY_CONSOLE
struct console console;
#endif
struct tty_port port;
};
extern struct vtty_port *
vs_serial_alloc_port(struct vs_service_device *service,
struct vtty_port_ops *port_ops);
extern void vs_serial_release(struct vtty_port *port);
extern void vs_serial_reset(struct vtty_port *port);
extern int vs_serial_handle_message(struct vtty_port *port,
struct vs_mbuf *mbuf, struct vs_pbuf *pbuf);
#endif /* _VS_SERIAL_COMMON_H */

View File

@ -0,0 +1,152 @@
/*
* drivers/char/vs_serial_server.c
*
* Copyright (c) 2012-2018 General Dynamics
* Copyright (c) 2014 Open Kernel Labs, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Serial vService server driver
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <vservices/transport.h>
#include <vservices/types.h>
#include <vservices/buffer.h>
#include <vservices/service.h>
#include <vservices/protocol/serial/common.h>
#include <vservices/protocol/serial/types.h>
#include <vservices/protocol/serial/server.h>
#include "vs_serial_common.h"
#define server_state_to_port(state) \
container_of(state, struct vtty_port, u.vs_server)
static struct vs_mbuf *vs_serial_server_alloc_msg_buf(struct vtty_port *port,
struct vs_pbuf *pbuf, gfp_t gfp_flags)
{
return vs_server_serial_serial_alloc_msg(&port->u.vs_server, pbuf,
gfp_flags);
}
static void vs_serial_server_free_msg_buf(struct vtty_port *port,
struct vs_mbuf *mbuf, struct vs_pbuf *pbuf)
{
vs_server_serial_serial_free_msg(&port->u.vs_server, pbuf, mbuf);
}
static int vs_serial_server_send_msg_buf(struct vtty_port *port,
struct vs_mbuf *mbuf, struct vs_pbuf *pbuf)
{
return vs_server_serial_serial_send_msg(&port->u.vs_server, *pbuf, mbuf);
}
static bool vs_serial_server_is_vservices_running(struct vtty_port *port)
{
return VSERVICE_BASE_STATE_IS_RUNNING(port->u.vs_server.state.base);
}
static struct vtty_port_ops server_port_ops = {
.alloc_msg_buf = vs_serial_server_alloc_msg_buf,
.free_msg_buf = vs_serial_server_free_msg_buf,
.send_msg_buf = vs_serial_server_send_msg_buf,
.is_running = vs_serial_server_is_vservices_running,
};
static struct vs_server_serial_state *
vs_serial_server_alloc(struct vs_service_device *service)
{
struct vtty_port *port;
port = vs_serial_alloc_port(service, &server_port_ops);
if (!port)
return NULL;
dev_set_drvdata(&service->dev, port);
return &port->u.vs_server;
}
static void vs_serial_server_release(struct vs_server_serial_state *_state)
{
vs_serial_release(server_state_to_port(_state));
}
static void vs_serial_server_closed(struct vs_server_serial_state *_state)
{
vs_serial_reset(server_state_to_port(_state));
}
static int
vs_serial_server_handle_message(struct vs_server_serial_state *_state,
struct vs_pbuf data, struct vs_mbuf *mbuf)
{
return vs_serial_handle_message(server_state_to_port(_state), mbuf,
&data);
}
static vs_server_response_type_t
vs_serial_server_req_open(struct vs_server_serial_state *_state)
{
struct vtty_port *port = server_state_to_port(_state);
dev_dbg(&port->service->dev, "req_open\n");
/* FIXME: Jira ticket SDK-3521 - ryanm. */
port->max_transfer_size = vs_service_max_mbuf_size(port->service) - 8;
_state->packet_size = port->max_transfer_size;
return VS_SERVER_RESP_SUCCESS;
}
static vs_server_response_type_t
vs_serial_server_req_close(struct vs_server_serial_state *_state)
{
struct vtty_port *port = server_state_to_port(_state);
dev_dbg(&port->service->dev, "req_close\n");
return VS_SERVER_RESP_SUCCESS;
}
static struct vs_server_serial vs_server_serial_driver = {
.rx_atomic = true,
.alloc = vs_serial_server_alloc,
.release = vs_serial_server_release,
.closed = vs_serial_server_closed,
.open = vs_serial_server_req_open,
.close = vs_serial_server_req_close,
.serial = {
.msg_msg = vs_serial_server_handle_message,
},
/* Large default quota for batching data messages */
.in_quota_best = 16,
.out_quota_best = 16,
};
static int __init vs_serial_server_init(void)
{
return vservice_serial_server_register(&vs_server_serial_driver,
"vserial");
}
static void __exit vs_serial_server_exit(void)
{
vservice_serial_server_unregister(&vs_server_serial_driver);
}
module_init(vs_serial_server_init);
module_exit(vs_serial_server_exit);
MODULE_DESCRIPTION("OKL4 Virtual Services Serial Server Driver");
MODULE_AUTHOR("Open Kernel Labs, Inc");

View File

@ -0,0 +1,634 @@
/*
* drivers/char/vservice_serial.c
*
* Copyright (c) 2012-2018 General Dynamics
* Copyright (c) 2014 Open Kernel Labs, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* serial vservice client driver
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/bitmap.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/console.h>
#include <linux/version.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/mutex.h>
#include <vservices/transport.h>
#include <vservices/types.h>
#include <vservices/buffer.h>
#include <vservices/protocol/serial/common.h>
#include <vservices/protocol/serial/types.h>
#include <vservices/protocol/serial/server.h>
#include <vservices/service.h>
#include <vservices/wait.h>
#include "vs_serial_common.h"
struct vtty_in_packet {
struct vs_pbuf pbuf;
size_t offset;
};
static int max_ttys = CONFIG_VSERVICES_VTTY_COUNT;
static unsigned long *alloced_ttys;
module_param(max_ttys, int, S_IRUGO);
static struct tty_driver *vtty_driver;
static DEFINE_MUTEX(tty_bitmap_lock);
static struct vtty_port *dev_to_port(struct device *dev)
{
struct vs_service_device *service = to_vs_service_device(dev);
#if defined(CONFIG_VSERVICES_SERIAL_SERVER) || \
defined(CONFIG_VSERIVCES_SERIAL_SERVER_MODULE)
if (service->is_server) {
struct vs_server_serial_state *server = dev_get_drvdata(dev);
return container_of(server, struct vtty_port, u.vs_server);
}
#endif
#if defined(CONFIG_VSERVICES_SERIAL_CLIENT) || \
defined(CONFIG_VSERIVCES_SERIAL_CLIENT_MODULE)
if (!service->is_server) {
struct vs_client_serial_state *client = dev_get_drvdata(dev);
return container_of(client, struct vtty_port, u.vs_client);
}
#endif
/* should never get here */
WARN_ON(1);
return NULL;
}
static struct vtty_port *port_from_tty(struct tty_struct *tty)
{
return dev_to_port(tty->dev->parent);
}
static int vtty_install(struct tty_driver *driver, struct tty_struct *tty)
{
struct vtty_port *port;
if (tty->index < 0 || !test_bit(tty->index, alloced_ttys))
return -ENXIO;
port = port_from_tty(tty);
if (!port)
return -ENXIO;
tty->driver_data = port;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)
if (tty->port)
tty->port->low_latency = 0;
#else
tty->low_latency = 0;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
tty_port_install(&port->port, driver, tty);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
tty->port = &port->port;
tty_standard_install(driver, tty);
#else
tty->port = &port->port;
if (tty_init_termios(tty) != 0)
return -ENOMEM;
tty_driver_kref_get(driver);
tty->count++;
driver->ttys[tty->index] = tty;
#endif
return 0;
}
static int vtty_open(struct tty_struct *tty, struct file *file)
{
struct vtty_port *port = tty->driver_data;
return tty_port_open(&port->port, tty, file);
}
static void vtty_close(struct tty_struct *tty, struct file *file)
{
struct vtty_port *port = tty->driver_data;
if (port)
tty_port_close(&port->port, tty, file);
}
static void vtty_shutdown(struct tty_port *port)
{
struct vtty_port *vtty_port =
container_of(port, struct vtty_port, port);
if (vtty_port->doing_release)
kfree(port);
}
static int vtty_write_room(struct tty_struct *tty)
{
struct vtty_port *port = tty->driver_data;
return vs_service_send_mbufs_available(port->service) *
port->max_transfer_size;
}
static struct vs_mbuf *vserial_alloc_send_buffer(struct vtty_port *port,
const unsigned char *buf, size_t size, struct vs_pbuf *pbuf,
gfp_t gfp_flags)
{
struct vs_mbuf *mbuf;
ssize_t ret;
mbuf = port->ops.alloc_msg_buf(port, pbuf, gfp_flags);
if (IS_ERR(mbuf)) {
ret = PTR_ERR(mbuf);
goto fail;
}
ret = vs_pbuf_resize(pbuf, size);
if (ret < (ssize_t)size)
goto fail_free_buf;
ret = vs_pbuf_copyin(pbuf, 0, buf, size);
if (ret < (ssize_t)size)
goto fail_free_buf;
return mbuf;
fail_free_buf:
port->ops.free_msg_buf(port, mbuf, pbuf);
fail:
return ERR_PTR(ret);
}
static int vtty_write(struct tty_struct *tty, const unsigned char *buf,
int count)
{
struct vtty_port *port;
size_t sent_bytes = 0, size;
struct vs_mbuf *mbuf;
struct vs_pbuf pbuf;
int err;
if (WARN_ON(!tty || !buf))
return -EINVAL;
port = tty->driver_data;
if (!port->ops.is_running(port)) {
dev_dbg(&port->service->dev, "tty is not running!");
return 0;
}
/*
* We need to break our message up into chunks of
* port->max_transfer_size.
*/
dev_dbg(&port->service->dev, "Writing %d bytes\n", count);
while (sent_bytes < count) {
size = min_t(size_t, count - sent_bytes,
port->max_transfer_size);
/*
* Passing &port->u.vs_client here works for both the client
* and the server since vs_client and vs_server are in the
* same union, and therefore have the same address.
*/
mbuf = vs_service_waiting_alloc(&port->u.vs_client,
vserial_alloc_send_buffer(port,
buf + sent_bytes, size, &pbuf, GFP_KERNEL));
if (IS_ERR(mbuf)) {
dev_err(&port->service->dev,
"Failed to alloc mbuf of %zu bytes: %ld - resetting service\n",
size, PTR_ERR(mbuf));
vs_service_reset(port->service, port->service);
return -EIO;
}
vs_service_state_lock(port->service);
err = port->ops.send_msg_buf(port, mbuf, &pbuf);
vs_service_state_unlock(port->service);
if (err) {
port->ops.free_msg_buf(port, mbuf, &pbuf);
dev_err(&port->service->dev,
"send failed: %d - resetting service\n",
err);
vs_service_reset(port->service, port->service);
return -EIO;
}
dev_dbg(&port->service->dev, "Sent %zu bytes (%zu/%d)\n",
size, sent_bytes + size, count);
sent_bytes += size;
}
dev_dbg(&port->service->dev, "Write complete - sent %zu/%d bytes\n",
sent_bytes, count);
return sent_bytes;
}
static int vtty_put_char(struct tty_struct *tty, unsigned char ch)
{
return vtty_write(tty, &ch, 1);
}
static size_t vs_serial_send_pbuf_to_tty(struct vtty_port *port,
struct vs_pbuf *pbuf, size_t offset)
{
struct tty_struct *tty = tty_port_tty_get(&port->port);
size_t space, size;
lockdep_assert_held(&port->in_lock);
size = vs_pbuf_size(pbuf) - offset;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)
space = tty_buffer_request_room(tty->port, size);
#else
space = tty_buffer_request_room(tty, size);
#endif
if (space) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)
tty_insert_flip_string(tty->port, pbuf->data + offset, space);
tty_flip_buffer_push(tty->port);
#else
tty_insert_flip_string(tty, pbuf->data + offset, space);
tty_flip_buffer_push(tty);
#endif
}
tty_kref_put(tty);
/* Return the number of bytes written */
return space;
}
static void vtty_throttle(struct tty_struct *tty)
{
struct vtty_port *port = tty->driver_data;
dev_dbg(&port->service->dev, "throttle\n");
spin_lock_bh(&port->in_lock);
port->tty_canrecv = false;
spin_unlock_bh(&port->in_lock);
}
static void vtty_unthrottle(struct tty_struct *tty)
{
struct vtty_port *port = tty->driver_data;
struct vtty_in_packet *packet;
struct vs_mbuf *mbuf;
size_t sent;
dev_dbg(&port->service->dev, "unthrottle\n");
spin_lock_bh(&port->in_lock);
while (!list_empty(&port->pending_in_packets)) {
mbuf = list_first_entry(&port->pending_in_packets,
struct vs_mbuf, queue);
packet = mbuf->priv;
sent = vs_serial_send_pbuf_to_tty(port, &packet->pbuf,
packet->offset);
packet->offset += sent;
if (packet->offset < vs_pbuf_size(&packet->pbuf)) {
/*
* Only wrote part of the buffer. This means that we
* still have pending data that cannot be written to
* the tty at this time. The tty layer will rethrottle
* and this function will be called again when the tty
* layer is next able to handle data and we can write
* the remainder of the buffer.
*/
dev_dbg(&port->service->dev,
"unthrottle: Only wrote %zu (%zu/%zu) bytes\n",
sent, packet->offset,
vs_pbuf_size(&packet->pbuf));
break;
}
dev_dbg(&port->service->dev,
"unthrottle: wrote %zu (%zu/%zu) bytes\n",
sent, packet->offset,
vs_pbuf_size(&packet->pbuf));
/* Wrote the whole buffer - free it */
list_del(&mbuf->queue);
port->ops.free_msg_buf(port, mbuf, &packet->pbuf);
kfree(packet);
}
port->tty_canrecv = true;
spin_unlock_bh(&port->in_lock);
}
static struct tty_port_operations vtty_port_ops = {
.shutdown = vtty_shutdown,
};
static struct tty_operations vtty_ops = {
.install = vtty_install,
.open = vtty_open,
.close = vtty_close,
.write = vtty_write,
.write_room = vtty_write_room,
.put_char = vtty_put_char,
.throttle = vtty_throttle,
.unthrottle = vtty_unthrottle
};
static int vs_serial_queue_incoming_packet(struct vtty_port *port,
struct vs_mbuf *mbuf, struct vs_pbuf *pbuf, size_t offset)
{
struct vtty_in_packet *packet;
lockdep_assert_held(&port->in_lock);
packet = kzalloc(sizeof(*packet), GFP_ATOMIC);
if (!packet) {
/*
* Uh oh, we are seriously out of memory. The incoming data
* will be lost.
*/
return -ENOMEM;
}
dev_dbg(&port->service->dev, "Queuing packet %zu bytes, offset %zu\n",
vs_pbuf_size(pbuf), offset);
mbuf->priv = packet;
memcpy(&packet->pbuf, pbuf, sizeof(*pbuf));
packet->offset = offset;
list_add_tail(&mbuf->queue, &port->pending_in_packets);
return 0;
}
int vs_serial_handle_message(struct vtty_port *port, struct vs_mbuf *mbuf,
struct vs_pbuf *pbuf)
{
struct tty_struct *tty = tty_port_tty_get(&port->port);
bool queue_packet = false;
size_t sent = 0;
int err;
if (!tty) {
dev_dbg(&port->service->dev,
"tty not open. Dropping %zu chars\n",
pbuf->size);
port->ops.free_msg_buf(port, mbuf, pbuf);
return 0;
}
dev_dbg(&port->service->dev, "Incoming message - len = %zu\n",
pbuf->size);
spin_lock(&port->in_lock);
if (!port->tty_canrecv || !list_empty(&port->pending_in_packets)) {
/*
* We cannot send to the tty right now, either because we are
* being throttled or because we still have pending data
* to write out to the tty. Queue the buffer up so we can
* write it later.
*/
dev_dbg(&port->service->dev,
"Cannot send (canrecv = %d, queued = %d) - queuing message\n",
port->tty_canrecv,
!list_empty(&port->pending_in_packets));
queue_packet = true;
} else {
sent = vs_serial_send_pbuf_to_tty(port, pbuf, 0);
if (sent < vs_pbuf_size(pbuf)) {
/*
* Only wrote part of the buffer to the tty. Queue
* the buffer to write the rest.
*/
dev_dbg(&port->service->dev,
"Sent %zu/%zu bytes to tty - queueing rest\n",
sent, vs_pbuf_size(pbuf));
queue_packet = true;
}
}
if (queue_packet) {
/*
* Queue the incoming data up. If we are not already throttled,
* the tty layer will do so now since it has no room in its
* buffers.
*/
err = vs_serial_queue_incoming_packet(port, mbuf, pbuf, sent);
if (err) {
dev_err(&port->service->dev,
"Failed to queue packet - dropping chars\n");
port->ops.free_msg_buf(port, mbuf, pbuf);
}
} else {
port->ops.free_msg_buf(port, mbuf, pbuf);
}
spin_unlock(&port->in_lock);
tty_kref_put(tty);
return 0;
}
EXPORT_SYMBOL_GPL(vs_serial_handle_message);
#ifdef CONFIG_OKL4_VTTY_CONSOLE
static int vconsole_setup(struct console *co, char *options)
{
if (co->index < 0 || co->index >= max_ttys)
co->index = 0;
pr_info("OKL4 virtual console init\n");
return 0;
}
static void vconsole_write(struct console *co, const char *p, unsigned count)
{
}
static struct tty_driver *vconsole_device(struct console *co, int *index)
{
*index = co->index;
return vtty_driver;
}
#endif /* CONFIG_OKL4_VTTY_CONSOLE */
static void vs_serial_free_buffers(struct vtty_port *port)
{
struct vtty_in_packet *packet;
struct vs_mbuf *mbuf;
/* Free the list of incoming buffers */
spin_lock_bh(&port->in_lock);
while (!list_empty(&port->pending_in_packets)) {
mbuf = list_first_entry(&port->pending_in_packets,
struct vs_mbuf, queue);
packet = mbuf->priv;
list_del(&mbuf->queue);
port->ops.free_msg_buf(port, mbuf, &packet->pbuf);
kfree(packet);
}
spin_unlock_bh(&port->in_lock);
}
/** vservices callbacks **/
struct vtty_port *vs_serial_alloc_port(struct vs_service_device *service,
struct vtty_port_ops *port_ops)
{
struct vtty_port *port;
int port_num;
mutex_lock(&tty_bitmap_lock);
port_num = find_first_zero_bit(alloced_ttys, max_ttys);
if (port_num >= max_ttys) {
mutex_unlock(&tty_bitmap_lock);
return NULL;
}
port = kzalloc(sizeof(struct vtty_port), GFP_KERNEL);
if (!port) {
mutex_unlock(&tty_bitmap_lock);
return NULL;
}
port->service = service;
port->ops = *port_ops;
port->tty_canrecv = true;
port->port_num = port_num;
INIT_LIST_HEAD(&port->pending_in_packets);
spin_lock_init(&port->in_lock);
#ifdef CONFIG_OKL4_VTTY_CONSOLE
/* Set up and register the port's console device */
strlcpy(port->console.name, "vconvs", sizeof(port->console.name));
port->console.write = vconsole_write;
port->console.flags = CON_PRINTBUFFER;
port->console.device = vconsole_device;
port->console.setup = vconsole_setup;
port->console.index = port_num;
register_console(&port->console);
#endif
port->vtty_driver = vtty_driver;
tty_port_init(&port->port);
port->port.ops = &vtty_port_ops;
tty_register_device(vtty_driver, port_num, &service->dev);
bitmap_set(alloced_ttys, port_num, 1);
mutex_unlock(&tty_bitmap_lock);
return port;
}
EXPORT_SYMBOL(vs_serial_alloc_port);
void vs_serial_release(struct vtty_port *port)
{
dev_dbg(&port->service->dev, "Release\n");
#ifdef CONFIG_OKL4_VTTY_CONSOLE
unregister_console(&port->console);
#endif
mutex_lock(&tty_bitmap_lock);
bitmap_clear(alloced_ttys, port->port_num, 1);
mutex_unlock(&tty_bitmap_lock);
if (port->port.tty) {
tty_vhangup(port->port.tty);
tty_kref_put(port->port.tty);
}
vs_serial_free_buffers(port);
port->doing_release = true;
tty_unregister_device(vtty_driver, port->port_num);
}
EXPORT_SYMBOL_GPL(vs_serial_release);
void vs_serial_reset(struct vtty_port *port)
{
/* Free list of in and out mbufs. */
vs_serial_free_buffers(port);
}
EXPORT_SYMBOL_GPL(vs_serial_reset);
static int __init vs_serial_init(void)
{
int err;
if (max_ttys == 0)
return -EINVAL;
alloced_ttys = kzalloc(sizeof(unsigned long) * BITS_TO_LONGS(max_ttys),
GFP_KERNEL);
if (!alloced_ttys) {
err = -ENOMEM;
goto fail_alloc_ttys;
}
/* Set up the tty driver. */
vtty_driver = alloc_tty_driver(max_ttys);
if (!vtty_driver) {
err = -ENOMEM;
goto fail_alloc_tty_driver;
}
vtty_driver->owner = THIS_MODULE;
vtty_driver->driver_name = "okl4-vservices-serial";
vtty_driver->name = "ttyVS";
vtty_driver->type = TTY_DRIVER_TYPE_SERIAL;
vtty_driver->subtype = SERIAL_TYPE_NORMAL;
vtty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
vtty_driver->init_termios = tty_std_termios;
vtty_driver->num = max_ttys;
/* These flags don't really matter; just use sensible defaults. */
vtty_driver->init_termios.c_cflag =
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
vtty_driver->init_termios.c_ispeed = 9600;
vtty_driver->init_termios.c_ospeed = 9600;
tty_set_operations(vtty_driver, &vtty_ops);
err = tty_register_driver(vtty_driver);
if (err)
goto fail_tty_driver_register;
return 0;
fail_tty_driver_register:
put_tty_driver(vtty_driver);
fail_alloc_tty_driver:
kfree(alloced_ttys);
fail_alloc_ttys:
return err;
}
static void __exit vs_serial_exit(void)
{
tty_unregister_driver(vtty_driver);
put_tty_driver(vtty_driver);
}
module_init(vs_serial_init);
module_exit(vs_serial_exit);
MODULE_DESCRIPTION("OKL4 Virtual Services Serial Core Driver");
MODULE_AUTHOR("Open Kernel Labs, Inc");

View File

@ -5,6 +5,22 @@
if VSERVICES_SERVER || VSERVICES_CLIENT
menu "Protocol drivers"
config VSERVICES_PROTOCOL_SERIAL
bool
config VSERVICES_PROTOCOL_SERIAL_SERVER
tristate "Serial server protocol"
depends on VSERVICES_SUPPORT && VSERVICES_SERVER
select VSERVICES_PROTOCOL_SERIAL
help
This option adds support for Virtual Services serial protocol server.
config VSERVICES_PROTOCOL_SERIAL_CLIENT
tristate "Serial client protocol"
depends on VSERVICES_SUPPORT && VSERVICES_CLIENT
select VSERVICES_PROTOCOL_SERIAL
help
This option adds support for Virtual Services serial protocol client.
endmenu

View File

@ -1,3 +1,4 @@
# This is a autogenerated Makefile for vservice-linux-stacks
obj-$(CONFIG_VSERVICES_SUPPORT) += core/
obj-$(CONFIG_VSERVICES_PROTOCOL_SERIAL) += serial/

View File

@ -0,0 +1,7 @@
ccflags-y += -Werror
obj-$(CONFIG_VSERVICES_PROTOCOL_SERIAL_CLIENT) += vservices_protocol_serial_client.o
vservices_protocol_serial_client-objs = client.o
obj-$(CONFIG_VSERVICES_PROTOCOL_SERIAL_SERVER) += vservices_protocol_serial_server.o
vservices_protocol_serial_server-objs = server.o

View File

@ -0,0 +1,925 @@
/*
* Copyright (c) 2012-2018 General Dynamics
* Copyright (c) 2014 Open Kernel Labs, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/*
* This is the generated code for the serial client protocol handling.
*/
#include <linux/types.h>
#include <linux/err.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/wait.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
#include <linux/export.h>
#endif
#include <vservices/types.h>
#include <vservices/buffer.h>
#include <vservices/protocol/serial/types.h>
#include <vservices/protocol/serial/common.h>
#include <vservices/protocol/serial/client.h>
#include <vservices/service.h>
#include "../../transport.h"
#define VS_MBUF_SIZE(mbuf) mbuf->size
#define VS_MBUF_DATA(mbuf) mbuf->data
#define VS_STATE_SERVICE_PTR(state) state->service
static int _vs_client_serial_req_open(struct vs_client_serial_state *_state);
/*** Linux driver model integration ***/
struct vs_serial_client_driver {
struct vs_client_serial *client;
struct list_head list;
struct vs_service_driver vsdrv;
};
#define to_client_driver(d) \
container_of(d, struct vs_serial_client_driver, vsdrv)
static void reset_nack_requests(struct vs_service_device *service)
{
}
static void serial_handle_start(struct vs_service_device *service)
{
struct vs_client_serial_state *state = dev_get_drvdata(&service->dev);
struct vs_service_driver *vsdrv =
to_vs_service_driver(service->dev.driver);
struct vs_client_serial *client __maybe_unused =
to_client_driver(vsdrv)->client;
vs_service_state_lock(service);
state->state = VSERVICE_SERIAL_PROTOCOL_RESET_STATE;
_vs_client_serial_req_open(state);
vs_service_state_unlock(service);
}
static void serial_handle_reset(struct vs_service_device *service)
{
struct vs_client_serial_state *state = dev_get_drvdata(&service->dev);
struct vs_service_driver *vsdrv =
to_vs_service_driver(service->dev.driver);
struct vs_client_serial *client __maybe_unused =
to_client_driver(vsdrv)->client;
vs_service_state_lock(service);
if (!VSERVICE_BASE_STATE_IS_RUNNING(state->state.base)) {
vs_service_state_unlock(service);
return;
}
state->state.base = VSERVICE_BASE_RESET_STATE;
reset_nack_requests(service);
if (client->closed)
client->closed(state);
state->state = VSERVICE_SERIAL_PROTOCOL_RESET_STATE;
vs_service_state_unlock(service);
}
static void serial_handle_start_bh(struct vs_service_device *service)
{
struct vs_client_serial_state *state = dev_get_drvdata(&service->dev);
struct vs_service_driver *vsdrv =
to_vs_service_driver(service->dev.driver);
struct vs_client_serial *client __maybe_unused =
to_client_driver(vsdrv)->client;
vs_service_state_lock_bh(service);
state->state = VSERVICE_SERIAL_PROTOCOL_RESET_STATE;
_vs_client_serial_req_open(state);
vs_service_state_unlock_bh(service);
}
static void serial_handle_reset_bh(struct vs_service_device *service)
{
struct vs_client_serial_state *state = dev_get_drvdata(&service->dev);
struct vs_service_driver *vsdrv =
to_vs_service_driver(service->dev.driver);
struct vs_client_serial *client __maybe_unused =
to_client_driver(vsdrv)->client;
vs_service_state_lock_bh(service);
if (!VSERVICE_BASE_STATE_IS_RUNNING(state->state.base)) {
vs_service_state_unlock_bh(service);
return;
}
state->state.base = VSERVICE_BASE_RESET_STATE;
reset_nack_requests(service);
if (client->closed)
client->closed(state);
state->state = VSERVICE_SERIAL_PROTOCOL_RESET_STATE;
vs_service_state_unlock_bh(service);
}
static int serial_client_probe(struct vs_service_device *service);
static int serial_client_remove(struct vs_service_device *service);
static int serial_handle_message(struct vs_service_device *service,
struct vs_mbuf *_mbuf);
static void serial_handle_notify(struct vs_service_device *service,
uint32_t flags);
static void serial_handle_start(struct vs_service_device *service);
static void serial_handle_start_bh(struct vs_service_device *service);
static void serial_handle_reset(struct vs_service_device *service);
static void serial_handle_reset_bh(struct vs_service_device *service);
static int serial_handle_tx_ready(struct vs_service_device *service);
int __vservice_serial_client_register(struct vs_client_serial *client,
const char *name, struct module *owner)
{
int ret;
struct vs_serial_client_driver *driver;
if (client->tx_atomic && !client->rx_atomic)
return -EINVAL;
driver = kzalloc(sizeof(*driver), GFP_KERNEL);
if (!driver) {
ret = -ENOMEM;
goto fail_alloc_driver;
}
client->driver = &driver->vsdrv;
driver->client = client;
driver->vsdrv.protocol = VSERVICE_SERIAL_PROTOCOL_NAME;
driver->vsdrv.is_server = false;
driver->vsdrv.rx_atomic = client->rx_atomic;
driver->vsdrv.tx_atomic = client->tx_atomic;
driver->vsdrv.probe = serial_client_probe;
driver->vsdrv.remove = serial_client_remove;
driver->vsdrv.receive = serial_handle_message;
driver->vsdrv.notify = serial_handle_notify;
driver->vsdrv.start = client->tx_atomic ?
serial_handle_start_bh : serial_handle_start;
driver->vsdrv.reset = client->tx_atomic ?
serial_handle_reset_bh : serial_handle_reset;
driver->vsdrv.tx_ready = serial_handle_tx_ready;
driver->vsdrv.out_notify_count = 0;
driver->vsdrv.in_notify_count = 0;
driver->vsdrv.driver.name = name;
driver->vsdrv.driver.owner = owner;
driver->vsdrv.driver.bus = &vs_client_bus_type;
ret = driver_register(&driver->vsdrv.driver);
if (ret) {
goto fail_driver_register;
}
return 0;
fail_driver_register:
client->driver = NULL;
kfree(driver);
fail_alloc_driver:
return ret;
}
EXPORT_SYMBOL(__vservice_serial_client_register);
int vservice_serial_client_unregister(struct vs_client_serial *client)
{
struct vs_serial_client_driver *driver;
if (!client->driver)
return 0;
driver = to_client_driver(client->driver);
driver_unregister(&driver->vsdrv.driver);
client->driver = NULL;
kfree(driver);
return 0;
}
EXPORT_SYMBOL(vservice_serial_client_unregister);
static int serial_client_probe(struct vs_service_device *service)
{
struct vs_service_driver *vsdrv =
to_vs_service_driver(service->dev.driver);
struct vs_client_serial *client = to_client_driver(vsdrv)->client;
struct vs_client_serial_state *state;
state = client->alloc(service);
if (!state)
return -ENOMEM;
else if (IS_ERR(state))
return PTR_ERR(state);
state->service = vs_get_service(service);
state->state = VSERVICE_SERIAL_PROTOCOL_RESET_STATE;
dev_set_drvdata(&service->dev, state);
return 0;
}
static int serial_client_remove(struct vs_service_device *service)
{
struct vs_client_serial_state *state = dev_get_drvdata(&service->dev);
struct vs_service_driver *vsdrv =
to_vs_service_driver(service->dev.driver);
struct vs_client_serial *client = to_client_driver(vsdrv)->client;
state->released = true;
dev_set_drvdata(&service->dev, NULL);
client->release(state);
vs_put_service(service);
return 0;
}
static int serial_handle_tx_ready(struct vs_service_device *service)
{
struct vs_service_driver *vsdrv =
to_vs_service_driver(service->dev.driver);
struct vs_client_serial *client = to_client_driver(vsdrv)->client;
struct vs_client_serial_state *state = dev_get_drvdata(&service->dev);
if (!VSERVICE_BASE_STATE_IS_RUNNING(state->state.base))
return 0;
if (client->tx_ready)
client->tx_ready(state);
return 0;
}
static int _vs_client_serial_req_open(struct vs_client_serial_state *_state)
{
struct vs_mbuf *_mbuf;
const size_t _msg_size = sizeof(vs_message_id_t) + 0UL;
struct vs_service_driver *vsdrv =
to_vs_service_driver(VS_STATE_SERVICE_PTR(_state)->dev.driver);
__maybe_unused struct vs_client_serial *_client =
to_client_driver(vsdrv)->client;
switch (_state->state.base.statenum) {
case VSERVICE_BASE_STATE_CLOSED:
break;
default:
dev_err(&_state->service->dev,
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
__func__, __LINE__, _state->state.base.statenum,
vservice_base_get_state_string(_state->state.base));
return -EPROTO;
}
_mbuf =
vs_service_alloc_mbuf(VS_STATE_SERVICE_PTR(_state), _msg_size,
vs_service_has_atomic_rx(VS_STATE_SERVICE_PTR
(_state)) ?
GFP_ATOMIC : GFP_KERNEL);
if (IS_ERR(_mbuf))
return PTR_ERR(_mbuf);
if (!_mbuf) {
WARN_ON_ONCE(1);
return -ENOMEM;
}
*(vs_message_id_t *) (VS_MBUF_DATA(_mbuf)) =
VSERVICE_SERIAL_BASE_REQ_OPEN;
_state->state.base.statenum = VSERVICE_BASE_STATE_CLOSED__OPEN;
{
int err = vs_service_send(VS_STATE_SERVICE_PTR(_state), _mbuf);
if (err) {
dev_warn(&_state->service->dev,
"[%s:%d] Protocol warning: Error %d sending message on transport.\n",
__func__, __LINE__, err);
return err;
}
}
return 0;
}
EXPORT_SYMBOL(_vs_client_serial_req_open);
static int _vs_client_serial_req_close(struct vs_client_serial_state *_state)
{
struct vs_mbuf *_mbuf;
const size_t _msg_size = sizeof(vs_message_id_t) + 0UL;
struct vs_service_driver *vsdrv =
to_vs_service_driver(VS_STATE_SERVICE_PTR(_state)->dev.driver);
__maybe_unused struct vs_client_serial *_client =
to_client_driver(vsdrv)->client;
switch (_state->state.base.statenum) {
case VSERVICE_BASE_STATE_RUNNING:
break;
default:
dev_err(&_state->service->dev,
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
__func__, __LINE__, _state->state.base.statenum,
vservice_base_get_state_string(_state->state.base));
return -EPROTO;
}
_mbuf =
vs_service_alloc_mbuf(VS_STATE_SERVICE_PTR(_state), _msg_size,
vs_service_has_atomic_rx(VS_STATE_SERVICE_PTR
(_state)) ?
GFP_ATOMIC : GFP_KERNEL);
if (IS_ERR(_mbuf))
return PTR_ERR(_mbuf);
if (!_mbuf) {
WARN_ON_ONCE(1);
return -ENOMEM;
}
*(vs_message_id_t *) (VS_MBUF_DATA(_mbuf)) =
VSERVICE_SERIAL_BASE_REQ_CLOSE;
_state->state.base.statenum = VSERVICE_BASE_STATE_RUNNING__CLOSE;
{
int err = vs_service_send(VS_STATE_SERVICE_PTR(_state), _mbuf);
if (err) {
dev_warn(&_state->service->dev,
"[%s:%d] Protocol warning: Error %d sending message on transport.\n",
__func__, __LINE__, err);
return err;
}
}
return 0;
}
EXPORT_SYMBOL(_vs_client_serial_req_close);
static int _vs_client_serial_req_reopen(struct vs_client_serial_state *_state)
{
struct vs_mbuf *_mbuf;
const size_t _msg_size = sizeof(vs_message_id_t) + 0UL;
struct vs_service_driver *vsdrv =
to_vs_service_driver(VS_STATE_SERVICE_PTR(_state)->dev.driver);
__maybe_unused struct vs_client_serial *_client =
to_client_driver(vsdrv)->client;
switch (_state->state.base.statenum) {
case VSERVICE_BASE_STATE_RUNNING:
break;
default:
dev_err(&_state->service->dev,
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
__func__, __LINE__, _state->state.base.statenum,
vservice_base_get_state_string(_state->state.base));
return -EPROTO;
}
_mbuf =
vs_service_alloc_mbuf(VS_STATE_SERVICE_PTR(_state), _msg_size,
vs_service_has_atomic_rx(VS_STATE_SERVICE_PTR
(_state)) ?
GFP_ATOMIC : GFP_KERNEL);
if (IS_ERR(_mbuf))
return PTR_ERR(_mbuf);
if (!_mbuf) {
WARN_ON_ONCE(1);
return -ENOMEM;
}
*(vs_message_id_t *) (VS_MBUF_DATA(_mbuf)) =
VSERVICE_SERIAL_BASE_REQ_REOPEN;
_state->state.base.statenum = VSERVICE_BASE_STATE_RUNNING__REOPEN;
{
int err = vs_service_send(VS_STATE_SERVICE_PTR(_state), _mbuf);
if (err) {
dev_warn(&_state->service->dev,
"[%s:%d] Protocol warning: Error %d sending message on transport.\n",
__func__, __LINE__, err);
return err;
}
}
return 0;
}
EXPORT_SYMBOL(_vs_client_serial_req_reopen);
static int
serial_base_handle_ack_open(const struct vs_client_serial *_client,
struct vs_client_serial_state *_state,
struct vs_mbuf *_mbuf)
{
const size_t _expected_size = sizeof(vs_message_id_t) + 4UL;
if (VS_MBUF_SIZE(_mbuf) < _expected_size)
return -EBADMSG;
switch (_state->state.base.statenum) {
case VSERVICE_BASE_STATE_CLOSED__OPEN:
break;
default:
dev_err(&_state->service->dev,
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
__func__, __LINE__, _state->state.base.statenum,
vservice_base_get_state_string(_state->state.base));
return -EPROTO;
}
_state->state.base.statenum = VSERVICE_BASE_STATE_RUNNING;
_state->serial.packet_size =
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 0UL);
_state->packet_size =
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 0UL);
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
_client->opened(_state);
return 0;
}
static int
serial_base_handle_nack_open(const struct vs_client_serial *_client,
struct vs_client_serial_state *_state,
struct vs_mbuf *_mbuf)
{
switch (_state->state.base.statenum) {
case VSERVICE_BASE_STATE_CLOSED__OPEN:
break;
default:
dev_err(&_state->service->dev,
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
__func__, __LINE__, _state->state.base.statenum,
vservice_base_get_state_string(_state->state.base));
return -EPROTO;
}
_state->state.base.statenum = VSERVICE_BASE_STATE_CLOSED;
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
dev_err(&VS_STATE_SERVICE_PTR(_state)->dev,
"Open operation failed for device %s\n",
VS_STATE_SERVICE_PTR(_state)->name);
return 0;
}
EXPORT_SYMBOL(serial_base_handle_ack_open);
static int
serial_base_handle_ack_close(const struct vs_client_serial *_client,
struct vs_client_serial_state *_state,
struct vs_mbuf *_mbuf)
{
const size_t _expected_size = sizeof(vs_message_id_t) + 0UL;
if (VS_MBUF_SIZE(_mbuf) < _expected_size)
return -EBADMSG;
switch (_state->state.base.statenum) {
case VSERVICE_BASE_STATE_RUNNING__CLOSE:
break;
default:
dev_err(&_state->service->dev,
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
__func__, __LINE__, _state->state.base.statenum,
vservice_base_get_state_string(_state->state.base));
return -EPROTO;
}
_state->state.base.statenum = VSERVICE_BASE_STATE_CLOSED;
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
wake_up_all(&_state->service->quota_wq);
_client->closed(_state);
return 0;
}
static int
serial_base_handle_nack_close(const struct vs_client_serial *_client,
struct vs_client_serial_state *_state,
struct vs_mbuf *_mbuf)
{
switch (_state->state.base.statenum) {
case VSERVICE_BASE_STATE_RUNNING__CLOSE:
break;
default:
dev_err(&_state->service->dev,
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
__func__, __LINE__, _state->state.base.statenum,
vservice_base_get_state_string(_state->state.base));
return -EPROTO;
}
_state->state.base.statenum = VSERVICE_BASE_STATE_RUNNING;
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
wake_up_all(&_state->service->quota_wq);
_client->closed(_state);
return 0;
}
EXPORT_SYMBOL(serial_base_handle_ack_close);
static int
serial_base_handle_ack_reopen(const struct vs_client_serial *_client,
struct vs_client_serial_state *_state,
struct vs_mbuf *_mbuf)
{
const size_t _expected_size = sizeof(vs_message_id_t) + 0UL;
if (VS_MBUF_SIZE(_mbuf) < _expected_size)
return -EBADMSG;
switch (_state->state.base.statenum) {
case VSERVICE_BASE_STATE_RUNNING__REOPEN:
break;
default:
dev_err(&_state->service->dev,
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
__func__, __LINE__, _state->state.base.statenum,
vservice_base_get_state_string(_state->state.base));
return -EPROTO;
}
_state->state.base.statenum = VSERVICE_BASE__RESET;
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
if (_client->reopened) {
_client->reopened(_state);
return 0;
}
wake_up_all(&_state->service->quota_wq);
_client->closed(_state);
return _vs_client_serial_req_open(_state);
}
static int
serial_base_handle_nack_reopen(const struct vs_client_serial *_client,
struct vs_client_serial_state *_state,
struct vs_mbuf *_mbuf)
{
switch (_state->state.base.statenum) {
case VSERVICE_BASE_STATE_RUNNING__REOPEN:
break;
default:
dev_err(&_state->service->dev,
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
__func__, __LINE__, _state->state.base.statenum,
vservice_base_get_state_string(_state->state.base));
return -EPROTO;
}
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
return 0;
}
EXPORT_SYMBOL(serial_base_handle_ack_reopen);
struct vs_mbuf *vs_client_serial_serial_alloc_msg(struct vs_client_serial_state
*_state, struct vs_pbuf *b,
gfp_t flags)
{
struct vs_mbuf *_mbuf;
const vs_message_id_t _msg_id = VSERVICE_SERIAL_SERIAL_MSG_MSG;
const uint32_t _msg_size =
sizeof(vs_message_id_t) + _state->serial.packet_size + 4UL;
_mbuf =
vs_service_alloc_mbuf(VS_STATE_SERVICE_PTR(_state), _msg_size,
flags);
if (IS_ERR(_mbuf))
return _mbuf;
if (!_mbuf) {
WARN_ON_ONCE(1);
return ERR_PTR(-ENOMEM);
}
*(vs_message_id_t *) (VS_MBUF_DATA(_mbuf)) = _msg_id;
if (!b)
goto fail;
b->data =
(uintptr_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 0UL +
sizeof(uint32_t));
b->size = _state->serial.packet_size;
b->max_size = b->size;
return _mbuf;
fail:
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
return NULL;
}
EXPORT_SYMBOL(vs_client_serial_serial_alloc_msg);
int vs_client_serial_serial_getbufs_msg(struct vs_client_serial_state *_state,
struct vs_pbuf *b,
struct vs_mbuf *_mbuf)
{
const vs_message_id_t _msg_id = VSERVICE_SERIAL_SERIAL_MSG_MSG;
const size_t _max_size =
sizeof(vs_message_id_t) + _state->serial.packet_size + 4UL;
const size_t _min_size = _max_size - _state->serial.packet_size;
size_t _exact_size;
if (*(vs_message_id_t *) (VS_MBUF_DATA(_mbuf)) != _msg_id)
return -EINVAL;
if ((VS_MBUF_SIZE(_mbuf) > _max_size)
|| (VS_MBUF_SIZE(_mbuf) < _min_size))
return -EBADMSG;
b->size =
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 0UL);
b->data =
(uintptr_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 0UL +
sizeof(uint32_t));
b->max_size = b->size;
/* Now check the size received is the exact size expected */
_exact_size = _max_size - (_state->serial.packet_size - b->size);
if (VS_MBUF_SIZE(_mbuf) != _exact_size)
return -EBADMSG;
return 0;
}
EXPORT_SYMBOL(vs_client_serial_serial_getbufs_msg);
int vs_client_serial_serial_free_msg(struct vs_client_serial_state *_state,
struct vs_pbuf *b, struct vs_mbuf *_mbuf)
{
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
return 0;
}
EXPORT_SYMBOL(vs_client_serial_serial_free_msg);
static int
vs_client_serial_serial_handle_msg(const struct vs_client_serial *_client,
struct vs_client_serial_state *_state,
struct vs_mbuf *_mbuf)
{
const size_t _max_size =
sizeof(vs_message_id_t) + _state->serial.packet_size + 4UL;
struct vs_pbuf b;
const size_t _min_size = _max_size - _state->serial.packet_size;
size_t _exact_size;
if (!VSERVICE_BASE_STATE_IS_RUNNING(_state->state.base))
return -EPROTO;
/* The first check is to ensure the message isn't complete garbage */
if ((VS_MBUF_SIZE(_mbuf) > _max_size)
|| (VS_MBUF_SIZE(_mbuf) < _min_size))
return -EBADMSG;
b.size =
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 0UL);
b.data =
(uintptr_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 0UL +
sizeof(uint32_t));
b.max_size = b.size;
/* Now check the size received is the exact size expected */
_exact_size = _max_size - (_state->serial.packet_size - b.size);
if (VS_MBUF_SIZE(_mbuf) != _exact_size)
return -EBADMSG;
if (_client->serial.msg_msg)
return _client->serial.msg_msg(_state, b, _mbuf);
return 0;
return 0;
}
EXPORT_SYMBOL(vs_client_serial_serial_handle_msg);
int
vs_client_serial_serial_send_msg(struct vs_client_serial_state *_state,
struct vs_pbuf b, struct vs_mbuf *_mbuf)
{
struct vs_service_driver *vsdrv =
to_vs_service_driver(VS_STATE_SERVICE_PTR(_state)->dev.driver);
__maybe_unused struct vs_client_serial *_client =
to_client_driver(vsdrv)->client;
if (_state->state.base.statenum != VSERVICE_BASE_STATE_RUNNING)
return -EPROTO;
if (*(vs_message_id_t *) (VS_MBUF_DATA(_mbuf)) !=
VSERVICE_SERIAL_SERIAL_MSG_MSG)
return -EINVAL;
if ((b.size + sizeof(vs_message_id_t) + 0UL) > VS_MBUF_SIZE(_mbuf))
return -EINVAL;
if (b.size < b.max_size)
VS_MBUF_SIZE(_mbuf) -= (b.max_size - b.size);
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 0UL) =
b.size;
{
int err = vs_service_send(VS_STATE_SERVICE_PTR(_state), _mbuf);
if (err) {
dev_warn(&_state->service->dev,
"[%s:%d] Protocol warning: Error %d sending message on transport.\n",
__func__, __LINE__, err);
return err;
}
}
return 0;
}
EXPORT_SYMBOL(vs_client_serial_serial_send_msg);
static int
serial_handle_message(struct vs_service_device *service, struct vs_mbuf *_mbuf)
{
vs_message_id_t message_id;
__maybe_unused struct vs_client_serial_state *state =
dev_get_drvdata(&service->dev);
struct vs_service_driver *vsdrv =
to_vs_service_driver(service->dev.driver);
__maybe_unused struct vs_client_serial *client =
to_client_driver(vsdrv)->client;
int ret;
/* Extract the message ID */
if (VS_MBUF_SIZE(_mbuf) < sizeof(message_id)) {
dev_err(&state->service->dev,
"[%s:%d] Protocol error: Invalid message size %zd\n",
__func__, __LINE__, VS_MBUF_SIZE(_mbuf));
return -EBADMSG;
}
message_id = *(vs_message_id_t *) (VS_MBUF_DATA(_mbuf));
switch (message_id) {
/** interface base **/
/* command in sync open */
case VSERVICE_SERIAL_BASE_ACK_OPEN:
ret = serial_base_handle_ack_open(client, state, _mbuf);
break;
case VSERVICE_SERIAL_BASE_NACK_OPEN:
ret = serial_base_handle_nack_open(client, state, _mbuf);
break;
/* command in sync close */
case VSERVICE_SERIAL_BASE_ACK_CLOSE:
ret = serial_base_handle_ack_close(client, state, _mbuf);
break;
case VSERVICE_SERIAL_BASE_NACK_CLOSE:
ret = serial_base_handle_nack_close(client, state, _mbuf);
break;
/* command in sync reopen */
case VSERVICE_SERIAL_BASE_ACK_REOPEN:
ret = serial_base_handle_ack_reopen(client, state, _mbuf);
break;
case VSERVICE_SERIAL_BASE_NACK_REOPEN:
ret = serial_base_handle_nack_reopen(client, state, _mbuf);
break;
/** interface serial **/
/* message msg */
case VSERVICE_SERIAL_SERIAL_MSG_MSG:
ret = vs_client_serial_serial_handle_msg(client, state, _mbuf);
break;
default:
dev_err(&state->service->dev,
"[%s:%d] Protocol error: Unknown message type %d\n",
__func__, __LINE__, (int)message_id);
ret = -EPROTO;
break;
}
if (ret) {
dev_err(&state->service->dev,
"[%s:%d] Protocol error: Handler for message type %d returned %d\n",
__func__, __LINE__, (int)message_id, ret);
}
return ret;
}
static void serial_handle_notify(struct vs_service_device *service,
uint32_t notify_bits)
{
__maybe_unused struct vs_client_serial_state *state =
dev_get_drvdata(&service->dev);
struct vs_service_driver *vsdrv =
to_vs_service_driver(service->dev.driver);
__maybe_unused struct vs_client_serial *client =
to_client_driver(vsdrv)->client;
uint32_t bits = notify_bits;
int ret;
while (bits) {
uint32_t not = __ffs(bits);
switch (not) {
/** interface serial **/
default:
dev_err(&state->service->dev,
"[%s:%d] Protocol error: Unknown notification %d\n",
__func__, __LINE__, (int)not);
ret = -EPROTO;
break;
}
bits &= ~(1 << not);
if (ret) {
dev_err(&state->service->dev,
"[%s:%d] Protocol error: Handler for notification %d returned %d\n",
__func__, __LINE__, (int)not, ret);
}
}
}
int vs_client_serial_reopen(struct vs_client_serial_state *_state)
{
return _vs_client_serial_req_reopen(_state);
}
EXPORT_SYMBOL(vs_client_serial_reopen);
int vs_client_serial_close(struct vs_client_serial_state *_state)
{
return _vs_client_serial_req_close(_state);
}
EXPORT_SYMBOL(vs_client_serial_close);
MODULE_DESCRIPTION("OKL4 Virtual Services serialClient Protocol Driver");
MODULE_AUTHOR("Open Kernel Labs, Inc");

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
header-y += types.h

View File

@ -0,0 +1,114 @@
/*
* Copyright (c) 2012-2018 General Dynamics
* Copyright (c) 2014 Open Kernel Labs, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#if !defined(__VSERVICES_CLIENT_SERIAL__)
#define __VSERVICES_CLIENT_SERIAL__
struct vs_service_device;
struct vs_client_serial_state;
struct vs_client_serial {
/*
* If set to false then the receive message handlers are run from
* workqueue context and are allowed to sleep. If set to true the
* message handlers are run from tasklet context and may not sleep.
*/
bool rx_atomic;
/*
* If this is set to true along with rx_atomic, the driver is allowed
* to send messages from softirq contexts other than the receive
* message handlers, after calling vs_service_state_lock_bh. Otherwise,
* messages may only be sent from the receive message handlers, or
* from task context after calling vs_service_state_lock. This must
* not be set to true if rx_atomic is set to false.
*/
bool tx_atomic;
/** session setup **/
struct vs_client_serial_state *(*alloc) (struct vs_service_device *
service);
void (*release) (struct vs_client_serial_state * _state);
struct vs_service_driver *driver;
/** Opened, reopened and closed functions **/
void (*opened) (struct vs_client_serial_state * _state);
void (*reopened) (struct vs_client_serial_state * _state);
void (*closed) (struct vs_client_serial_state * _state);
/** Send/receive state callbacks **/
int (*tx_ready) (struct vs_client_serial_state * _state);
struct {
int (*msg_msg) (struct vs_client_serial_state * _state,
struct vs_pbuf b, struct vs_mbuf * _mbuf);
} serial;
};
struct vs_client_serial_state {
vservice_serial_protocol_state_t state;
uint32_t packet_size;
struct {
uint32_t packet_size;
} serial;
struct vs_service_device *service;
bool released;
};
extern int vs_client_serial_reopen(struct vs_client_serial_state *_state);
extern int vs_client_serial_close(struct vs_client_serial_state *_state);
/** interface serial **/
/* message msg */
extern struct vs_mbuf *vs_client_serial_serial_alloc_msg(struct
vs_client_serial_state
*_state,
struct vs_pbuf *b,
gfp_t flags);
extern int vs_client_serial_serial_getbufs_msg(struct vs_client_serial_state
*_state, struct vs_pbuf *b,
struct vs_mbuf *_mbuf);
extern int vs_client_serial_serial_free_msg(struct vs_client_serial_state
*_state, struct vs_pbuf *b,
struct vs_mbuf *_mbuf);
extern int vs_client_serial_serial_send_msg(struct vs_client_serial_state
*_state, struct vs_pbuf b,
struct vs_mbuf *_mbuf);
/** Module registration **/
struct module;
extern int __vservice_serial_client_register(struct vs_client_serial *client,
const char *name,
struct module *owner);
static inline int vservice_serial_client_register(struct vs_client_serial
*client, const char *name)
{
#ifdef MODULE
extern struct module __this_module;
struct module *this_module = &__this_module;
#else
struct module *this_module = NULL;
#endif
return __vservice_serial_client_register(client, name, this_module);
}
extern int vservice_serial_client_unregister(struct vs_client_serial *client);
#endif /* ! __VSERVICES_CLIENT_SERIAL__ */

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2012-2018 General Dynamics
* Copyright (c) 2014 Open Kernel Labs, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#if !defined(__VSERVICES_SERIAL_PROTOCOL_H__)
#define __VSERVICES_SERIAL_PROTOCOL_H__
#define VSERVICE_SERIAL_PROTOCOL_NAME "com.ok-labs.serial"
typedef enum {
VSERVICE_SERIAL_BASE_REQ_OPEN,
VSERVICE_SERIAL_BASE_ACK_OPEN,
VSERVICE_SERIAL_BASE_NACK_OPEN,
VSERVICE_SERIAL_BASE_REQ_CLOSE,
VSERVICE_SERIAL_BASE_ACK_CLOSE,
VSERVICE_SERIAL_BASE_NACK_CLOSE,
VSERVICE_SERIAL_BASE_REQ_REOPEN,
VSERVICE_SERIAL_BASE_ACK_REOPEN,
VSERVICE_SERIAL_BASE_NACK_REOPEN,
VSERVICE_SERIAL_BASE_MSG_RESET,
VSERVICE_SERIAL_SERIAL_MSG_MSG,
} vservice_serial_message_id_t;
typedef enum {
VSERVICE_SERIAL_NBIT_IN__COUNT
} vservice_serial_nbit_in_t;
typedef enum {
VSERVICE_SERIAL_NBIT_OUT__COUNT
} vservice_serial_nbit_out_t;
/* Notification mask macros */
#endif /* ! __VSERVICES_SERIAL_PROTOCOL_H__ */

View File

@ -0,0 +1,134 @@
/*
* Copyright (c) 2012-2018 General Dynamics
* Copyright (c) 2014 Open Kernel Labs, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#if !defined(VSERVICES_SERVER_SERIAL)
#define VSERVICES_SERVER_SERIAL
struct vs_service_device;
struct vs_server_serial_state;
struct vs_server_serial {
/*
* If set to false then the receive message handlers are run from
* workqueue context and are allowed to sleep. If set to true the
* message handlers are run from tasklet context and may not sleep.
*/
bool rx_atomic;
/*
* If this is set to true along with rx_atomic, the driver is allowed
* to send messages from softirq contexts other than the receive
* message handlers, after calling vs_service_state_lock_bh. Otherwise,
* messages may only be sent from the receive message handlers, or
* from task context after calling vs_service_state_lock. This must
* not be set to true if rx_atomic is set to false.
*/
bool tx_atomic;
/*
* These are the driver's recommended message quotas. They are used
* by the core service to select message quotas for services with no
* explicitly configured quotas.
*/
u32 in_quota_best;
u32 out_quota_best;
/** session setup **/
struct vs_server_serial_state *(*alloc) (struct vs_service_device *
service);
void (*release) (struct vs_server_serial_state * _state);
struct vs_service_driver *driver;
/** Open, reopen, close and closed functions **/
vs_server_response_type_t(*open) (struct vs_server_serial_state *
_state);
vs_server_response_type_t(*reopen) (struct vs_server_serial_state *
_state);
vs_server_response_type_t(*close) (struct vs_server_serial_state *
_state);
void (*closed) (struct vs_server_serial_state * _state);
/** Send/receive state callbacks **/
int (*tx_ready) (struct vs_server_serial_state * _state);
struct {
int (*msg_msg) (struct vs_server_serial_state * _state,
struct vs_pbuf b, struct vs_mbuf * _mbuf);
} serial;
};
struct vs_server_serial_state {
vservice_serial_protocol_state_t state;
uint32_t packet_size;
struct {
uint32_t packet_size;
} serial;
struct vs_service_device *service;
bool released;
};
/** Complete calls for server core functions **/
extern int vs_server_serial_open_complete(struct vs_server_serial_state *_state,
vs_server_response_type_t resp);
extern int vs_server_serial_close_complete(struct vs_server_serial_state
*_state,
vs_server_response_type_t resp);
extern int vs_server_serial_reopen_complete(struct vs_server_serial_state
*_state,
vs_server_response_type_t resp);
/** interface serial **/
/* message msg */
extern struct vs_mbuf *vs_server_serial_serial_alloc_msg(struct
vs_server_serial_state
*_state,
struct vs_pbuf *b,
gfp_t flags);
extern int vs_server_serial_serial_getbufs_msg(struct vs_server_serial_state
*_state, struct vs_pbuf *b,
struct vs_mbuf *_mbuf);
extern int vs_server_serial_serial_free_msg(struct vs_server_serial_state
*_state, struct vs_pbuf *b,
struct vs_mbuf *_mbuf);
extern int vs_server_serial_serial_send_msg(struct vs_server_serial_state
*_state, struct vs_pbuf b,
struct vs_mbuf *_mbuf);
/** Module registration **/
struct module;
extern int __vservice_serial_server_register(struct vs_server_serial *server,
const char *name,
struct module *owner);
static inline int vservice_serial_server_register(struct vs_server_serial
*server, const char *name)
{
#ifdef MODULE
extern struct module __this_module;
struct module *this_module = &__this_module;
#else
struct module *this_module = NULL;
#endif
return __vservice_serial_server_register(server, name, this_module);
}
extern int vservice_serial_server_unregister(struct vs_server_serial *server);
#endif /* ! VSERVICES_SERVER_SERIAL */

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2012-2018 General Dynamics
* Copyright (c) 2014 Open Kernel Labs, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#if !defined(VSERVICES_SERIAL_TYPES_H)
#define VSERVICES_SERIAL_TYPES_H
typedef enum {
/* state closed */
VSERVICE_BASE_STATE_CLOSED = 0,
VSERVICE_BASE_STATE_CLOSED__OPEN,
VSERVICE_BASE_STATE_CLOSED__CLOSE,
VSERVICE_BASE_STATE_CLOSED__REOPEN,
/* state running */
VSERVICE_BASE_STATE_RUNNING,
VSERVICE_BASE_STATE_RUNNING__OPEN,
VSERVICE_BASE_STATE_RUNNING__CLOSE,
VSERVICE_BASE_STATE_RUNNING__REOPEN,
VSERVICE_BASE__RESET = VSERVICE_BASE_STATE_CLOSED
} vservice_base_statenum_t;
typedef struct {
vservice_base_statenum_t statenum;
} vservice_base_state_t;
#define VSERVICE_BASE_RESET_STATE (vservice_base_state_t) { \
.statenum = VSERVICE_BASE__RESET}
#define VSERVICE_BASE_STATE_IS_CLOSED(state) (\
((state).statenum == VSERVICE_BASE_STATE_CLOSED) || \
((state).statenum == VSERVICE_BASE_STATE_CLOSED__OPEN) || \
((state).statenum == VSERVICE_BASE_STATE_CLOSED__CLOSE) || \
((state).statenum == VSERVICE_BASE_STATE_CLOSED__REOPEN))
#define VSERVICE_BASE_STATE_IS_RUNNING(state) (\
((state).statenum == VSERVICE_BASE_STATE_RUNNING) || \
((state).statenum == VSERVICE_BASE_STATE_RUNNING__OPEN) || \
((state).statenum == VSERVICE_BASE_STATE_RUNNING__CLOSE) || \
((state).statenum == VSERVICE_BASE_STATE_RUNNING__REOPEN))
#define VSERVICE_BASE_STATE_VALID(state) ( \
VSERVICE_BASE_STATE_IS_CLOSED(state) ? true : \
VSERVICE_BASE_STATE_IS_RUNNING(state) ? true : \
false)
static inline const char *vservice_base_get_state_string(vservice_base_state_t
state)
{
static const char *names[] =
{ "closed", "closed__open", "closed__close", "closed__reopen",
"running", "running__open", "running__close", "running__reopen"
};
if (!VSERVICE_BASE_STATE_VALID(state)) {
return "INVALID";
}
return names[state.statenum];
}
typedef struct {
} vservice_serial_state_t;
#define VSERVICE_SERIAL_RESET_STATE (vservice_serial_state_t) { \
}
#define VSERVICE_SERIAL_STATE_VALID(state) true
typedef struct {
vservice_base_state_t base;
vservice_serial_state_t serial;
} vservice_serial_protocol_state_t;
#define VSERVICE_SERIAL_PROTOCOL_RESET_STATE (vservice_serial_protocol_state_t) {\
.base = VSERVICE_BASE_RESET_STATE,\
.serial = VSERVICE_SERIAL_RESET_STATE }
#define VSERVICE_SERIAL_IS_STATE_RESET(state) \
((state).base.statenum == VSERVICE_BASE__RESET)
#endif /* ! VSERVICES_SERIAL_TYPES_H */