idev.c revision e06cc7b07465369fb7c01c9778b84cf82c82fdcf
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <inttypes.h>
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
#include <systemd/sd-event.h>
#include <systemd/sd-login.h>
#include <xkbcommon/xkbcommon.h>
#include "hashmap.h"
#include "idev.h"
#include "idev-internal.h"
#include "login-shared.h"
#include "macro.h"
#include "set.h"
#include "udev-util.h"
#include "util.h"
static void element_open(idev_element *e);
static void element_close(idev_element *e);
/*
* Devices
*/
assert_return(s, NULL);
}
int r;
assert_return(d, -EINVAL);
if (!d->name)
return -ENOMEM;
if (r < 0)
return r;
return 0;
}
if (!d)
return NULL;
if (d->name)
tmp = *d;
return NULL;
}
assert(d);
else
return 0;
}
idev_link *l;
assert(d);
}
assert(d);
assert(l);
if (d->enabled)
element_open(l->element);
}
assert(d);
assert(l);
if (d->enabled)
element_close(l->element);
}
void idev_device_enable(idev_device *d) {
idev_link *l;
assert(d);
if (!d->enabled) {
d->enabled = true;
element_open(l->element);
}
}
void idev_device_disable(idev_device *d) {
idev_link *l;
assert(d);
if (d->enabled) {
d->enabled = false;
element_close(l->element);
}
}
/*
* Elements
*/
assert_return(s, NULL);
}
int r;
assert_return(e, -EINVAL);
if (!e->name)
return -ENOMEM;
if (r < 0)
return r;
return 0;
}
if (!e)
return NULL;
if (e->name)
tmp = *e;
return NULL;
}
int r, error = 0;
idev_link *l;
assert(e);
if (r != 0)
error = r;
}
return error;
}
assert(e);
}
static void element_open(idev_element *e) {
assert(e);
}
static void element_close(idev_element *e) {
assert(e);
}
static void element_enable(idev_element *e) {
assert(e);
if (!e->enabled) {
e->enabled = true;
}
}
static void element_disable(idev_element *e) {
assert(e);
if (e->enabled) {
e->enabled = false;
}
}
/*
* Sessions
*/
}
idev_event event = {
.device_add = {
.device = d,
},
};
return session_raise(s, &event);
}
idev_event event = {
.device_remove = {
.device = d,
},
};
return session_raise(s, &event);
}
idev_event event = {
.device_data = {
.device = d,
},
};
return session_raise(s, &event);
}
int r;
assert(s);
assert(d);
d->public = true;
r = session_raise_device_add(s, d);
if (r != 0) {
d->public = false;
goto error;
}
return 0;
if (r < 0)
log_debug("idev: %s: error while adding device '%s': %s",
return r;
}
int r, error = 0;
assert(s);
assert(d);
d->public = false;
r = session_raise_device_remove(s, d);
if (r != 0)
error = r;
if (error < 0)
log_debug("idev: %s: error while removing device '%s': %s",
idev_device_free(d);
return error;
}
assert(s);
assert(e);
if (s->enabled)
element_enable(e);
return 0;
}
int r, error = 0;
idev_device *d;
idev_link *l;
assert(s);
assert(e);
while ((l = e->links)) {
d = l->device;
device_detach(d, l);
if (!d->links) {
r = session_remove_device(s, d);
if (r != 0)
error = r;
}
free(l);
}
element_disable(e);
if (error < 0)
log_debug("idev: %s: error while removing element '%s': %s",
return error;
}
assert_return(c, NULL);
}
idev_context *c,
unsigned int flags,
const char *name,
void *userdata) {
int r;
assert_return(c, -EINVAL);
if (!s)
return -ENOMEM;
s->context = idev_context_ref(c);
if (!s->name)
return -ENOMEM;
if (s->managed) {
if (r < 0)
return r;
}
if (!s->element_map)
return -ENOMEM;
if (!s->device_map)
return -ENOMEM;
if (r < 0)
return r;
*out = s;
s = NULL;
return 0;
}
idev_element *e;
if (!s)
return NULL;
while ((e = hashmap_first(s->element_map)))
session_remove_element(s, e);
if (s->name)
hashmap_free(s->device_map);
hashmap_free(s->element_map);
free(s);
return NULL;
}
bool idev_session_is_enabled(idev_session *s) {
return s && s->enabled;
}
void idev_session_enable(idev_session *s) {
idev_element *e;
Iterator i;
assert(s);
if (!s->enabled) {
s->enabled = true;
HASHMAP_FOREACH(e, s->element_map, i)
element_enable(e);
}
}
void idev_session_disable(idev_session *s) {
idev_element *e;
Iterator i;
assert(s);
if (s->enabled) {
s->enabled = false;
HASHMAP_FOREACH(e, s->element_map, i)
element_disable(e);
}
}
idev_link *l;
assert(e);
assert(d);
if (!l)
return -ENOMEM;
l->element = e;
l->device = d;
device_attach(d, l);
return 0;
}
static int guess_type(struct udev_device *d) {
const char *id_key;
return IDEV_DEVICE_KEYBOARD;
return IDEV_DEVICE_CNT;
}
idev_element *e;
idev_device *d;
int r, type;
assert_return(s, -EINVAL);
if (devnum == 0)
return 0;
e = idev_find_evdev(s, devnum);
if (e)
return 0;
r = idev_evdev_new(&e, s, ud);
if (r < 0)
return r;
r = session_add_element(s, e);
if (r != 0)
return r;
if (type < 0)
return type;
switch (type) {
case IDEV_DEVICE_KEYBOARD:
d = idev_find_keyboard(s, e->name);
if (d) {
log_debug("idev: %s: keyboard for new evdev element '%s' already available",
return 0;
}
r = idev_keyboard_new(&d, s, e->name);
if (r < 0)
return r;
r = add_link(e, d);
if (r < 0) {
idev_device_free(d);
return r;
}
return session_add_device(s, d);
default:
/* unknown elements are silently ignored */
return 0;
}
}
idev_element *e;
assert(s);
if (devnum == 0)
return 0;
e = idev_find_evdev(s, devnum);
if (!e)
return 0;
return session_remove_element(s, e);
}
/*
* Contexts
*/
if (!c)
return -ENOMEM;
c->ref = 1;
if (sysbus)
if (!c->session_map)
return -ENOMEM;
if (!c->data_map)
return -ENOMEM;
*out = c;
c = NULL;
return 0;
}
static void context_cleanup(idev_context *c) {
hashmap_free(c->data_map);
hashmap_free(c->session_map);
free(c);
}
assert_return(c, NULL);
++c->ref;
return c;
}
if (!c)
return NULL;
if (--c->ref == 0)
context_cleanup(c);
return NULL;
}