Loading...
Searching...
No Matches
drv_admin.c
Go to the documentation of this file.
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <dirent.h>
#include <dlfcn.h>
#ifdef HAVE_TERMIOS_H
# include <termios.h>
#endif
#ifdef GWINSZ_IN_SYS_IOCTL
# include <sys/ioctl.h>
#endif
#include "lirc/driver.h"
#include "lirc/drv_admin.h"
#include "lirc/lirc_options.h"
#include "lirc_log.h"
#include "driver.h"
static const logchannel_t logchannel = LOG_LIB;
static const char* const PLUGIN_FILE_EXTENSION = "so";
#define MAX_PLUGINS 256
extern struct driver drv;
typedef struct {
char* array[MAX_PLUGINS];
int size;
} char_array;
static void* last_plugin = NULL;
const struct driver drv_null = {
.name = "null",
.device = "/dev/null",
.features = 0,
.send_mode = 0,
.rec_mode = 0,
.code_length = 0,
.init_func = NULL,
.deinit_func = NULL,
.send_func = NULL,
.rec_func = NULL,
.decode_func = NULL,
.readdata = NULL,
.drvctl_func = default_drvctl,
.open_func = default_open,
.close_func = default_close,
.api_version = 2,
.driver_version = "0.10.2"
};
static int ends_with_so(const char* str)
{
char* dot = strrchr(str, '.');
return (NULL == dot) ? 0 : strcmp(dot + 1, PLUGIN_FILE_EXTENSION) == 0;
}
static int line_cmp(const void* arg1, const void* arg2)
{
return strcmp(*(const char**)arg1, *(const char**)arg2);
}
static struct driver* add_hw_name(struct driver* hw, void* arg)
{
char_array* a = (char_array*)arg;
if (a->size >= MAX_PLUGINS) {
log_error("Too many plugins(%d)", MAX_PLUGINS);
return hw;
}
a->array[a->size] = strdup(hw->name);
a->size += 1;
return NULL;
}
static struct driver* match_hw_name(struct driver* drv, void* name)
{
// drv_guest_func. Returns hw if hw->name == name, else NULL.
if (drv == (struct driver*)NULL || name == NULL)
return (struct driver*)NULL;
if (strcasecmp(drv->name, (char*)name) == 0)
return drv;
return (struct driver*)NULL;
}
static struct driver*
visit_plugin(const char* path, drv_guest_func func, void* arg)
{
// Apply func(hw, arg) for all drivers found in plugin on path.
struct driver** drivers;
struct driver* result = (struct driver*)NULL;
(void)dlerror();
if (last_plugin != NULL)
dlclose(last_plugin);
last_plugin = dlopen(path, RTLD_NOW);
if (last_plugin == NULL) {
log_error(dlerror());
return result;
}
drivers = (struct driver**)dlsym(last_plugin, "hardwares");
if (drivers == (struct driver**)NULL) {
log_warn("No hardwares entrypoint found in %s", path);
} else {
for (; *drivers; drivers++) {
if ((*drivers)->name == NULL) {
log_warn("No driver name in %s", path);
continue;
}
result = (*func)(*drivers, arg);
if (result != (struct driver*)NULL)
break;
}
}
return result;
}
/* Apply plugin_guest(path, drv_guest, arg) to all so-files in dir. */
static struct driver* for_each_plugin_in_dir(const char* dirpath,
plugin_guest_func plugin_guest,
drv_guest_func drv_guest,
void* arg)
{
DIR* dir;
struct dirent* ent;
struct driver* result = (struct driver*)NULL;
char path[1024];
char buff[1024];
dir = opendir(dirpath);
if (dir == NULL) {
log_info("Cannot open plugindir %s", dirpath);
return (struct driver*)NULL;
}
while ((ent = readdir(dir)) != NULL) {
if (!ends_with_so(ent->d_name))
continue;
strncpy(buff, dirpath, sizeof(buff) - 1);
if (buff[strlen(buff) - 1] == '/')
buff[strlen(buff) - 1] = '\0';
snprintf(path, sizeof(path),
"%s/%s", buff, ent->d_name);
result = plugin_guest(path, drv_guest, arg);
if (result != (struct driver*)NULL)
break;
}
closedir(dir);
return result;
}
static struct driver* for_each_path(plugin_guest_func plg_guest,
drv_guest_func drv_guest,
void* arg,
const char* pluginpath_arg)
{
const char* pluginpath;
char* tmp_path;
char* s;
struct driver* result = (struct driver*)NULL;
if (pluginpath_arg == NULL) {
pluginpath = ciniparser_getstring(lirc_options,
"lircd:plugindir",
getenv(PLUGINDIR_VAR));
if (pluginpath == NULL)
pluginpath = PLUGINDIR;
} else {
pluginpath = pluginpath_arg;
}
if (strchr(pluginpath, ':') == (char*)NULL) {
return for_each_plugin_in_dir(pluginpath,
plg_guest,
drv_guest,
arg);
}
tmp_path = alloca(strlen(pluginpath) + 1);
strncpy(tmp_path, pluginpath, strlen(pluginpath) + 1);
for (s = strtok(tmp_path, ":"); s != NULL; s = strtok(NULL, ":")) {
result = for_each_plugin_in_dir(s,
plg_guest,
drv_guest,
arg);
if (result != (struct driver*)NULL)
break;
}
return result;
}
struct driver* for_each_driver(drv_guest_func func,
void* arg,
const char* pluginpath)
{
return for_each_path(visit_plugin, func, arg, pluginpath);
}
void for_each_plugin(plugin_guest_func plugin_guest,
void* arg,
const char* pluginpath)
{
for_each_path(plugin_guest, NULL, arg, pluginpath);
}
static void get_columns(FILE* f, char_array names, int* cols, int* width)
{
int maxlen = 0;
struct winsize winsize;
int i;
*cols = 1;
*width = 32;
if (!isatty(fileno(f)))
return;
if (ioctl(fileno(f), TIOCGWINSZ, &winsize) != 0)
return;
for (i = 0; i < names.size; i += 1) {
if (strlen(names.array[i]) > maxlen)
maxlen = strlen(names.array[i]);
}
maxlen += 1;
*cols = winsize.ws_col / maxlen;
*width = maxlen;
}
void hw_print_drivers(FILE* file)
{
char_array names;
int i;
int cols;
int width;
char format[16];
names.size = 0;
if (for_each_driver(add_hw_name, (void*)&names, NULL) != NULL) {
fprintf(stderr, "Too many plugins (%d)\n", MAX_PLUGINS);
return;
}
qsort(names.array, names.size, sizeof(char*), line_cmp);
get_columns(file, names, &cols, &width);
snprintf(format, sizeof(format), "%%-%ds", width);
for (i = 0; i < names.size; i += 1) {
fprintf(file, format, names.array[i]);
if ((i + 1) % cols == 0)
fprintf(file, "\n");
free(names.array[i]);
}
if ((i + 1) % cols != 0)
fprintf(file, "\n");
}
int hw_choose_driver(const char* name)
{
struct driver* found;
if (name == NULL) {
memcpy(&drv, &drv_null, sizeof(struct driver));
drv.fd = -1;
return 0;
}
if (strcasecmp(name, "dev/input") == 0)
/* backwards compatibility */
name = "devinput";
found = for_each_driver(match_hw_name, (void*)name, NULL);
if (found != (struct driver*)NULL) {
memcpy(&drv, found, sizeof(struct driver));
drv.fd = -1;
return 0;
}
return -1;
}
drv
struct driver drv
The global driver data that drivers etc are accessing.
Definition driver.c:28
driver.h
Interface to the userspace drivers.
hw_choose_driver
int hw_choose_driver(const char *name)
Search for driver with given name, update global drv with driver data if found.
Definition drv_admin.c:297
MAX_PLUGINS
#define MAX_PLUGINS
Max number if plugins handled.
Definition drv_admin.c:40
for_each_driver
struct driver * for_each_driver(drv_guest_func func, void *arg, const char *pluginpath)
Apply func to all existing drivers.
Definition drv_admin.c:227
drv_null
const struct driver drv_null
Default driver, a placeholder.
Definition drv_admin.c:54
hw_print_drivers
void hw_print_drivers(FILE *file)
Prints all drivers known to the system to the file given as argument.
Definition drv_admin.c:270
for_each_plugin
void for_each_plugin(plugin_guest_func plugin_guest, void *arg, const char *pluginpath)
Apply func to all plugins (i.
Definition drv_admin.c:235
drv_guest_func
struct driver *(* drv_guest_func)(struct driver *, void *)
Argument to for_each_driver().
Definition drv_admin.h:32
plugin_guest_func
struct driver *(* plugin_guest_func)(const char *, drv_guest_func, void *)
Argument to for_each_plugin.
Definition drv_admin.h:41
ciniparser_getstring
const char * ciniparser_getstring(dictionary *d, const char *key, char *def)
Get the string associated to a key.
Definition ciniparser.c:286
default_close
int default_close(void)
For now, a placeholder.
Definition driver.c:52
default_drvctl
int default_drvctl(unsigned int fd, void *arg)
Return DRV_ERR_NOTIMPLEMENTED.
Definition driver.c:57
default_open
int default_open(const char *path)
Stores path in drv.device if non-null.
Definition driver.c:37
PLUGINDIR
#define PLUGINDIR
Default directory for plugins/drivers.
Definition lirc_config.h:66
PLUGINDIR_VAR
#define PLUGINDIR_VAR
Environment variable holding defaults for PLUGINDIR.
Definition lirc_config.h:87
lirc_log.h
Logging functionality.
log_info
#define log_info(fmt,...)
Log an info message.
Definition lirc_log.h:114
log_error
#define log_error(fmt,...)
Log an error message.
Definition lirc_log.h:104
logchannel_t
logchannel_t
Log channels used to filter messages.
Definition lirc_log.h:53
log_warn
#define log_warn(fmt,...)
Log a warning message.
Definition lirc_log.h:109
driver
The data the driver exports i.
Definition driver.h:132
driver::name
const char * name
Driver name, as listed by -H help and used as argument to i –driver.
Definition driver.h:224