Skip to content
Snippets Groups Projects
Commit a23b66d4 authored by Luca Seritan's avatar Luca Seritan Committed by Grzegorz Gwóźdź
Browse files

Rewrite input to c++


Implement input device interface, rewrite the keypad input to use gpio
callbacks and implement basic polling in main. The input devices add
themselves to the devices list based on custom kernel configs.

Signed-off-by: default avatarLuca Seritan <luca.seritan@huawei.com>
Signed-off-by: default avatarGrzegorz Gwozdz <grzegorz.gwozdz@huawei.com>
parent fe6b1313
Branches both-changes
No related tags found
No related merge requests found
#ifdef CONFIG_COAP_INPUT
#include "coapInput.hpp"
#include <logging/log.h>
LOG_MODULE_REGISTER(coap_server, LOG_LEVEL_DBG);
#include <zephyr/net/coap.h>
#include <zephyr/net/coap_link_format.h>
extern "C" {
#include <net_private.h>
#include <ipv6.h>
}
#include <errno.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/zephyr.h>
#include <zephyr/net/socket.h>
#include <zephyr/net/net_mgmt.h>
#include <zephyr/net/net_ip.h>
#include <zephyr/net/udp.h>
coap_input::coap_input(std::string port) {
int r = start_coap_server(std::stoul(port));
if (r < 0) {
LOG_ERR("Could not start coap server");
throw(errno);
}
doorlock::add_input(this);
}
bool coap_input::join_coap_multicast_group(uint16_t port)
{
static struct in6_addr my_addr;
static struct sockaddr_in6 mcast_addr = {
.sin6_family = AF_INET6,
.sin6_port = htons(port),
.sin6_addr = ALL_NODES_LOCAL_COAP_MCAST };
struct net_if_addr *ifaddr;
struct net_if *iface;
int ret;
iface = net_if_get_default();
if (!iface) {
LOG_ERR("Could not get the default interface\n");
return false;
}
if (net_addr_pton(AF_INET6,
CONFIG_NET_CONFIG_MY_IPV6_ADDR,
&my_addr) < 0) {
LOG_ERR("Invalid IPv6 address %s",
CONFIG_NET_CONFIG_MY_IPV6_ADDR);
throw(errno);
}
ifaddr = net_if_ipv6_addr_add(iface, &my_addr, NET_ADDR_MANUAL, 0);
if (!ifaddr) {
LOG_ERR("Could not add unicast address to interface");
return false;
}
ifaddr->addr_state = NET_ADDR_PREFERRED;
ret = net_ipv6_mld_join(iface, &mcast_addr.sin6_addr);
if (ret < 0) {
LOG_ERR("Cannot join %s IPv6 multicast group (%d)",
net_sprint_ipv6_addr(&mcast_addr.sin6_addr), ret);
return false;
}
LOG_DBG("joined multicast group");
return true;
}
int coap_input::start_coap_server(uint16_t port)
{
join_coap_multicast_group(port);
struct sockaddr_in6 addr6;
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(port);
this->_m_sockfd = zsock_socket(addr6.sin6_family, SOCK_DGRAM, IPPROTO_UDP);
if (_m_sockfd < 0) {
LOG_ERR("Failed to create UDP socket %d", errno);
return -errno;
}
int r = zsock_bind(_m_sockfd, (struct sockaddr *)&addr6, sizeof(addr6));
if (r < 0) {
LOG_ERR("Failed to bind UDP socket %d", errno);
return -errno;
}
return 0;
}
int coap_input::fd() const noexcept {
return this->_m_sockfd;
}
long coap_input::read_event() const {
int received;
struct sockaddr client_addr;
socklen_t client_addr_len;
uint8_t request[MAX_COAP_MSG_LEN];
client_addr_len = sizeof(client_addr);
received = zsock_recvfrom(this->_m_sockfd, request, sizeof(request), 0,
&client_addr, &client_addr_len);
if (received < 0) {
LOG_ERR("Connection error %d", errno);
return -errno;
}
struct coap_packet packet;
struct coap_option options[16] = { 0 };
uint8_t opt_num = 16U;
int r = coap_packet_parse(&packet, request, received, NULL, 0);
if (r < 0) {
LOG_ERR("Invalid data received (%d)\n", r);
return -1;
}
uint16_t payload_len = 1;
const uint8_t *payload = coap_packet_get_payload(&packet, &payload_len);
if(payload_len != 1) {
LOG_ERR("Only single key presses are supported at a time");
return -1;
}
return payload[0];
}
coap_input coap_input_device("5683");
#endif // CONFIG_COAP_INPUT
#ifndef COAP_INPUT_HPP
#ifdef CONFIG_COAP_INPUT
#include "inputDevice.hpp"
#include <string>
#define MAX_COAP_MSG_LEN 256
#define ALL_NODES_LOCAL_COAP_MCAST \
{ { { 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xfd } } }
class coap_input : input_device {
public:
coap_input(std::string port);
long read_event() const;
int fd() const noexcept;
private:
int _m_sockfd;
bool join_coap_multicast_group(uint16_t port);
int start_coap_server(uint16_t port);
void process_packet();
};
#endif // CONFIG_COAP_INPUT
#endif // COAP_INPUT_HPP
#ifndef INPUT_DEVICE_HPP
#define INPUT_DEVICE_HPP
class input_device {
public:
virtual long read_event() const = 0;
virtual int fd() const noexcept = 0;
};
namespace doorlock {
void add_input(input_device *device);
};
#endif // INPUT_DEVICE_HPP
\ No newline at end of file
#define NO_ACTION 0
#define ERROR -1
#define CORRECT_PIN 1
#define WRONG_PIN 2
#define CHANGED_PIN 3
#define CHANGING_PIN 4
#define TIMEOUT 5
#include "inputStateMachine.hpp"
#include <zephyr/sys/printk.h>
input_state_machine::input_state_machine() {
this->_m_current_input = "";
this->_m_current_state = input_state::STATE_INITIAL_PIN;
using std::placeholders::_1;
this->_m_state_process[input_state::STATE_RESET] = std::bind(&input_state_machine::state_reset_process, this, _1);
this->_m_state_process[input_state::STATE_INPUT_PIN] = std::bind(&input_state_machine::state_input_pin_process, this, _1);
this->_m_state_process[input_state::STATE_CHANGE_PIN] = std::bind(&input_state_machine::state_change_pin_process, this, _1);
this->_m_state_process[input_state::STATE_INITIAL_PIN] = std::bind(&input_state_machine::state_initial_pin_process, this, _1);
}
int input_state_machine::process(char event) {
if (event != '*' && event != '#' && (event < '0' || event > '9'))
return ERROR;
return _m_state_process[_m_current_state](event);
}
int input_state_machine::state_reset_process(char event) {
if (event == '#') {
this->_m_current_state = input_state::STATE_CHANGE_PIN;
return CHANGING_PIN;
}
if (event >= '0' && event <= '9') {
this->_m_current_input += event;
this->_m_current_state = input_state::STATE_INPUT_PIN;
}
return NO_ACTION;
}
int input_state_machine::state_input_pin_process(char event) {
if (event == '*') {
this->_m_current_input = "";
this->_m_current_state = input_state::STATE_RESET;
}
if (event >= '0' && event <= '9') {
this->_m_current_input += event;
if (this->_m_current_input.size() != 4) {
return NO_ACTION;
}
if (this->_m_current_input == _m_pin) {
this->_m_current_input = "";
return CORRECT_PIN;
} else {
this->_m_current_input = "";
return WRONG_PIN;
}
}
}
int input_state_machine::state_change_pin_process(char event) {
if (event == '*') {
this->_m_current_input = "";
this->_m_current_state = input_state::STATE_RESET;
return NO_ACTION;
}
if (event >= '0' && event <= '9') {
this->_m_current_input += event;
if (this->_m_current_input.size() != 4) {
return CHANGING_PIN;
} else {
this->_m_pin = _m_current_input;
_m_current_input = "";
this->_m_current_state = input_state::STATE_INPUT_PIN;
return CHANGED_PIN;
}
}
}
int input_state_machine::state_initial_pin_process(char event) {
if (event >= '0' && event <= '9') {
this->_m_current_input += event;
if (this->_m_current_input.size() != 4) {
return CHANGING_PIN;
} else {
this->_m_pin = this->_m_current_input;
printk("New pin: %s\n", this->_m_pin.c_str());
_m_current_input = "";
this->_m_current_state = input_state::STATE_INPUT_PIN;
return CHANGED_PIN;
}
}
}
#ifndef STATE_MACHINE_HPP
#define STATE_MACHINE_HPP
#define NO_ACTION 0
#define ERROR -1
#define CORRECT_PIN 1
#define WRONG_PIN 2
#define CHANGED_PIN 3
#define CHANGING_PIN 4
#define TIMEOUT 5
#include <string>
#include <functional>
#include <map>
class input_state_machine {
public:
input_state_machine();
int process(char event);
private:
enum class input_state {
STATE_RESET = 1,
STATE_INPUT_PIN,
STATE_CHANGE_PIN,
STATE_INITIAL_PIN
};
std::string _m_pin;
std::string _m_current_input;
input_state _m_current_state;
std::map<input_state, std::function<int(char)> > _m_state_process;
int state_reset_process(char event);
int state_input_pin_process(char event);
int state_change_pin_process(char event);
int state_initial_pin_process(char event);
};
#endif // STATE_MACHINE_HPP
\ No newline at end of file
#ifdef CONFIG_KEYPAD_INPUT
#include "inputDevice.hpp"
#include "keypadInput.hpp"
#include <zephyr/drivers/gpio.h>
#include <zephyr/net/socket.h>
#include <zephyr/posix/unistd.h>
void gpio_handler(const device* port,
gpio_callback *cb,
gpio_port_pins_t pins) {
keypad_input::handler_wrapper *wrapper = CONTAINER_OF(cb, keypad_input::handler_wrapper, cb);
wrapper->input->process();
}
keypad_input::keypad_input()
: _m_wrapper(gpio_cb, this)
{
for(int i=0; i<7; i++)
pins[i] = gpio_pin_configure_dt(&kbd[i], GPIO_INPUT | GPIO_PULL_UP);
int ret = zsock_socketpair(AF_UNIX, SOCK_STREAM, 0, this->_m_fd);
if (ret != 0) {
printk("Error %d while creating socket pair\n", errno);
}
_m_gpio_dev = device_get_binding("GPIO_0");
if(_m_gpio_dev == NULL) {
printk("Failed to get GPIO device, error %d\n", errno);
}
for(int i = 0; i < 7; ++i)
gpio_init_callback(&gpio_cb, gpio_handler, BIT(pins[i]));
gpio_add_callback(_m_gpio_dev, &gpio_cb);
for(int i = 0; i < 7; ++i)
gpio_pin_interrupt_configure(_m_gpio_dev, pins[i], GPIO_INT_EDGE_FALLING);
doorlock::add_input(this);
}
long keypad_input::read_event() const {
long key;
int ret = read(this->_m_fd[1], &key, 1);
if(ret < 0) {
printk("Error while reading from file descriptor");
throw(errno);
}
return key;
}
int keypad_input::fd() const noexcept {
return this->_m_fd[1];
}
void keypad_input::process() {
memset(pressedKeys, 0, 13);
for(int i=0; i<7; i++)
gpio_pin_configure_dt(&kbd[i], GPIO_INPUT | GPIO_PULL_UP);
for(int row=0; row<4; row++) {
gpio_pin_configure_dt(&kbd[row_pins[row]], GPIO_OUTPUT);
gpio_pin_set_dt(&kbd[row_pins[row]], 0);
for(int col=0; col<3; col++) {
if(!gpio_pin_get_dt(&kbd[col_pins[col]]))
pressedKeys[strlen(pressedKeys)] = keys[row][col];
}
gpio_pin_configure_dt(&kbd[row_pins[row]], GPIO_INPUT | GPIO_PULL_UP);
}
int ret = write(this->_m_fd[0], pressedKeys, strlen(pressedKeys));
if(ret < 0) {
printk("Error while writing to file descriptor");
throw(errno);
}
}
keypad_input keypad_input;
#endif // CONFIG_KEYPAD_INPUT
#ifndef KEYPAD_INPUT_HPP
#define KEYPAD_INPUT_HPP
#ifdef CONFIG_KEYPAD_INPUT
#include "inputDevice.hpp"
#include <zephyr/drivers/gpio.h>
#include <zephyr/net/socket.h>
#include <zephyr/posix/unistd.h>
namespace {
const struct gpio_dt_spec kbd[7] = {
GPIO_DT_SPEC_GET_OR(DT_ALIAS(kbd0), gpios, {0}),
GPIO_DT_SPEC_GET_OR(DT_ALIAS(kbd1), gpios, {0}),
GPIO_DT_SPEC_GET_OR(DT_ALIAS(kbd2), gpios, {0}),
GPIO_DT_SPEC_GET_OR(DT_ALIAS(kbd3), gpios, {0}),
GPIO_DT_SPEC_GET_OR(DT_ALIAS(kbd4), gpios, {0}),
GPIO_DT_SPEC_GET_OR(DT_ALIAS(kbd5), gpios, {0}),
GPIO_DT_SPEC_GET_OR(DT_ALIAS(kbd6), gpios, {0})
};
const int col_pins[3] = { 6, 2, 1 };
const int row_pins[4] = { 5, 0, 4, 3 };
const char keys[4][3] = {
{ '1', '2', '3' },
{ '4', '5', '6' },
{ '7', '8', '9' },
{ '*', '0', '#' }
};
int pins[7];
char pressedKeys[13];
gpio_callback gpio_cb;
}
class keypad_input : public input_device {
public:
keypad_input();
long read_event() const;
int fd() const noexcept;
private:
int _m_fd[2];
const device *_m_gpio_dev;
void process();
struct handler_wrapper {
gpio_callback cb;
keypad_input *input;
handler_wrapper(gpio_callback& _cb, keypad_input *_input) {
cb = _cb;
input = _input;
}
};
handler_wrapper _m_wrapper;
friend void gpio_handler(const device* port,
gpio_callback *cb,
gpio_port_pins_t pins);
};
#endif // CONFIG_KEYPAD_INPUT
#endif // KEYPAD_INPUT_HPP
#ifdef CONFIG_SHELL_INPUT
#include "inputDevice.hpp"
#include "shellInput.hpp"
#include <zephyr/net/socket.h>
#include <zephyr/posix/unistd.h>
#include <zephyr/shell/shell.h>
shell_input::shell_input() {
int ret = zsock_socketpair(AF_UNIX, SOCK_STREAM, 0, this->_m_fd);
if (ret != 0) {
printk("Error %d while creating socket pair\n", errno);
throw(errno);
}
doorlock::add_input(this);
}
long shell_input::read_event() const {
char key;
int ret = read(this->_m_fd[1], &key, 1);
if(ret < 0) {
printk("Error while reading from file descriptor");
throw(errno);
}
return (long) key;
}
int shell_input::fd() const noexcept {
return this->_m_fd[1];
}
int shell_input::process(const shell *sh, int argc, char ** argv) {
return write(this->_m_fd[0], argv[2], strlen(argv[2]));
}
shell_input shell_input;
namespace {
int process_wrapper(const shell *sh, int argc, char **argv) {
return shell_input.process(sh, argc, argv);
}
}
SHELL_CMD_ARG_REGISTER(doorlock, NULL, "Pass input to doorlock. Usage: doorlock key #", process_wrapper, 3, 0);
#endif // CONFIG_SHELL_INPUT
#ifndef SHELL_INPUT_HPP
#define SHELL_INPUT_HPP
#ifdef CONFIG_SHELL_INPUT
#include "inputDevice.hpp"
#include <zephyr/net/socket.h>
#include <zephyr/posix/unistd.h>
#include <zephyr/shell/shell.h>
class shell_input : public input_device {
public:
shell_input();
long read_event() const;
int fd() const noexcept;
int process(const shell *sh, int argc, char **argv);
private:
int _m_fd[2];
};
#endif // CONFIG_SHELL_INPUT
#endif // SHELL_INPUT_HPP
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment