mirror of
https://github.com/sipeed/MaixCDK.git
synced 2026-11-09 17:04:38 -06:00
Merge pull request #48 from 916BGAI/maix_key
add long press and powerkey support, initialize all keys by default.
This commit is contained in:
@@ -22,10 +22,11 @@ namespace maix::peripheral::key
|
||||
enum Keys{
|
||||
KEY_NONE = 0x000,
|
||||
KEY_ESC = 0x001,
|
||||
KEY_POWER = 0x074,
|
||||
KEY_OK = 0x160,
|
||||
KEY_OPTION = 0x165,
|
||||
KEY_NEXT = 0x197,
|
||||
KEY_PREV = 0x19c
|
||||
KEY_PREV = 0x19c,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -33,8 +34,9 @@ namespace maix::peripheral::key
|
||||
* @maixpy maix.peripheral.key.State
|
||||
*/
|
||||
enum State{
|
||||
KEY_RELEASED = 0,
|
||||
KEY_PRESSED = 1,
|
||||
KEY_RELEASED = 0,
|
||||
KEY_PRESSED = 1,
|
||||
KEY_LONG_PRESSED = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -50,11 +52,14 @@ namespace maix::peripheral::key
|
||||
* callback will be called with args key(key.Keys) and value(key.State).
|
||||
* If set to null, you can get key value by read() function.
|
||||
* This callback called in a standalone thread, so you can block a while in callback, and you should be carefully when operate shared data.
|
||||
* @param open auto open device in constructor, if false, you need call open() to open device
|
||||
* @param open auto open device in constructor, if false, you need call open() to open device.
|
||||
* @param device Specifies the input device to use. The default initializes all keys,
|
||||
* for a specific device, provide the path (e.g., "/dev/input/device").
|
||||
* @param long_press_time The duration (in milliseconds) from pressing the key to triggering the long press event. Default is 2000ms.
|
||||
* @maixpy maix.peripheral.key.Key.__init__
|
||||
* @maixcdk maix.peripheral.key.Key.Key
|
||||
*/
|
||||
Key(std::function<void(int, int)> callback = nullptr, bool open = true);
|
||||
Key(std::function<void(int, int)> callback = nullptr, bool open = true, const string &device = "", int long_press_time = 2000);
|
||||
|
||||
~Key();
|
||||
|
||||
@@ -98,8 +103,17 @@ namespace maix::peripheral::key
|
||||
*/
|
||||
std::pair<int, int> read();
|
||||
|
||||
/**
|
||||
* @brief Sets and retrieves the key's long press time.
|
||||
* @param press_time The long press time to set for the key.
|
||||
* Setting it to 0 will disable the long press event.
|
||||
* @return int type, the current long press time for the key (in milliseconds).
|
||||
* @maixpy maix.peripheral.key.Key.long_press_time
|
||||
*/
|
||||
int long_press_time(int press_time = -1);
|
||||
|
||||
private:
|
||||
int _fd;
|
||||
std::vector<int> _fds;
|
||||
std::string _device;
|
||||
std::function<void(int, int)> _callback;
|
||||
void *_data;
|
||||
|
||||
@@ -57,8 +57,10 @@ namespace maix::peripheral::key
|
||||
public:
|
||||
thread::Thread *thread;
|
||||
int fd;
|
||||
int long_press_time;
|
||||
bool read_thread_exit;
|
||||
bool read_thread_need_exit;
|
||||
std::vector<int> fds;
|
||||
Key *key;
|
||||
std::function<void(int, int)> callback;
|
||||
};
|
||||
@@ -69,6 +71,7 @@ namespace maix::peripheral::key
|
||||
Port_Data *data = (Port_Data *)args;
|
||||
int key = 0;
|
||||
int value = 0;
|
||||
uint64_t tick = 0;
|
||||
// epoll wait key event
|
||||
struct epoll_event ev;
|
||||
int epoll_fd = epoll_create(1);
|
||||
@@ -79,12 +82,16 @@ namespace maix::peripheral::key
|
||||
return;
|
||||
}
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.fd = data->fd;
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, data->fd, &ev) < 0)
|
||||
{
|
||||
data->read_thread_exit = true;
|
||||
log::error("epoll_ctl add failed: %s", strerror(errno));
|
||||
return;
|
||||
for (int fd : data->fds) {
|
||||
if (fd > 0) {
|
||||
ev.data.fd = fd;
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
|
||||
log::error("epoll_ctl add failed for fd %d: %s", fd, strerror(errno));
|
||||
data->read_thread_exit = true;
|
||||
close(epoll_fd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
int fail_count = 0;
|
||||
while (!data->read_thread_need_exit && !app::need_exit())
|
||||
@@ -92,10 +99,17 @@ namespace maix::peripheral::key
|
||||
int nfds = epoll_wait(epoll_fd, &ev, 1, 200);
|
||||
if (nfds > 0)
|
||||
{
|
||||
data->fd = ev.data.fd;
|
||||
err::Err e = data->key->read(key, value);
|
||||
if (e == err::Err::ERR_NONE)
|
||||
{
|
||||
data->callback(key, value);
|
||||
// get ticks_ms when the key is pressed.
|
||||
if (key != 0 && value == 1) {
|
||||
tick = time::ticks_ms();
|
||||
} else if (key != 0 && value == 0) {
|
||||
tick = 0;
|
||||
}
|
||||
}
|
||||
else if (e != err::Err::ERR_NOT_READY)
|
||||
{
|
||||
@@ -108,18 +122,24 @@ namespace maix::peripheral::key
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// if long press event occurs, send KEY_LONG_PRESSED.
|
||||
if (tick != 0 && data->long_press_time !=0) {
|
||||
if ((int)(time::ticks_ms()- tick) >= data->long_press_time) {
|
||||
data->callback(key, State::KEY_LONG_PRESSED);
|
||||
tick = 0;
|
||||
}
|
||||
}
|
||||
time::sleep_ms(1);
|
||||
}
|
||||
log::info("read key thread exit");
|
||||
data->read_thread_exit = true;
|
||||
}
|
||||
|
||||
Key::Key(std::function<void(int, int)> callback, bool open)
|
||||
Key::Key(std::function<void(int, int)> callback, bool open, const string &device, int long_press_time)
|
||||
{
|
||||
if(_key_defult_listener)
|
||||
rm_default_listener();
|
||||
this->_callback = callback;
|
||||
this->_fd = -1;
|
||||
this->_data = nullptr;
|
||||
this->_device = "";
|
||||
Port_Data *data = new Port_Data();
|
||||
@@ -130,6 +150,7 @@ namespace maix::peripheral::key
|
||||
}
|
||||
data->thread = nullptr;
|
||||
data->fd = -1;
|
||||
data->long_press_time = long_press_time;
|
||||
data->read_thread_exit = false;
|
||||
data->read_thread_need_exit = false;
|
||||
data->key = this;
|
||||
@@ -169,37 +190,49 @@ namespace maix::peripheral::key
|
||||
|
||||
err::Err Key::open()
|
||||
{
|
||||
if (this->_fd > 0)
|
||||
if (this->_fds.size())
|
||||
{
|
||||
this->close();
|
||||
}
|
||||
bool is_open = false;
|
||||
if(!this->_device.empty())
|
||||
{
|
||||
this->_fd = ::open(_device.c_str(), O_RDONLY);
|
||||
if (this->_fd > 0)
|
||||
int fd = ::open(_device.c_str(), O_RDONLY);
|
||||
if (fd > 0) {
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
this->_fds.push_back(fd);
|
||||
is_open = true;
|
||||
}
|
||||
}
|
||||
if(!is_open)
|
||||
{
|
||||
this->_fd = ::open(KEY_DEVICE, O_RDONLY);
|
||||
if (this->_fd < 0)
|
||||
int fd = ::open(KEY_DEVICE, O_RDONLY);
|
||||
if (fd < 0)
|
||||
{
|
||||
this->_fd = ::open(KEY_DEVICE2, O_RDONLY);
|
||||
if (this->_fd < 0)
|
||||
{
|
||||
return err::Err::ERR_NOT_FOUND;
|
||||
std::vector<std::string> key_devices = {{KEY_DEVICE2}};
|
||||
for (const auto& device : key_devices) {
|
||||
int fd = ::open(device.c_str(), O_RDONLY);
|
||||
if (fd == -1) {
|
||||
log::error(("Failed to open device: " + device).c_str());
|
||||
this->_device = device;
|
||||
return err::Err::ERR_NOT_FOUND;
|
||||
}
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
this->_fds.push_back(fd);
|
||||
}
|
||||
} else {
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
this->_fds.push_back(fd);
|
||||
}
|
||||
}
|
||||
// set non-blocking
|
||||
int flags = fcntl(this->_fd, F_GETFL, 0);
|
||||
fcntl(this->_fd, F_SETFL, flags | O_NONBLOCK);
|
||||
if (this->_callback)
|
||||
{
|
||||
// new thread to read key
|
||||
Port_Data *data = (Port_Data *)this->_data;
|
||||
data->fd = this->_fd;
|
||||
data->fds = this->_fds;
|
||||
data->read_thread_exit = false;
|
||||
data->read_thread_need_exit = false;
|
||||
data->thread = new thread::Thread(_read_process, this->_data);
|
||||
@@ -210,14 +243,25 @@ namespace maix::peripheral::key
|
||||
|
||||
err::Err Key::read(int &key, int &value)
|
||||
{
|
||||
if (this->_fd < 0)
|
||||
Port_Data *data = (Port_Data *)this->_data;
|
||||
if (this->_fds.size() == 0)
|
||||
{
|
||||
return err::Err::ERR_NOT_OPEN;
|
||||
}
|
||||
struct input_event ev;
|
||||
int ret;
|
||||
while (1)
|
||||
{
|
||||
int ret = ::read(this->_fd, &ev, sizeof(struct input_event));
|
||||
if (this->_callback == nullptr) {
|
||||
for (int fd : this->_fds)
|
||||
{
|
||||
ret = ::read(fd, &ev, sizeof(struct input_event));
|
||||
if (ret > 0 && ev.code != 0)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ret = ::read(data->fd, &ev, sizeof(struct input_event));
|
||||
}
|
||||
if (ret < 0)
|
||||
{
|
||||
if(errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
@@ -261,22 +305,39 @@ namespace maix::peripheral::key
|
||||
{
|
||||
Port_Data *data = (Port_Data *)this->_data;
|
||||
data->read_thread_need_exit = true;
|
||||
if (this->_fd > 0)
|
||||
|
||||
bool err = false;
|
||||
for (int &fd : this->_fds)
|
||||
{
|
||||
int ret = ::close(this->_fd);
|
||||
this->_fd = -1;
|
||||
if (ret < 0)
|
||||
if (fd > 0)
|
||||
{
|
||||
return err::Err::ERR_IO;
|
||||
int ret = ::close(fd);
|
||||
if (ret < 0)
|
||||
{
|
||||
log::error("Failed to close fd %d: %s", fd, strerror(errno));
|
||||
err = true;
|
||||
}
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
this->_fd = -1;
|
||||
return err::Err::ERR_NONE;
|
||||
|
||||
this->_fds.clear();
|
||||
return err ? err::Err::ERR_IO : err::Err::ERR_NONE;
|
||||
}
|
||||
|
||||
bool Key::is_opened()
|
||||
{
|
||||
return this->_fd > 0;
|
||||
return this->_fds.size() > 0;
|
||||
}
|
||||
|
||||
int Key::long_press_time(int press_time)
|
||||
{
|
||||
Port_Data *data = (Port_Data *)this->_data;
|
||||
if (press_time < 0) {
|
||||
return data->long_press_time;
|
||||
} else {
|
||||
return data->long_press_time = press_time;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace maix::peripheral::key
|
||||
|
||||
@@ -11,16 +11,21 @@
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/uinput.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include "maix_app.hpp"
|
||||
#include "maix_log.hpp"
|
||||
#include "maix_i2c.hpp"
|
||||
|
||||
#define KEY_DEVICE "/dev/input/event_keys"
|
||||
#define KEY_DEVICE2 "/dev/input/event0"
|
||||
#define KEY_DEVICE0 "/dev/input/event0"
|
||||
#define KEY_DEVICE1 "/dev/input/powerkey"
|
||||
|
||||
static bool _key_defult_listener = false;
|
||||
static maix::peripheral::key::Key *_default_key = nullptr;
|
||||
static maix::peripheral::i2c::I2C *i2c_dev = nullptr;
|
||||
|
||||
static void on_key(int key, int state)
|
||||
{
|
||||
@@ -40,7 +45,7 @@ namespace maix::peripheral::key
|
||||
{
|
||||
if(_default_key)
|
||||
return;
|
||||
_default_key = new Key(on_key);
|
||||
_default_key = new Key(on_key, true, KEY_DEVICE0);
|
||||
_key_defult_listener = true;
|
||||
}
|
||||
|
||||
@@ -58,9 +63,14 @@ namespace maix::peripheral::key
|
||||
{
|
||||
public:
|
||||
thread::Thread *thread;
|
||||
int fd;
|
||||
thread::Thread *powerkey_thread;
|
||||
int fd, io_fd, uinput_fd;
|
||||
int long_press_time;
|
||||
bool read_thread_exit;
|
||||
bool read_thread_need_exit;
|
||||
bool powerkey_thread_exit;
|
||||
bool powerkey_thread_need_exit;
|
||||
std::vector<int> fds;
|
||||
Key *key;
|
||||
std::function<void(int, int)> callback;
|
||||
};
|
||||
@@ -71,6 +81,7 @@ namespace maix::peripheral::key
|
||||
Port_Data *data = (Port_Data *)args;
|
||||
int key = 0;
|
||||
int value = 0;
|
||||
uint64_t tick = 0;
|
||||
// epoll wait key event
|
||||
struct epoll_event ev;
|
||||
int epoll_fd = epoll_create(1);
|
||||
@@ -81,12 +92,16 @@ namespace maix::peripheral::key
|
||||
return;
|
||||
}
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.fd = data->fd;
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, data->fd, &ev) < 0)
|
||||
{
|
||||
data->read_thread_exit = true;
|
||||
log::error("epoll_ctl add failed: %s", strerror(errno));
|
||||
return;
|
||||
for (int fd : data->fds) {
|
||||
if (fd > 0) {
|
||||
ev.data.fd = fd;
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
|
||||
log::error("epoll_ctl add failed for fd %d: %s", fd, strerror(errno));
|
||||
data->read_thread_exit = true;
|
||||
close(epoll_fd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
int fail_count = 0;
|
||||
while (!data->read_thread_need_exit && !app::need_exit())
|
||||
@@ -94,10 +109,17 @@ namespace maix::peripheral::key
|
||||
int nfds = epoll_wait(epoll_fd, &ev, 1, 200);
|
||||
if (nfds > 0)
|
||||
{
|
||||
data->fd = ev.data.fd;
|
||||
err::Err e = data->key->read(key, value);
|
||||
if (e == err::Err::ERR_NONE)
|
||||
{
|
||||
data->callback(key, value);
|
||||
// get ticks_ms when the key is pressed.
|
||||
if (key != 0 && value == 1) {
|
||||
tick = time::ticks_ms();
|
||||
} else if (key != 0 && value == 0) {
|
||||
tick = 0;
|
||||
}
|
||||
}
|
||||
else if (e != err::Err::ERR_NOT_READY)
|
||||
{
|
||||
@@ -110,20 +132,185 @@ namespace maix::peripheral::key
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// if long press event occurs, send KEY_LONG_PRESSED.
|
||||
if (tick != 0 && data->long_press_time !=0) {
|
||||
if ((int)(time::ticks_ms()- tick) >= data->long_press_time) {
|
||||
data->callback(key, State::KEY_LONG_PRESSED);
|
||||
tick = 0;
|
||||
}
|
||||
}
|
||||
time::sleep_ms(1);
|
||||
}
|
||||
log::info("read key thread exit");
|
||||
data->read_thread_exit = true;
|
||||
}
|
||||
|
||||
Key::Key(std::function<void(int, int)> callback, bool open)
|
||||
static void _powerkey_process(void *args)
|
||||
{
|
||||
Port_Data *data = (Port_Data *)args;
|
||||
data->io_fd = open("/sys/class/gpio/gpio448/value", O_RDONLY);
|
||||
if (data->io_fd < 0) {
|
||||
data->powerkey_thread_exit = true;
|
||||
log::error("open gpio failed: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
struct epoll_event ev;
|
||||
int epoll_fd = epoll_create1(0);
|
||||
if (epoll_fd < 0)
|
||||
{
|
||||
data->powerkey_thread_exit = true;
|
||||
log::error("create epoll failed: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
ev.events = EPOLLPRI;
|
||||
ev.data.fd = data->io_fd;
|
||||
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, data->io_fd, &ev) < 0) {
|
||||
log::error("epoll_ctl add failed: %s", strerror(errno));
|
||||
data->powerkey_thread_exit = true;
|
||||
close(epoll_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[32];
|
||||
uint8_t i2c_data = 0xFF;
|
||||
struct input_event uinput_ev;
|
||||
static bool is_pressed = false;
|
||||
read(data->io_fd, buf, sizeof(buf));
|
||||
while (!data->powerkey_thread_need_exit && !app::need_exit())
|
||||
{
|
||||
int nfds = epoll_wait(epoll_fd, &ev, 1, 200);
|
||||
if (nfds > 0) {
|
||||
if (ev.events & EPOLLPRI) {
|
||||
lseek(data->io_fd, 0, SEEK_SET);
|
||||
read(data->io_fd, buf, sizeof(buf));
|
||||
|
||||
if (buf[0] == '0' && !is_pressed) {
|
||||
is_pressed = true;
|
||||
uinput_ev.type = EV_KEY;
|
||||
uinput_ev.code = KEY_POWER;
|
||||
uinput_ev.value = 1;
|
||||
gettimeofday(&uinput_ev.time, NULL);
|
||||
write(data->uinput_fd, &uinput_ev, sizeof(uinput_ev));
|
||||
log::debug("Key pressed.\n");
|
||||
} else if (buf[0] == '0' && is_pressed) {
|
||||
is_pressed = false;
|
||||
uinput_ev.type = EV_KEY;
|
||||
uinput_ev.code = KEY_POWER;
|
||||
uinput_ev.value = 0;
|
||||
gettimeofday(&uinput_ev.time, NULL);
|
||||
write(data->uinput_fd, &uinput_ev, sizeof(uinput_ev));
|
||||
log::debug("Key press detected.\n");
|
||||
}
|
||||
uinput_ev.type = EV_SYN;
|
||||
uinput_ev.code = SYN_REPORT;
|
||||
uinput_ev.value = 0;
|
||||
gettimeofday(&uinput_ev.time, NULL);
|
||||
write(data->uinput_fd, &uinput_ev, sizeof(uinput_ev));
|
||||
|
||||
if (i2c_dev->writeto_mem(0x34, 0x49, &i2c_data, 1) != 1) {
|
||||
log::error("clean pmu irq failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
time::sleep_ms(20);
|
||||
}
|
||||
log::info("powerkey thread exit");
|
||||
data->powerkey_thread_exit = true;
|
||||
}
|
||||
|
||||
static void _init_power_key(void *args)
|
||||
{
|
||||
uint8_t i2c_data = 0xFF;
|
||||
i2c_dev = new peripheral::i2c::I2C(4, i2c::Mode::MASTER);
|
||||
if (i2c_dev->writeto_mem(0x34, 0x49, &i2c_data, 1) != 1) {
|
||||
log::error("clean pmu irq failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fs::exists("/sys/class/gpio/gpio448")) {
|
||||
system("echo 448 > /sys/class/gpio/export");
|
||||
usleep(100000);
|
||||
system("echo \"in\" > /sys/class/gpio/gpio448/direction");
|
||||
system("echo \"falling\" > /sys/class/gpio/gpio448/edge");
|
||||
log::info("export axp2101 irq gpio success");
|
||||
}
|
||||
|
||||
Port_Data *data = (Port_Data *)args;
|
||||
data->uinput_fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
|
||||
if (data->uinput_fd < 0) {
|
||||
log::error("open /dev/uinput error");
|
||||
return;
|
||||
}
|
||||
|
||||
ioctl(data->uinput_fd, UI_SET_EVBIT, EV_KEY);
|
||||
ioctl(data->uinput_fd, UI_SET_KEYBIT, KEY_POWER);
|
||||
struct uinput_user_dev uidev;
|
||||
memset(&uidev, 0, sizeof(uidev));
|
||||
strncpy(uidev.name, "powerkey", UINPUT_MAX_NAME_SIZE);
|
||||
uidev.id.bustype = BUS_USB;
|
||||
uidev.id.vendor = 0x3346;
|
||||
uidev.id.product = 0x2333;
|
||||
uidev.id.version = 1;
|
||||
write(data->uinput_fd, &uidev, sizeof(uidev));
|
||||
ioctl(data->uinput_fd, UI_DEV_CREATE);
|
||||
|
||||
int max_event = -1;
|
||||
if (fs::exists(KEY_DEVICE1)) {
|
||||
fs::remove(KEY_DEVICE1);
|
||||
}
|
||||
|
||||
DIR* dir = opendir("/dev/input/");
|
||||
if (!dir) {
|
||||
log::error("failed to open /dev/input");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
struct dirent* ent;
|
||||
while ((ent = readdir(dir)) != nullptr) {
|
||||
std::string name(ent->d_name);
|
||||
if (name.find("event") == 0) {
|
||||
int eventNumber = std::stoi(name.substr(5));
|
||||
max_event = std::max(max_event, eventNumber);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
std::string targetPath = "/dev/input/event" + std::to_string(max_event);
|
||||
if (fs::symlink(targetPath.c_str(), KEY_DEVICE1) == -1) {
|
||||
log::error("%s symlink error.", KEY_DEVICE1);
|
||||
} else {
|
||||
log::info("%s symlink to %s.", KEY_DEVICE1, targetPath.c_str());
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
closedir(dir);
|
||||
log::error("Exception occurred: %s", e.what());
|
||||
}
|
||||
|
||||
data->powerkey_thread = new thread::Thread(_powerkey_process, data);
|
||||
data->powerkey_thread->detach();
|
||||
}
|
||||
|
||||
static void _deinit_power_key(void *args)
|
||||
{
|
||||
Port_Data *data = (Port_Data *)args;
|
||||
ioctl(data->uinput_fd, UI_DEV_DESTROY);
|
||||
close(data->uinput_fd);
|
||||
if (data->io_fd > 0) {
|
||||
close(data->io_fd);
|
||||
}
|
||||
fs::remove(KEY_DEVICE1);
|
||||
}
|
||||
|
||||
Key::Key(std::function<void(int, int)> callback, bool open, const string &device, int long_press_time)
|
||||
{
|
||||
if(_key_defult_listener)
|
||||
rm_default_listener();
|
||||
this->_callback = callback;
|
||||
this->_fd = -1;
|
||||
this->_data = nullptr;
|
||||
this->_device = "";
|
||||
this->_device = device;
|
||||
Port_Data *data = new Port_Data();
|
||||
this->_data = data;
|
||||
if (!this->_data)
|
||||
@@ -131,18 +318,31 @@ namespace maix::peripheral::key
|
||||
throw err::Exception(err::ERR_NO_MEM, "create key data failed");
|
||||
}
|
||||
data->thread = nullptr;
|
||||
data->powerkey_thread = nullptr;
|
||||
data->fd = -1;
|
||||
data->io_fd = -1;
|
||||
data->uinput_fd = -1;
|
||||
data->long_press_time = long_press_time;
|
||||
data->read_thread_exit = false;
|
||||
data->read_thread_need_exit = false;
|
||||
data->powerkey_thread_exit = false;
|
||||
data->powerkey_thread_need_exit = false;
|
||||
data->key = this;
|
||||
data->callback = callback;
|
||||
|
||||
if (sys::device_id() == "maixcam_pro" &&
|
||||
!fs::exists(KEY_DEVICE1) &&
|
||||
(device == "" || device == KEY_DEVICE1)) {
|
||||
log::info("%s: Init pmu power key.", sys::device_name().c_str());
|
||||
_init_power_key((void*)data);
|
||||
}
|
||||
|
||||
if (open)
|
||||
{
|
||||
err::Err e = this->open();
|
||||
if (e != err::ERR_NONE)
|
||||
{
|
||||
throw err::Exception(err::ERR_NOT_FOUND, std::string("Key device") + KEY_DEVICE + " not found");
|
||||
throw err::Exception(err::ERR_NOT_FOUND, std::string("Key device") + this->_device + " not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,44 +364,79 @@ namespace maix::peripheral::key
|
||||
delete data->thread;
|
||||
data->thread = nullptr;
|
||||
}
|
||||
if(data->powerkey_thread)
|
||||
{
|
||||
data->powerkey_thread_need_exit = true;
|
||||
log::info("wait powerkey thread exit");
|
||||
while (!data->powerkey_thread_exit)
|
||||
{
|
||||
time::sleep_ms(1);
|
||||
}
|
||||
delete data->powerkey_thread;
|
||||
data->powerkey_thread = nullptr;
|
||||
}
|
||||
if (fs::exists(KEY_DEVICE1)) {
|
||||
_deinit_power_key((void*)data);
|
||||
}
|
||||
delete data;
|
||||
this->_data = nullptr;
|
||||
}
|
||||
if (i2c_dev != nullptr) {
|
||||
delete i2c_dev;
|
||||
i2c_dev = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
err::Err Key::open()
|
||||
{
|
||||
if (this->_fd > 0)
|
||||
if (this->_fds.size())
|
||||
{
|
||||
this->close();
|
||||
}
|
||||
bool is_open = false;
|
||||
if(!this->_device.empty())
|
||||
{
|
||||
this->_fd = ::open(_device.c_str(), O_RDONLY);
|
||||
if (this->_fd > 0)
|
||||
int fd = ::open(_device.c_str(), O_RDONLY);
|
||||
if (fd > 0) {
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
this->_fds.push_back(fd);
|
||||
is_open = true;
|
||||
}
|
||||
}
|
||||
if(!is_open)
|
||||
{
|
||||
this->_fd = ::open(KEY_DEVICE, O_RDONLY);
|
||||
if (this->_fd < 0)
|
||||
int fd = ::open(KEY_DEVICE, O_RDONLY);
|
||||
if (fd < 0)
|
||||
{
|
||||
this->_fd = ::open(KEY_DEVICE2, O_RDONLY);
|
||||
if (this->_fd < 0)
|
||||
{
|
||||
return err::Err::ERR_NOT_FOUND;
|
||||
std::vector<std::string> key_devices;
|
||||
if (sys::device_id() == "maixcam_pro") {
|
||||
key_devices = {KEY_DEVICE0, KEY_DEVICE1};
|
||||
} else {
|
||||
key_devices = {KEY_DEVICE0};
|
||||
}
|
||||
for (const auto& device : key_devices) {
|
||||
int fd = ::open(device.c_str(), O_RDONLY);
|
||||
if (fd == -1) {
|
||||
log::error(("Failed to open device: " + device).c_str());
|
||||
this->_device = device;
|
||||
return err::Err::ERR_NOT_FOUND;
|
||||
}
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
this->_fds.push_back(fd);
|
||||
}
|
||||
} else {
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
this->_fds.push_back(fd);
|
||||
}
|
||||
}
|
||||
// set non-blocking
|
||||
int flags = fcntl(this->_fd, F_GETFL, 0);
|
||||
fcntl(this->_fd, F_SETFL, flags | O_NONBLOCK);
|
||||
if (this->_callback)
|
||||
{
|
||||
// new thread to read key
|
||||
Port_Data *data = (Port_Data *)this->_data;
|
||||
data->fd = this->_fd;
|
||||
data->fds = this->_fds;
|
||||
data->read_thread_exit = false;
|
||||
data->read_thread_need_exit = false;
|
||||
data->thread = new thread::Thread(_read_process, this->_data);
|
||||
@@ -212,14 +447,25 @@ namespace maix::peripheral::key
|
||||
|
||||
err::Err Key::read(int &key, int &value)
|
||||
{
|
||||
if (this->_fd < 0)
|
||||
Port_Data *data = (Port_Data *)this->_data;
|
||||
if (this->_fds.size() == 0)
|
||||
{
|
||||
return err::Err::ERR_NOT_OPEN;
|
||||
}
|
||||
struct input_event ev;
|
||||
int ret;
|
||||
while (1)
|
||||
{
|
||||
int ret = ::read(this->_fd, &ev, sizeof(struct input_event));
|
||||
if (this->_callback == nullptr) {
|
||||
for (int fd : this->_fds)
|
||||
{
|
||||
ret = ::read(fd, &ev, sizeof(struct input_event));
|
||||
if (ret > 0 && ev.code != 0)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ret = ::read(data->fd, &ev, sizeof(struct input_event));
|
||||
}
|
||||
if (ret < 0)
|
||||
{
|
||||
if(errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
@@ -266,22 +512,39 @@ namespace maix::peripheral::key
|
||||
{
|
||||
Port_Data *data = (Port_Data *)this->_data;
|
||||
data->read_thread_need_exit = true;
|
||||
if (this->_fd > 0)
|
||||
|
||||
bool err = false;
|
||||
for (int &fd : this->_fds)
|
||||
{
|
||||
int ret = ::close(this->_fd);
|
||||
this->_fd = -1;
|
||||
if (ret < 0)
|
||||
if (fd > 0)
|
||||
{
|
||||
return err::Err::ERR_IO;
|
||||
int ret = ::close(fd);
|
||||
if (ret < 0)
|
||||
{
|
||||
log::error("Failed to close fd %d: %s", fd, strerror(errno));
|
||||
err = true;
|
||||
}
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
this->_fd = -1;
|
||||
return err::Err::ERR_NONE;
|
||||
|
||||
this->_fds.clear();
|
||||
return err ? err::Err::ERR_IO : err::Err::ERR_NONE;
|
||||
}
|
||||
|
||||
bool Key::is_opened()
|
||||
{
|
||||
return this->_fd > 0;
|
||||
return this->_fds.size() > 0;
|
||||
}
|
||||
|
||||
int Key::long_press_time(int press_time)
|
||||
{
|
||||
Port_Data *data = (Port_Data *)this->_data;
|
||||
if (press_time < 0) {
|
||||
return data->long_press_time;
|
||||
} else {
|
||||
return data->long_press_time = press_time;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace maix::peripheral::key
|
||||
|
||||
Reference in New Issue
Block a user