project for a (works on my devices) demo

This commit is contained in:
beno
2026-03-13 16:38:39 +01:00
parent 3b13017f39
commit a70999a15c
17 changed files with 1113 additions and 61 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*.o
*.exe
*.so.*
old/

View File

@@ -0,0 +1,3 @@
export COMPILER=aarch64-buildroot-linux-gnu-gcc
export L_EVDEV=-L ../../resources/libraries/

View File

@@ -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
View File

@@ -0,0 +1,3 @@
export COMPILER=gcc
export L_EVDEV=-L /usr/lib/x86_64-linux-gnu/

79
Makefile Normal file
View 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}

View File

@@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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>