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:
parent
05623dfc72
commit
17607ebf91
@ -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.
|
||||
|
@ -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
|
||||
|
132
drivers/char/vs_serial_client.c
Normal file
132
drivers/char/vs_serial_client.c
Normal 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");
|
91
drivers/char/vs_serial_common.h
Normal file
91
drivers/char/vs_serial_common.h
Normal 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 */
|
152
drivers/char/vs_serial_server.c
Normal file
152
drivers/char/vs_serial_server.c
Normal 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");
|
634
drivers/char/vservices_serial.c
Normal file
634
drivers/char/vservices_serial.c
Normal 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");
|
@ -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
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
# This is a autogenerated Makefile for vservice-linux-stacks
|
||||
|
||||
obj-$(CONFIG_VSERVICES_SUPPORT) += core/
|
||||
obj-$(CONFIG_VSERVICES_PROTOCOL_SERIAL) += serial/
|
||||
|
7
drivers/vservices/protocol/serial/Makefile
Normal file
7
drivers/vservices/protocol/serial/Makefile
Normal 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
|
925
drivers/vservices/protocol/serial/client.c
Normal file
925
drivers/vservices/protocol/serial/client.c
Normal 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");
|
1086
drivers/vservices/protocol/serial/server.c
Normal file
1086
drivers/vservices/protocol/serial/server.c
Normal file
File diff suppressed because it is too large
Load Diff
1
include/vservices/protocol/serial/Kbuild
Normal file
1
include/vservices/protocol/serial/Kbuild
Normal file
@ -0,0 +1 @@
|
||||
header-y += types.h
|
114
include/vservices/protocol/serial/client.h
Normal file
114
include/vservices/protocol/serial/client.h
Normal 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__ */
|
37
include/vservices/protocol/serial/common.h
Normal file
37
include/vservices/protocol/serial/common.h
Normal 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__ */
|
134
include/vservices/protocol/serial/server.h
Normal file
134
include/vservices/protocol/serial/server.h
Normal 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 */
|
88
include/vservices/protocol/serial/types.h
Normal file
88
include/vservices/protocol/serial/types.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user