project for a (works on my devices) demo
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.o
|
||||
*.exe
|
||||
*.so.*
|
||||
old/
|
||||
3
AARCH64_BUILD/variables.mk
Normal file
3
AARCH64_BUILD/variables.mk
Normal file
@@ -0,0 +1,3 @@
|
||||
export COMPILER=aarch64-buildroot-linux-gnu-gcc
|
||||
|
||||
export L_EVDEV=-L ../../resources/libraries/
|
||||
1
IA64_BUILD/libmydevicehandler.so
Symbolic link
1
IA64_BUILD/libmydevicehandler.so
Symbolic link
@@ -0,0 +1 @@
|
||||
/home/beno/Desktop/rg552_minimal_dev_env/WAYLAND/demos/my_device_handler/IA64_BUILD/libmydevicehandler.so.0
|
||||
3
IA64_BUILD/variables.mk
Normal file
3
IA64_BUILD/variables.mk
Normal file
@@ -0,0 +1,3 @@
|
||||
export COMPILER=gcc
|
||||
|
||||
export L_EVDEV=-L /usr/lib/x86_64-linux-gnu/
|
||||
79
Makefile
Normal file
79
Makefile
Normal file
@@ -0,0 +1,79 @@
|
||||
include external.mk
|
||||
include ${ARCH}_BUILD/variables.mk
|
||||
|
||||
GENERIC_TARGET_DEMO=main.exe #will create a demo for testing the functionalities implemented by the lib
|
||||
GENERIC_TARGET_LIB=libmydevicehandler.so.0 #will create a shared lib
|
||||
|
||||
BUILD_DIR=${PWD_D}${ARCH}_BUILD
|
||||
|
||||
LIBRARY_OBJECTS=${BUILD_DIR}/my_device_handler.o ${BUILD_DIR}/registries.o ${BUILD_DIR}/registries_items.o ${BUILD_DIR}/events.o
|
||||
OBJECTS=${BUILD_DIR}/main.o ${LIBRARY_OBJECTS}
|
||||
|
||||
|
||||
all:
|
||||
make ${GENERIC_TARGET_LIB}
|
||||
make ${BUILD_DIR}/${GENERIC_TARGET_DEMO}
|
||||
|
||||
${GENERIC_TARGET_LIB} : ${LIBRARY_OBJECTS}
|
||||
${COMPILER} \
|
||||
-fPIC \
|
||||
-shared \
|
||||
-Wl,-soname,${GENERIC_TARGET_LIB} \
|
||||
-o ${BUILD_DIR}/${GENERIC_TARGET_LIB} \
|
||||
${L_EVDEV} \
|
||||
${LIBRARY_OBJECTS} \
|
||||
${LINK_LIBS}
|
||||
|
||||
${BUILD_DIR}/${GENERIC_TARGET_DEMO} : ${OBJECTS}
|
||||
${COMPILER} \
|
||||
-fPIC \
|
||||
-o ${BUILD_DIR}/${GENERIC_TARGET_DEMO} \
|
||||
${L_EVDEV} \
|
||||
${OBJECTS} \
|
||||
${LINK_LIBS}
|
||||
|
||||
# yup, -pthread needs to be used even in compilation
|
||||
${BUILD_DIR}/main.o : main.c
|
||||
${COMPILER} \
|
||||
-fPIC \
|
||||
-o ${BUILD_DIR}/main.o \
|
||||
-c ${C_FLAGS} \
|
||||
main.c
|
||||
|
||||
${BUILD_DIR}/my_device_handler.o : my_device_handler.c my_device_handler.h registries.h events.h
|
||||
${COMPILER} \
|
||||
-fPIC \
|
||||
-o ${BUILD_DIR}/my_device_handler.o \
|
||||
-c ${C_FLAGS} \
|
||||
-pthread \
|
||||
my_device_handler.c
|
||||
|
||||
|
||||
${BUILD_DIR}/registries.o : registries.c registries.h registries_items.h
|
||||
${COMPILER} \
|
||||
-fPIC \
|
||||
-o ${BUILD_DIR}/registries.o \
|
||||
-c ${C_FLAGS} \
|
||||
registries.c
|
||||
|
||||
${BUILD_DIR}/registries_items.o : registries_items.c registries_items.h threading.h
|
||||
${COMPILER} \
|
||||
-fPIC \
|
||||
-o ${BUILD_DIR}/registries_items.o \
|
||||
-c ${C_FLAGS} \
|
||||
${I_EVDEV} \
|
||||
-pthread \
|
||||
registries_items.c
|
||||
|
||||
${BUILD_DIR}/events.o : events.c events.h
|
||||
${COMPILER} \
|
||||
-fPIC \
|
||||
-o ${BUILD_DIR}/events.o \
|
||||
-c ${C_FLAGS} \
|
||||
events.c
|
||||
|
||||
|
||||
.phony : clean
|
||||
|
||||
clean :
|
||||
-rm ${OBJECTS} ${TARGET} ${GENERIC_TARGET_DEMO}
|
||||
91
README.md
91
README.md
@@ -1,74 +1,39 @@
|
||||
# my_device_handler
|
||||
|
||||
A library for reading Event Devices (EVDEVs).
|
||||
|
||||
|
||||
## Getting started
|
||||
|
||||
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
|
||||
|
||||
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
|
||||
|
||||
## Add your files
|
||||
|
||||
* [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
|
||||
* [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command:
|
||||
|
||||
```
|
||||
cd existing_repo
|
||||
git remote add origin http://localhost/root/my_device_handler.git
|
||||
git branch -M main
|
||||
git push -uf origin main
|
||||
```
|
||||
|
||||
## Integrate with your tools
|
||||
|
||||
* [Set up project integrations](http://localhost/root/my_device_handler/-/settings/integrations)
|
||||
|
||||
## Collaborate with your team
|
||||
|
||||
* [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
|
||||
* [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
|
||||
* [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
|
||||
* [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
||||
* [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/)
|
||||
|
||||
## Test and Deploy
|
||||
|
||||
Use the built-in continuous integration in GitLab.
|
||||
|
||||
* [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/)
|
||||
* [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
|
||||
* [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
|
||||
* [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
|
||||
* [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
|
||||
|
||||
***
|
||||
|
||||
# Editing this README
|
||||
|
||||
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
|
||||
|
||||
## Suggestions for a good README
|
||||
|
||||
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
|
||||
|
||||
## Name
|
||||
Choose a self-explaining name for your project.
|
||||
|
||||
## Description
|
||||
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
|
||||
|
||||
A library for reading Event Devices (EVDEVs), the devices usually found under /dev/
|
||||
uses the library underlying libinput, libevdev, which is less "picky" about the kind of devices
|
||||
|
||||
|
||||
## Badges
|
||||
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
|
||||
|
||||
## Visuals
|
||||
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
||||
***
|
||||
|
||||
## Installation
|
||||
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
|
||||
|
||||
## Usage
|
||||
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
|
||||
|
||||
int device_handler_init();
|
||||
sets up the library.
|
||||
returns 0 ( setup error code )
|
||||
|
||||
int device_handler_add( const char *path);
|
||||
int device_handler_remove( const char *path);
|
||||
add/remove an event-device to the list of listened.
|
||||
return 0
|
||||
|
||||
int device_handler_poll_events( device_event** events);
|
||||
copies all the gathered device events into a new array at the given address and returns the item count.
|
||||
|
||||
void device_handler_destroy_events( device_event *events);
|
||||
frees the array used for copying device events, usually before its use for another copy
|
||||
|
||||
|
||||
## Support
|
||||
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
|
||||
@@ -77,17 +42,21 @@ Tell people where they can go to for help. It can be any combination of an issue
|
||||
If you have ideas for releases in the future, it is a good idea to list them in the README.
|
||||
|
||||
## Contributing
|
||||
State if you are open to contributions and what your requirements are for accepting them.
|
||||
Fully open to contributions.
|
||||
What requirements?
|
||||
|
||||
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
|
||||
|
||||
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
|
||||
|
||||
## Authors and acknowledgment
|
||||
Show your appreciation to those who have contributed to the project.
|
||||
Luca Benini [beno]
|
||||
```
|
||||
your name here
|
||||
```
|
||||
|
||||
## License
|
||||
For open source projects, say how it is licensed.
|
||||
MA CHE NE SAPPIAMO ANCORA
|
||||
|
||||
## Project status
|
||||
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
||||
se sei stanco, scrivilo in cima che qualcuno raccoglie il testimone
|
||||
|
||||
74
events.c
Normal file
74
events.c
Normal file
@@ -0,0 +1,74 @@
|
||||
#include "events.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
events_queue *global_queue;
|
||||
|
||||
void global_queue_init(){
|
||||
global_queue = events_queue_new();
|
||||
}
|
||||
|
||||
|
||||
events_queue* events_queue_new(){
|
||||
events_queue* eq = malloc( sizeof( events_queue));
|
||||
eq->headptr = malloc( sizeof( struct event_list_head));
|
||||
init_events_queue( eq);
|
||||
return eq;
|
||||
}
|
||||
|
||||
void init_events_queue( events_queue *queue){
|
||||
SLIST_INIT( queue->headptr);
|
||||
queue->events_last = NULL;
|
||||
queue->count = 0;
|
||||
}
|
||||
|
||||
void add_to_events_queue( events_queue *queue, device_event* event){
|
||||
event_list_entry* entry = malloc( sizeof( event_list_entry));
|
||||
|
||||
entry->event = event;
|
||||
|
||||
if(SLIST_EMPTY( queue->headptr)){
|
||||
SLIST_INSERT_HEAD( queue->headptr, entry, entries);
|
||||
}
|
||||
else{
|
||||
SLIST_INSERT_AFTER( queue->events_last, entry, entries);
|
||||
}
|
||||
queue->events_last = entry;
|
||||
queue->count++;
|
||||
}
|
||||
|
||||
void add_all_to_global_queue( events_queue *queue){
|
||||
while (!SLIST_EMPTY( queue->headptr)) { /* List Deletion. */
|
||||
event_list_entry *list_entry = SLIST_FIRST( queue->headptr);
|
||||
add_to_events_queue( global_queue, list_entry->event);
|
||||
SLIST_REMOVE_HEAD( queue->headptr, entries);
|
||||
free(list_entry);
|
||||
}
|
||||
init_events_queue( queue);
|
||||
}
|
||||
|
||||
int drain_events_into( device_event** events){
|
||||
|
||||
event_list_entry *list_entry;
|
||||
device_event *current;
|
||||
int index = 0;
|
||||
int count = global_queue->count;
|
||||
|
||||
if( count){
|
||||
*events = malloc( count * sizeof( device_event));
|
||||
|
||||
while ( !SLIST_EMPTY( global_queue->headptr)) {
|
||||
list_entry = SLIST_FIRST( global_queue->headptr);
|
||||
current = list_entry->event;
|
||||
SLIST_REMOVE_HEAD( global_queue->headptr, entries);
|
||||
(*events)[index++] = *current;
|
||||
free(list_entry);
|
||||
}
|
||||
/*init_events_queue */
|
||||
init_events_queue( global_queue);
|
||||
}
|
||||
else{
|
||||
*events = NULL;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
45
events.h
Normal file
45
events.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/queue.h>
|
||||
|
||||
/*
|
||||
emulating the structure from
|
||||
/usr/aarch64-linux-gnu/include/linux/input.h
|
||||
or
|
||||
/usr/include/linux/input.h
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned short type;
|
||||
unsigned short code;
|
||||
unsigned int value;
|
||||
} device_event;
|
||||
|
||||
|
||||
|
||||
typedef struct entry {
|
||||
device_event *event;
|
||||
SLIST_ENTRY(entry) entries; /* Singly linked List. */
|
||||
} event_list_entry;
|
||||
|
||||
SLIST_HEAD(event_list_head, entry);
|
||||
|
||||
|
||||
|
||||
typedef struct {
|
||||
struct event_list_head *headptr;
|
||||
event_list_entry *events_last;
|
||||
int count;
|
||||
} events_queue;
|
||||
|
||||
|
||||
void global_queue_init();
|
||||
|
||||
events_queue* events_queue_new();
|
||||
|
||||
void init_events_queue( events_queue *queue);
|
||||
|
||||
void add_to_events_queue( events_queue *queue, device_event* event);
|
||||
|
||||
void add_all_to_global_queue( events_queue *queue);
|
||||
|
||||
int drain_events_into( device_event** events);
|
||||
14
external.mk
Normal file
14
external.mk
Normal file
@@ -0,0 +1,14 @@
|
||||
export PWD_D=/here/projects/my_device_handler/
|
||||
|
||||
# ...
|
||||
# undefined reference to symbol '__stack_chk_guard@@GLIBC_2.17'
|
||||
# ...
|
||||
# error adding symbols: DSO missing from command line
|
||||
#
|
||||
# FIX BY INCLUDING FLAG
|
||||
# -fno-stack-protector
|
||||
export C_FLAGS=-ansi -Werror -Wall -Wpedantic -fPIC -O2 -g -fno-stack-protector -std=c99
|
||||
|
||||
export LINK_LIBS=-levdev -lpthread
|
||||
|
||||
export I_EVDEV=-I ../../resources/sources/libevdev-1.13.4/libevdev
|
||||
63
main.c
Normal file
63
main.c
Normal file
@@ -0,0 +1,63 @@
|
||||
#include <stdio.h>
|
||||
#include "my_device_handler.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
int main( int argc, char **argv){
|
||||
/* event[0|3] on RG552
|
||||
event[4|5] on MSI
|
||||
*/
|
||||
const char *dev_A = "/dev/input/event4"; // 3
|
||||
const char *dev_B = "/dev/input/event5"; // 0
|
||||
|
||||
device_handler_init();
|
||||
/*
|
||||
|
||||
|
||||
list_devices();
|
||||
*/
|
||||
printf("starting application\n");
|
||||
device_handler_add( dev_A);
|
||||
printf("added one of the two devices\n");
|
||||
device_handler_add( dev_B);
|
||||
|
||||
printf("added the two devices\n");
|
||||
|
||||
printf("extra 1\n");
|
||||
device_handler_add( dev_A);
|
||||
printf("extra 2\n");
|
||||
device_handler_add( dev_A);
|
||||
printf("extra 3\n");
|
||||
device_handler_remove( dev_B);
|
||||
printf("extra 4\n");
|
||||
device_handler_remove( dev_A);
|
||||
printf("extra 5\n");
|
||||
device_handler_add( dev_B);
|
||||
printf("extra 6\n");
|
||||
device_handler_add( dev_B);
|
||||
printf("extra 7\n");
|
||||
device_handler_add( dev_A);
|
||||
|
||||
|
||||
printf("repeated device add\n");
|
||||
|
||||
/*
|
||||
list_devices();
|
||||
*/
|
||||
|
||||
while( 1){
|
||||
device_event *events, current;
|
||||
int count = device_handler_poll_events( &events);
|
||||
int i;
|
||||
|
||||
for( i = 0; i < count; i++){
|
||||
current = events[i];
|
||||
printf("%d %d %d\n", current.type, current.code, current.value);
|
||||
}
|
||||
|
||||
device_handler_destroy_events( events);
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
140
my_device_handler.c
Normal file
140
my_device_handler.c
Normal file
@@ -0,0 +1,140 @@
|
||||
#include "registries_items.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#include "registries.h"
|
||||
#include "my_device_handler.h"
|
||||
|
||||
void reinitialize_all_devices_wrote_barrier( int childCount){
|
||||
int anyerror = pthread_barrier_init( &all_devices_wrote_barrier, NULL, 1 + childCount);
|
||||
if(anyerror){
|
||||
fprintf( stderr, "all_devices_wrote_barrier reinitialization error\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void reinitialize_devices_write_new_round_barrier( int childCount){
|
||||
int anyerror = pthread_barrier_init( &devices_write_new_round_barrier, NULL, 1 + childCount);
|
||||
if(anyerror){
|
||||
fprintf( stderr, "devices_write_new_round_barrier reinitialization error\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int device_handler_init(){
|
||||
global_queue_init();
|
||||
reinitialize_all_devices_wrote_barrier(0);
|
||||
reinitialize_devices_write_new_round_barrier(0);
|
||||
initialize_device_added_flag_barrier();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_handler_add( const char *path){
|
||||
if( is_listed_as_active( path)){ /* already enlisted */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( is_listed_for_insertion( path)){ /* already requested the insertion */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( is_listed_for_removal( path)){ /* undo the removal request */
|
||||
remove_from_update_list( path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
add_to_update_list( path, INSERT); /* request insertion */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int device_handler_remove( const char *path){
|
||||
printf("go with remove device\n");
|
||||
if( is_listed_for_removal( path)){ /* already requested the removal */
|
||||
printf("-> was alredy listed for removal\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( is_listed_for_insertion( path)){ /* undo the insertion request */
|
||||
printf("-> was listed for insertion, remove request\n");
|
||||
remove_from_update_list( path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( ! is_listed_as_active( path)){ /* is not enlisted anywhere */
|
||||
printf("-> is not listed as active\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("-> have to enlist for removal\n");
|
||||
add_to_update_list( path, REMOVE); /* request removal */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* each device thread
|
||||
takes the mutex, writes its events, releases
|
||||
then reaches the all_wrote barrier
|
||||
then reaches the new_round barrier
|
||||
|
||||
|
||||
the main thread instead
|
||||
reaches the all_wrote barrier, releasing it
|
||||
reads all the events (dispatching them in some way or another)
|
||||
gets the count of devices waiting to be added and the count of those waiting to be removed
|
||||
if there are pending additions and/or removals
|
||||
calculate newChildCount
|
||||
if newChildCount != current
|
||||
recreates the all_wrote barrier with a count of (1 [itself] + newChildCount )
|
||||
adds all the devices waiting to be added, same for removals
|
||||
( placed here for preventing actions from the device threads listed for removal )
|
||||
reaches the new_round barrier, releasing it
|
||||
if newChildCount != current
|
||||
recreates the new_round barrier with a count of (1 [itself] + newChildCount )
|
||||
( the device list update was here, but this could have caused the lock on the first barrier of some of the device theads listed for removal,
|
||||
it is unspecified how the barrier behaves upon the destruction of a waiting thread
|
||||
they could have also stored some new event, causing minor inconsistencies
|
||||
)
|
||||
*/
|
||||
|
||||
int device_handler_poll_events( device_event** events){
|
||||
int currentChildCount;
|
||||
int update_delta;
|
||||
int newChildCount;
|
||||
|
||||
int events_count;
|
||||
|
||||
currentChildCount = active_list_count();
|
||||
|
||||
pthread_barrier_wait( &all_devices_wrote_barrier);
|
||||
|
||||
/* remove events from queue */
|
||||
events_count = drain_events_into( events);
|
||||
|
||||
update_delta = update_list_get_delta();
|
||||
newChildCount = currentChildCount + update_delta;
|
||||
|
||||
if( newChildCount != currentChildCount){
|
||||
reinitialize_all_devices_wrote_barrier( newChildCount);
|
||||
}
|
||||
|
||||
/* here update the devices list */
|
||||
update_list_drain_into_active();
|
||||
|
||||
pthread_barrier_wait( &devices_write_new_round_barrier);
|
||||
|
||||
if( newChildCount != currentChildCount){
|
||||
reinitialize_devices_write_new_round_barrier( newChildCount);
|
||||
currentChildCount = newChildCount;
|
||||
}
|
||||
|
||||
/* return the number of events*/
|
||||
return events_count;
|
||||
}
|
||||
|
||||
|
||||
void device_handler_destroy_events( device_event *events){
|
||||
/* no need for items destruction (they are static objects) */
|
||||
free( events);
|
||||
}
|
||||
11
my_device_handler.h
Normal file
11
my_device_handler.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "events.h"
|
||||
|
||||
int device_handler_init();
|
||||
|
||||
int device_handler_add( const char *path);
|
||||
|
||||
int device_handler_remove( const char *path);
|
||||
|
||||
int device_handler_poll_events( device_event** events);
|
||||
|
||||
void device_handler_destroy_events( device_event *events);
|
||||
363
registries.c
Normal file
363
registries.c
Normal file
@@ -0,0 +1,363 @@
|
||||
#define _GNU_SOURCE /* tdestroy */
|
||||
|
||||
#include "registries_items.h"
|
||||
#include "registries.h"
|
||||
|
||||
#include <search.h> /* entry registry implemented with a tree */
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int delta_counter;
|
||||
int active_counter;
|
||||
|
||||
/**
|
||||
|
||||
UNDER NO CIRCUMSTANCE TRY TO USE tfind
|
||||
IT'S BROKEN ( passes the wrong arguments to the comparison function: one of the two is passed already dereferenced)
|
||||
*/
|
||||
|
||||
/* TSEARCH(3) tree root */
|
||||
static void *active_registry = NULL;
|
||||
static void *pending_registry = NULL;
|
||||
|
||||
|
||||
/* all the is_listed_* functions could be simplified by the use of "tfind"
|
||||
but tfind is broken... (wrong description in manual)
|
||||
*/
|
||||
|
||||
static int
|
||||
active_entries_compare( const void *pa, const void *pb)
|
||||
{
|
||||
int comparison;
|
||||
active_registry_item *item1 = (active_registry_item*) pa;
|
||||
active_registry_item *item2 = (active_registry_item*) pb;
|
||||
|
||||
comparison = strcmp( item1->path, item2->path);
|
||||
|
||||
if ( 0 > comparison)
|
||||
return -1;
|
||||
|
||||
if ( 0 < comparison)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int is_listed_as_active( const char *path){
|
||||
int found = 0;
|
||||
|
||||
active_registry_item **tnode;
|
||||
active_registry_item *item = active_registry_item_dummy_new( path); /* not a str, but a pointer to str */
|
||||
|
||||
tnode = tsearch( item, &active_registry, active_entries_compare);
|
||||
if( *tnode == item){
|
||||
tdelete( item, &active_registry, active_entries_compare);
|
||||
found = 0;
|
||||
}
|
||||
else{
|
||||
found = 1;
|
||||
}
|
||||
|
||||
active_registry_item_dummy_destroy( item);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static void drain_list_item(void *nodedata){
|
||||
pending_registry_request *req = (pending_registry_request*) nodedata;
|
||||
if( req->type == INSERT){
|
||||
add_to_active( req->path);
|
||||
}
|
||||
else{
|
||||
remove_from_active( req->path);
|
||||
}
|
||||
pending_registry_request_destroy(req);
|
||||
}
|
||||
|
||||
void update_list_drain_into_active(){
|
||||
tdestroy( pending_registry, drain_list_item);
|
||||
pending_registry = NULL;
|
||||
}
|
||||
|
||||
void add_to_active( const char *path){
|
||||
active_registry_item **tnode;
|
||||
active_registry_item *item = active_registry_item_new( path);
|
||||
|
||||
tnode = tsearch( item, &active_registry, active_entries_compare);
|
||||
|
||||
if( *tnode != item){
|
||||
fprintf( stderr, "inconsistency during insertion of path in active devices list\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void remove_from_active( const char *path){
|
||||
|
||||
active_registry_item *dummy_item, **holding_node, *true_item;
|
||||
|
||||
dummy_item = active_registry_item_dummy_new( path);
|
||||
|
||||
holding_node = tsearch( dummy_item, &active_registry, active_entries_compare);
|
||||
|
||||
if(*holding_node == dummy_item){
|
||||
fprintf( stderr, "inconsistency during removal of path in active devices list\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
true_item = *holding_node;
|
||||
|
||||
tdelete( dummy_item, &active_registry, active_entries_compare);
|
||||
|
||||
active_registry_item_dummy_destroy( dummy_item);
|
||||
active_registry_item_destroy( true_item);
|
||||
}
|
||||
|
||||
static void
|
||||
active_counting_action( const void *nodep, VISIT which, int depth)
|
||||
{
|
||||
switch ( which) {
|
||||
case preorder:
|
||||
break;
|
||||
case postorder:
|
||||
active_counter++;
|
||||
break;
|
||||
case endorder:
|
||||
break;
|
||||
case leaf:
|
||||
active_counter++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int active_list_count(){
|
||||
active_counter = 0;
|
||||
twalk( active_registry, active_counting_action);
|
||||
return active_counter;
|
||||
}
|
||||
|
||||
static int
|
||||
pending_entries_compare( const void *pa, const void *pb)
|
||||
{
|
||||
|
||||
pending_registry_request *req1 = ( pending_registry_request*) pa;
|
||||
pending_registry_request *req2 = ( pending_registry_request*) pb;
|
||||
|
||||
int comparison;
|
||||
|
||||
comparison = strcmp( req1->path, req2->path);
|
||||
|
||||
if ( 0 > comparison)
|
||||
return -1;
|
||||
|
||||
if ( 0 < comparison)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
INTERNAL USE ONLY
|
||||
for querying the presence or absence of entries
|
||||
*/
|
||||
pending_registry_request* get_from_pending_registry( const char *path){
|
||||
int found = 0;
|
||||
|
||||
pending_registry_request **tnode;
|
||||
|
||||
pending_registry_request *req = pending_registry_request_new( path);
|
||||
|
||||
tnode = tsearch( req, &pending_registry, pending_entries_compare);
|
||||
|
||||
if( *tnode == req){
|
||||
tdelete( req, &pending_registry, pending_entries_compare);
|
||||
found = 0;
|
||||
}
|
||||
else{
|
||||
found = 1;
|
||||
}
|
||||
|
||||
pending_registry_request_destroy( req);
|
||||
|
||||
if( ! found){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return *tnode;
|
||||
}
|
||||
|
||||
int is_listed_for_removal( const char *path){
|
||||
pending_registry_request *req;
|
||||
|
||||
req = get_from_pending_registry( path);
|
||||
if( NULL == req){
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( req->type == REMOVE){
|
||||
return 1;
|
||||
}
|
||||
else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int is_listed_for_insertion( const char *path){
|
||||
|
||||
pending_registry_request *req = get_from_pending_registry( path);
|
||||
|
||||
if( NULL == req){
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( req->type == INSERT){
|
||||
return 1;
|
||||
}
|
||||
else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void add_to_update_list( const char *path, update_request_type type){
|
||||
pending_registry_request **tnode;
|
||||
|
||||
pending_registry_request *req = pending_registry_request_new( path);
|
||||
req->type = type;
|
||||
|
||||
tnode = tsearch( req, &pending_registry, pending_entries_compare);
|
||||
|
||||
if( *tnode != req){
|
||||
pending_registry_request_destroy( req);
|
||||
}
|
||||
}
|
||||
|
||||
void remove_from_update_list( const char *path){
|
||||
pending_registry_request **tnode, *found, *req;
|
||||
|
||||
/*type field is not used in comparison*/
|
||||
req = pending_registry_request_new( path);
|
||||
|
||||
/* all this mess just because i can't use tfind */
|
||||
tnode = tsearch( req, &pending_registry, pending_entries_compare);
|
||||
if( *tnode == req){
|
||||
printf("inconsistency during removal from update list\n");
|
||||
exit( 1);
|
||||
}
|
||||
else{
|
||||
found = *tnode;
|
||||
}
|
||||
|
||||
tnode = tdelete( found, &pending_registry, pending_entries_compare);
|
||||
|
||||
pending_registry_request_destroy( req);
|
||||
pending_registry_request_destroy( found);
|
||||
}
|
||||
|
||||
static void
|
||||
delta_counting_action(const void *nodep, VISIT which, int depth)
|
||||
{
|
||||
pending_registry_request *datap;
|
||||
switch ( which) {
|
||||
case preorder:
|
||||
break;
|
||||
case postorder:
|
||||
datap = *( pending_registry_request**) nodep;
|
||||
if( datap->type == INSERT){
|
||||
delta_counter++;
|
||||
}
|
||||
else{
|
||||
delta_counter--;
|
||||
}
|
||||
break;
|
||||
case endorder:
|
||||
break;
|
||||
case leaf:
|
||||
datap = *( pending_registry_request**) nodep;
|
||||
if( datap->type == INSERT){
|
||||
delta_counter++;
|
||||
}
|
||||
else{
|
||||
delta_counter--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* each pending insertion counts as +1
|
||||
each removal counts as -1
|
||||
*/
|
||||
int update_list_get_delta(){
|
||||
delta_counter = 0;
|
||||
twalk( pending_registry, delta_counting_action);
|
||||
return delta_counter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
active_print(const void *nodep, VISIT which, int depth)
|
||||
{
|
||||
active_registry_item *datap;
|
||||
switch ( which) {
|
||||
case preorder:
|
||||
break;
|
||||
case postorder:
|
||||
datap = *( active_registry_item**) nodep;
|
||||
printf("path : \"%s\" th_id %ld\n", datap->path, datap->thread);
|
||||
break;
|
||||
case endorder:
|
||||
break;
|
||||
case leaf:
|
||||
datap = *( active_registry_item**) nodep;
|
||||
printf("path : \"%s\" th_id %ld\n", datap->path, datap->thread);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pending_print(const void *nodep, VISIT which, int depth)
|
||||
{
|
||||
pending_registry_request *datap;
|
||||
switch ( which) {
|
||||
case preorder:
|
||||
break;
|
||||
case postorder:
|
||||
datap = *( pending_registry_request**) nodep;
|
||||
printf("path : \"%s\" code %d\n", datap->path, datap->type);
|
||||
break;
|
||||
case endorder:
|
||||
break;
|
||||
case leaf:
|
||||
datap = *( pending_registry_request**) nodep;
|
||||
printf("path : \"%s\" code %d\n", datap->path, datap->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void print_active_list(){
|
||||
printf(">>>> >>>> print_active_list\n");
|
||||
twalk( active_registry, active_print);
|
||||
printf("<<<< <<<< print_active_list\n");
|
||||
}
|
||||
|
||||
void print_update_list(){
|
||||
printf(">>>> >>>> print_update_list\n");
|
||||
twalk( pending_registry, pending_print);
|
||||
printf("<<<< <<<< print_update_list\n");
|
||||
}
|
||||
32
registries.h
Normal file
32
registries.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
INSERT,
|
||||
REMOVE
|
||||
} update_request_type;
|
||||
|
||||
int is_listed_as_active( const char *path);
|
||||
|
||||
void add_to_active( const char *path);
|
||||
|
||||
void remove_from_active( const char *path);
|
||||
|
||||
int active_list_count();
|
||||
|
||||
int is_listed_for_removal( const char *path);
|
||||
|
||||
int is_listed_for_insertion( const char *path);
|
||||
|
||||
void add_to_update_list( const char *path, update_request_type type);
|
||||
|
||||
void remove_from_update_list( const char *path);
|
||||
|
||||
void update_list_drain_into_active();
|
||||
|
||||
int update_list_get_delta();
|
||||
|
||||
|
||||
/* debug only (could remove) */
|
||||
void print_active_list();
|
||||
|
||||
void print_update_list();
|
||||
201
registries_items.c
Normal file
201
registries_items.c
Normal file
@@ -0,0 +1,201 @@
|
||||
#include "registries_items.h"
|
||||
|
||||
#include <libevdev.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
#include "events.h"
|
||||
|
||||
pthread_barrier_t all_devices_wrote_barrier;
|
||||
pthread_barrier_t devices_write_new_round_barrier;
|
||||
|
||||
/* the spawning thread must reach the barrier and only after that, take the mutex
|
||||
the spawned instead first takes the mutex and sets the flag, then releases the mutex and reach the barrier, thus allowing the spawning to get a consistent read
|
||||
|
||||
!!!
|
||||
beware that if a signal reaches a waiting thread, that thread will handle the signal and then resume its wait unless such wait didn't complete in the meanwhile
|
||||
!!! it is unspecified whether the other threads wait for the signaled thread
|
||||
*/
|
||||
pthread_barrier_t device_added_flag_barrier;
|
||||
pthread_mutex_t device_added_flag_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
int device_added_flag;
|
||||
|
||||
|
||||
pthread_mutex_t enqueue_events_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
|
||||
/* nice struct from the phtread_create manpage example */
|
||||
typedef struct { /* Used as argument to thread_start() */
|
||||
pthread_t thread_id; /* ID returned by pthread_create() */
|
||||
int thread_num; /* Application-defined thread # */
|
||||
char *argv_string; /* From command-line argument */
|
||||
} thread_info;
|
||||
|
||||
|
||||
void initialize_device_added_flag_barrier(){
|
||||
int anyerror = pthread_barrier_init( &device_added_flag_barrier, NULL, 2);
|
||||
if(anyerror){
|
||||
fprintf( stderr, "setup failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void* collecting_routine( void *args){
|
||||
const char *path = (char*) args;
|
||||
|
||||
struct libevdev *dev = NULL;
|
||||
int fd;
|
||||
int rc = 1;
|
||||
int open_error = 0;
|
||||
|
||||
struct input_event ev;
|
||||
device_event *evt;
|
||||
|
||||
events_queue *queue;
|
||||
|
||||
fd = open( path, O_RDONLY|O_NONBLOCK);
|
||||
rc = libevdev_new_from_fd( fd, &dev);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to init libevdev for device (%s)\n", strerror(-rc));
|
||||
fprintf(stderr, "[NEWBIE TIP] : try with sudo (maybe you don't have the rights for the EVDEVs)\n");
|
||||
open_error = 1;
|
||||
}
|
||||
|
||||
pthread_mutex_lock( &device_added_flag_mutex);
|
||||
device_added_flag = ( !open_error);
|
||||
pthread_mutex_unlock( &device_added_flag_mutex);
|
||||
|
||||
pthread_barrier_wait( &device_added_flag_barrier);
|
||||
|
||||
if( !open_error){
|
||||
queue = events_queue_new();
|
||||
|
||||
printf( "Input device name: \"%s\"\n", libevdev_get_name( dev));
|
||||
printf( "Input device ID: bus %#x vendor %#x product %#x\n",
|
||||
libevdev_get_id_bustype( dev),
|
||||
libevdev_get_id_vendor( dev),
|
||||
libevdev_get_id_product( dev));
|
||||
/*
|
||||
if (!libevdev_has_event_type(dev, EV_REL) ||
|
||||
!libevdev_has_event_code(dev, EV_KEY, BTN_LEFT)) {
|
||||
printf("This device does not look like a mouse\n");
|
||||
exit(1);
|
||||
}
|
||||
*/
|
||||
|
||||
do {
|
||||
rc = libevdev_next_event( dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
|
||||
/* RC = 1 should be SYN_DROPPED event */
|
||||
if (rc == 0){
|
||||
evt = malloc( sizeof( device_event));
|
||||
evt->type = ev.type;
|
||||
evt->code = ev.code;
|
||||
evt->value = ev.value;
|
||||
add_to_events_queue( queue, evt);
|
||||
|
||||
/*
|
||||
printf("LIBEVDEV Event: %s %s %d\n",
|
||||
libevdev_event_type_get_name( ev.type),
|
||||
libevdev_event_code_get_name( ev.type, ev.code),
|
||||
ev.value);
|
||||
*/
|
||||
}
|
||||
if( rc == -EAGAIN){
|
||||
/*
|
||||
printf("lock %s\n", libevdev_get_name( dev));
|
||||
*/
|
||||
pthread_mutex_lock( &enqueue_events_mutex);
|
||||
add_all_to_global_queue( queue);
|
||||
pthread_mutex_unlock( &enqueue_events_mutex);
|
||||
pthread_barrier_wait( &all_devices_wrote_barrier);
|
||||
pthread_barrier_wait( &devices_write_new_round_barrier);
|
||||
}
|
||||
}
|
||||
while ( rc == 1 || rc == 0 || rc == -EAGAIN);
|
||||
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* !!! type defaults to INSERT */
|
||||
pending_registry_request* pending_registry_request_new( const char *path){
|
||||
pending_registry_request *req = malloc( sizeof( pending_registry_request));
|
||||
req->path = malloc( ( 1 + strlen( path)) * sizeof( char));
|
||||
strcpy( req->path, path);
|
||||
return req;
|
||||
}
|
||||
|
||||
void pending_registry_request_destroy( pending_registry_request *req){
|
||||
free( req->path);
|
||||
free( req);
|
||||
}
|
||||
|
||||
active_registry_item* active_registry_item_dummy_new( const char *path){
|
||||
active_registry_item* item = malloc( sizeof( active_registry_item));
|
||||
item->path = malloc(( 1 + strlen( path)) * sizeof(char));
|
||||
strcpy( item->path, path);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
active_registry_item* active_registry_item_new( const char *path){
|
||||
active_registry_item* item = active_registry_item_dummy_new( path);
|
||||
|
||||
/*HERE CREATE THE THREAD*/
|
||||
thread_info th_info;
|
||||
/*
|
||||
all the thread attribute stuff has been postponed
|
||||
|
||||
SCHED(7)
|
||||
Response time
|
||||
A blocked high priority thread waiting for I/O has a certain response time before it is scheduled again. The device driver writer can greatly reduce this response time by using
|
||||
a "slow interrupt" interrupt handler.
|
||||
|
||||
https://en.wikipedia.org/wiki/Interrupt_handler
|
||||
*/
|
||||
/* null implies default values */
|
||||
const pthread_attr_t *attr = NULL;
|
||||
|
||||
int thread_create_failed = 1;
|
||||
int device_add_error = 0;
|
||||
|
||||
thread_create_failed = pthread_create(
|
||||
&th_info.thread_id,
|
||||
attr,
|
||||
collecting_routine,
|
||||
(void *) path
|
||||
);
|
||||
|
||||
if( thread_create_failed){
|
||||
fprintf( stderr, "unable to create device polling thread\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pthread_barrier_wait( &device_added_flag_barrier);
|
||||
|
||||
pthread_mutex_lock( &device_added_flag_mutex);
|
||||
device_add_error = ( !device_added_flag);
|
||||
pthread_mutex_unlock( &device_added_flag_mutex);
|
||||
|
||||
if( device_add_error){
|
||||
fprintf( stderr, "device polling thread failed to initialize its resources\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void active_registry_item_dummy_destroy(active_registry_item *item){
|
||||
free( item->path);
|
||||
free( item);
|
||||
}
|
||||
|
||||
void active_registry_item_destroy( active_registry_item *item){
|
||||
pthread_cancel( item->thread);
|
||||
active_registry_item_dummy_destroy( item);
|
||||
}
|
||||
33
registries_items.h
Normal file
33
registries_items.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "threading.h"
|
||||
|
||||
extern pthread_barrier_t all_devices_wrote_barrier;
|
||||
extern pthread_barrier_t devices_write_new_round_barrier;
|
||||
|
||||
typedef struct {
|
||||
char *path;
|
||||
pthread_t thread;
|
||||
} active_registry_item;
|
||||
|
||||
void initialize_device_added_flag_barrier();
|
||||
|
||||
|
||||
active_registry_item* active_registry_item_dummy_new( const char *path);
|
||||
|
||||
active_registry_item* active_registry_item_new( const char *path);
|
||||
|
||||
void active_registry_item_dummy_destroy( active_registry_item *item);
|
||||
|
||||
void active_registry_item_destroy( active_registry_item *item);
|
||||
|
||||
|
||||
typedef struct {
|
||||
char *path;
|
||||
int type; /* cannot use enums */
|
||||
} pending_registry_request;
|
||||
|
||||
pending_registry_request* pending_registry_request_new( const char *path);
|
||||
|
||||
void pending_registry_request_destroy( pending_registry_request *req);
|
||||
|
||||
17
threading.h
Normal file
17
threading.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
/* https://stackoverflow.com/questions/61647896/unknown-type-name-pthread-barrier-t */
|
||||
/*
|
||||
-std=c99 is bad for this,
|
||||
use
|
||||
-std=gnu99
|
||||
|
||||
or if keeping -std=c99
|
||||
use either
|
||||
#define _XOPEN_SOURCE 600
|
||||
or
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
*/
|
||||
#define _POSIX_C_SOURCE 200112L /* Or higher */
|
||||
|
||||
#include <pthread.h>
|
||||
Reference in New Issue
Block a user