From: David Herrmann Date: Sat, 31 Dec 2011 11:04:17 +0000 (+0100) Subject: input: rearrange code to avoid forward declarations X-Git-Tag: kmscon-7~1288 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=0799fbb4bec8ebb7a04cd0de6aac7684dbcc2981;p=platform%2Fupstream%2Fkmscon.git input: rearrange code to avoid forward declarations The diff may look a bit wild but this mostly fixes coding style issues: - sort includes alphabetically - add missing includes - rearrange structures/functions to avoid forward declarations - adding "data" user-defineable field to input structure (for callbacks) - correctly handle errors in device_data_arrived - remove device_added/device_removed - merge input_init into constructor - add some log messages for debugging - remove devices when wake-up fails - set cb/data fields on eloop connection, not on initialization - remove all devices when disconnecting eloop so the eloop is guaranteed to be not used by the input subsystem after disconnection. - some error handling fixes - remove some TODOs which are correctly implemented - Add O_NONBLOCK when opening the device node (I don't know how it actually worked without it in the data_arrived callback) Signed-off-by: David Herrmann --- diff --git a/src/input.c b/src/input.c index 5890c97..f035f84 100644 --- a/src/input.c +++ b/src/input.c @@ -41,13 +41,14 @@ #include #include -#include -#include -#include - #include #include +#include +#include +#include +#include +#include "eloop.h" #include "log.h" #include "input.h" @@ -56,556 +57,507 @@ enum input_state { INPUT_AWAKE, }; +struct kmscon_input_device { + size_t ref; + struct kmscon_input_device *next; + struct kmscon_input *input; + + int rfd; + char *devnode; + struct kmscon_fd *fd; +}; + struct kmscon_input { size_t ref; enum input_state state; struct kmscon_input_device *devices; - /* - * We need to keep a reference to the eloop for sleeping, waking up and - * hotplug. Avoiding this makes things harder. - */ - struct kmscon_eloop *loop; + struct kmscon_eloop *eloop; + kmscon_input_cb cb; + void *data; struct udev *udev; struct udev_monitor *monitor; struct kmscon_fd *monitor_fd; - - kmscon_input_cb cb; }; -struct kmscon_input_device { - size_t ref; - struct kmscon_input *input; - struct kmscon_input_device *next; +static void remove_device(struct kmscon_input *input, const char *node); - int rfd; - char *devnode; - struct kmscon_fd *fd; -}; +static void device_data_arrived(struct kmscon_fd *fd, int mask, void *data) +{ + int i; + ssize_t len, n; + struct kmscon_input_device *device = data; + struct kmscon_input *input = device->input; + struct input_event ev[16]; + + len = sizeof(ev); + while (len == sizeof(ev)) { + len = read(device->rfd, &ev, sizeof(ev)); + if (len < 0) { + if (errno == EWOULDBLOCK) + break; + + log_warning("input: reading device %s failed %d\n", + device->devnode, errno); + remove_device(input, device->devnode); + } else if (len == 0) { + log_debug("input: EOF device %s\n", device->devnode); + remove_device(input, device->devnode); + } else if (len % sizeof(*ev)) { + log_warning("input: read invalid input_event\n"); + } else { + n = len / sizeof(*ev); + for (i = 0; i < n; i++) + input->cb(input, ev[i].type, ev[i].code, + ev[i].value, input->data); + } + } +} -/* kmscon_input_device prototypes */ -int kmscon_input_device_new(struct kmscon_input_device **out, - struct kmscon_input *input, - const char *devnode); -void kmscon_input_device_ref(struct kmscon_input_device *device); -void kmscon_input_device_unref(struct kmscon_input_device *device); - -int kmscon_input_device_open(struct kmscon_input_device *device); -void kmscon_input_device_close(struct kmscon_input_device *device); - -int kmscon_input_device_connect_eloop(struct kmscon_input_device *device); -void kmscon_input_device_disconnect_eloop(struct kmscon_input_device *device); -void kmscon_input_device_sleep(struct kmscon_input_device *device); -int kmscon_input_device_wake_up(struct kmscon_input_device *device); - -/* internal procedures prototypes */ -static int init_input(struct kmscon_input *input); -static void device_changed(struct kmscon_fd *fd, int mask, void *data); -static int add_initial_devices(struct kmscon_input *input); -static int add_device(struct kmscon_input *input, struct udev_device *udev_device); -static void device_added(struct kmscon_input *input, struct udev_device *udev_device); -static void device_removed(struct kmscon_input *input, struct udev_device *udev_device); -static int remove_device(struct kmscon_input *input, struct udev_device *udev_device); -static void device_data_arrived(struct kmscon_fd *fd, int mask, void *data); - -int kmscon_input_new(struct kmscon_input **out, kmscon_input_cb cb) +int kmscon_input_device_wake_up(struct kmscon_input_device *device) { int ret; - struct kmscon_input *input; - if (!out) + if (!device || !device->input || !device->input->eloop) return -EINVAL; - input = malloc(sizeof(*input)); - if (!input) - return -ENOMEM; + if (device->fd) + return 0; - memset(input, 0, sizeof(*input)); - input->ref = 1; - input->state = INPUT_ASLEEP; - input->cb = cb; + device->rfd = open(device->devnode, O_CLOEXEC | O_NONBLOCK, O_RDONLY); + if (device->rfd < 0) { + log_warning("input: cannot open input device %s: %d\n", + device->devnode, errno); + return -errno; + } - ret = init_input(input); + ret = kmscon_eloop_new_fd(device->input->eloop, &device->fd, + device->rfd, KMSCON_READABLE, device_data_arrived, device); if (ret) { - free(input); + close(device->rfd); + device->rfd = -1; return ret; } - *out = input; return 0; } -void kmscon_input_ref(struct kmscon_input *input) +void kmscon_input_device_sleep(struct kmscon_input_device *device) { - if (!input) + if (!device) return; - ++input->ref; + if (device->rfd < 0) + return; + + kmscon_eloop_rm_fd(device->fd); + device->fd = NULL; + close(device->rfd); + device->rfd = -1; } -static int init_input(struct kmscon_input *input) +static int kmscon_input_device_new(struct kmscon_input_device **out, + struct kmscon_input *input, const char *devnode) { - int ret; + struct kmscon_input_device *device; - input->udev = udev_new(); - if (!input->udev) - return -EFAULT; + if (!out || !input) + return -EINVAL; - input->monitor = udev_monitor_new_from_netlink(input->udev, "udev"); - if (!input->monitor) { - ret = -EFAULT; - goto err_udev; - } + log_debug("input: new input device %s\n", devnode); - ret = udev_monitor_filter_add_match_subsystem_devtype(input->monitor, - "input", NULL); - if (ret) - goto err_monitor; + device = malloc(sizeof(*device)); + if (!device) + return -ENOMEM; - /* - * There's no way to query the state of the monitor, so just start it - * from here. - */ - ret = udev_monitor_enable_receiving(input->monitor); - if (ret) - goto err_monitor; + memset(device, 0, sizeof(*device)); + device->ref = 1; - return 0; + device->devnode = strdup(devnode); + if (!device->devnode) { + free(device); + return -ENOMEM; + } -err_monitor: - udev_monitor_unref(input->monitor); -err_udev: - udev_unref(input->udev); - return ret; + kmscon_input_ref(input); + device->input = input; + device->rfd = -1; + + *out = device; + return 0; } -/* - * This takes a ref of the loop, but it most likely not what should be - * keeping it alive. - */ -int kmscon_input_connect_eloop(struct kmscon_input *input, struct kmscon_eloop *loop) +static void kmscon_input_device_ref(struct kmscon_input_device *device) { - int ret; - int fd; - - if (!input || !loop || !input->monitor) - return -EINVAL; - - if (input->loop) - return -EALREADY; - - input->loop = loop; - kmscon_eloop_ref(loop); - - fd = udev_monitor_get_fd(input->monitor); - - ret = kmscon_eloop_new_fd(loop, &input->monitor_fd, fd, KMSCON_READABLE, - device_changed, input); - if (ret) - goto err_loop; + if (!device) + return; - /* XXX: What if a device is added NOW? */ + ++device->ref; +} - ret = add_initial_devices(input); - if (ret) - goto err_fd; +static void kmscon_input_device_unref(struct kmscon_input_device *device) +{ + if (!device || !device->ref) + return; - return 0; + if (--device->ref) + return; -err_fd: - kmscon_eloop_rm_fd(input->monitor_fd); - input->monitor_fd = NULL; -err_loop: - kmscon_eloop_unref(loop); - input->loop = NULL; - return ret; + kmscon_input_device_sleep(device); + log_debug("input: destroying input device %s\n", device->devnode); + free(device->devnode); + free(device); } -static int add_initial_devices(struct kmscon_input *input) +int kmscon_input_new(struct kmscon_input **out) { int ret; + struct kmscon_input *input; - struct udev_enumerate *e; - struct udev_list_entry *first; - struct udev_list_entry *item; - struct udev_device *udev_device; - const char *syspath; + if (!out) + return -EINVAL; - e = udev_enumerate_new(input->udev); - if (!e) - return -EFAULT; + input = malloc(sizeof(*input)); + if (!input) + return -ENOMEM; - ret = udev_enumerate_add_match_subsystem(e, "input"); - if (ret) - goto err_enum; + log_debug("input: creating input object\n"); - ret = udev_enumerate_scan_devices(e); - if (ret) - goto err_enum; + memset(input, 0, sizeof(*input)); + input->ref = 1; + input->state = INPUT_ASLEEP; - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - syspath = udev_list_entry_get_name(item); + input->udev = udev_new(); + if (!input->udev) { + log_warning("input: cannot create udev object\n"); + ret = -EFAULT; + goto err_free; + } - udev_device = udev_device_new_from_syspath(input->udev, syspath); - if (!udev_device) - continue; + input->monitor = udev_monitor_new_from_netlink(input->udev, "udev"); + if (!input->monitor) { + log_warning("input: cannot create udev monitor\n"); + ret = -EFAULT; + goto err_udev; + } - add_device(input, udev_device); + ret = udev_monitor_filter_add_match_subsystem_devtype(input->monitor, + "input", NULL); + if (ret) { + log_warning("input: cannot add udev filter\n"); + ret = -EFAULT; + goto err_monitor; + } - udev_device_unref(udev_device); + ret = udev_monitor_enable_receiving(input->monitor); + if (ret) { + log_warning("input: cannot start udev monitor\n"); + ret = -EFAULT; + goto err_monitor; } -err_enum: - udev_enumerate_unref(e); + *out = input; + return 0; + +err_monitor: + udev_monitor_unref(input->monitor); +err_udev: + udev_unref(input->udev); +err_free: + free(input); return ret; } -static void device_changed(struct kmscon_fd *fd, int mask, void *data) +void kmscon_input_ref(struct kmscon_input *input) { - struct kmscon_input *input = data; - struct udev_device *udev_device; - const char *action; - - if (!input || !input->monitor) - return; - - udev_device = udev_monitor_receive_device(input->monitor); - if (!udev_device) - goto err_device; - - action = udev_device_get_action(udev_device); - if (!action) - goto err_device; - - /* - * XXX: need to do something with the others? (change, online, - * offline) - */ - if (!strcmp(action, "add")) - device_added(input, udev_device); - else if (!strcmp(action, "remove")) - device_removed(input, udev_device); + if (!input) + return; -err_device: - udev_device_unref(udev_device); + ++input->ref; } -static void device_added(struct kmscon_input *input, - struct udev_device *udev_device) +void kmscon_input_unref(struct kmscon_input *input) { - add_device(input, udev_device); -} + if (!input || !input->ref) + return; -static void device_removed(struct kmscon_input *input, - struct udev_device *udev_device) -{ - remove_device(input, udev_device); + if (--input->ref) + return; + + kmscon_input_disconnect_eloop(input); + udev_monitor_unref(input->monitor); + udev_unref(input->udev); + free(input); + log_debug("input: destroying input object\n"); } -static int add_device(struct kmscon_input *input, struct udev_device *udev_device) +static void add_device(struct kmscon_input *input, + struct udev_device *udev_device) { int ret; struct kmscon_input_device *device; - const char *value, *devnode; + const char *value, *node; if (!input || !udev_device) - return -EINVAL; + return; /* * TODO: Here should go a proper filtering of input devices we're - * interested in. Maybe also seats, etc? + * interested in. Currently, we add all kinds of devices. A simple + * blacklist should be the easiest way. */ - value = udev_device_get_property_value(udev_device, "ID_INPUT_KEYBOARD"); - if (!value || strcmp(value, "1") != 0) - return 0; - devnode = udev_device_get_devnode(udev_device); - if (!devnode) - return -EFAULT; + node = udev_device_get_devnode(udev_device); + if (!node) { + log_warning("input: cannot get udev node for input device\n"); + return; + } - ret = kmscon_input_device_new(&device, input, devnode); - if (ret) - return ret; + value = udev_device_get_property_value(udev_device, + "ID_INPUT_KEYBOARD"); + if (!value || strcmp(value, "1")) { + log_debug("input: ignoring non-keyboard device %s\n", node); + return; + } + + ret = kmscon_input_device_new(&device, input, node); + if (ret) { + log_warning("input: cannot create input device for %s\n", + node); + return; + } if (input->state == INPUT_AWAKE) { ret = kmscon_input_device_wake_up(device); if (ret) { - log_warning("input: cannot wake up new device %s: %s\n", - devnode, strerror(-ret)); - goto err_device; + log_warning("input: cannot wake up new device %s\n", + node); + kmscon_input_device_unref(device); + return; } } device->next = input->devices; input->devices = device; - - log_debug("input: added device %s\n", devnode); - - return 0; - -err_device: - kmscon_input_device_unref(device); - return ret; + log_debug("input: added device %s\n", node); } -static int remove_device(struct kmscon_input *input, struct udev_device *udev_device) +static void remove_device(struct kmscon_input *input, const char *node) { struct kmscon_input_device *iter, *prev; - const char *devnode; - - if (!input || !udev_device) - return -EINVAL; - - if (!input->devices) - return 0; - devnode = udev_device_get_devnode(udev_device); - if (!devnode) - return -EFAULT; + if (!input || !node || !input->devices) + return; iter = input->devices; prev = NULL; + while (iter) { - if (!strcmp(iter->devnode, devnode)) { + if (!strcmp(iter->devnode, node)) { if (prev == NULL) input->devices = iter->next; else prev->next = iter->next; + kmscon_input_device_unref(iter); - log_debug("input: removed device %s\n", devnode); + log_debug("input: removed device %s\n", node); break; } prev = iter; iter = iter->next; } - - return 0; } -void kmscon_input_unref(struct kmscon_input *input) +static void remove_device_udev(struct kmscon_input *input, + struct udev_device *udev_device) { - struct kmscon_input_device *iter, *next; + const char *node; - if (!input || !input->ref) - return; - - if (--input->ref) + if (!udev_device) return; - iter = input->devices; - while (iter) { - next = iter->next; - kmscon_input_device_unref(iter); - iter = next; - } - - kmscon_input_disconnect_eloop(input); - udev_monitor_unref(input->monitor); - udev_unref(input->udev); - - free(input); -} - -void kmscon_input_disconnect_eloop(struct kmscon_input *input) -{ - if (!input || !input->loop) + node = udev_device_get_devnode(udev_device); + if (!node) return; - kmscon_eloop_rm_fd(input->monitor_fd); - input->monitor_fd = NULL; - kmscon_eloop_unref(input->loop); - input->loop = NULL; + remove_device(input, node); } -void kmscon_input_sleep(struct kmscon_input *input) +static void device_changed(struct kmscon_fd *fd, int mask, void *data) { - struct kmscon_input_device *iter; + struct kmscon_input *input = data; + struct udev_device *udev_device; + const char *action; - if (!input) + udev_device = udev_monitor_receive_device(input->monitor); + if (!udev_device) return; - for (iter = input->devices; iter; iter = iter->next) - kmscon_input_device_sleep(iter); + action = udev_device_get_action(udev_device); + if (!action) { + log_warning("input: cannot get action field of new device\n"); + goto err_device; + } - input->state = INPUT_ASLEEP; + if (!strcmp(action, "add")) + add_device(input, udev_device); + else if (!strcmp(action, "remove")) + remove_device_udev(input, udev_device); + +err_device: + udev_device_unref(udev_device); } -void kmscon_input_wake_up(struct kmscon_input *input) +static void add_initial_devices(struct kmscon_input *input) { - struct kmscon_input_device *iter; + int ret; + struct udev_enumerate *e; + struct udev_list_entry *first; + struct udev_list_entry *item; + struct udev_device *udev_device; + const char *syspath; - if (!input) + e = udev_enumerate_new(input->udev); + if (!e) { + log_warning("input: cannot create udev enumeration\n"); return; + } - /* - * XXX: should probably catch errors here and do something about - * them. - */ - for (iter = input->devices; iter; iter = iter->next) - kmscon_input_device_wake_up(iter); + ret = udev_enumerate_add_match_subsystem(e, "input"); + if (ret) { + log_warning("input: cannot add match to udev enumeration\n"); + goto err_enum; + } - input->state = INPUT_AWAKE; -} + ret = udev_enumerate_scan_devices(e); + if (ret) { + log_warning("input: cannot scan udev enumeration\n"); + goto err_enum; + } -bool kmscon_input_is_asleep(struct kmscon_input *input) -{ - if (!input) - return false; + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + syspath = udev_list_entry_get_name(item); + if (!syspath) + continue; - return input->state == INPUT_ASLEEP; + udev_device = udev_device_new_from_syspath(input->udev, syspath); + if (!udev_device) { + log_warning("input: cannot create device " + "from udev path\n"); + continue; + } + + add_device(input, udev_device); + udev_device_unref(udev_device); + } + +err_enum: + udev_enumerate_unref(e); } -int kmscon_input_device_new(struct kmscon_input_device **out, - struct kmscon_input *input, - const char *devnode) +int kmscon_input_connect_eloop(struct kmscon_input *input, + struct kmscon_eloop *eloop, kmscon_input_cb cb, void *data) { - struct kmscon_input_device *device; + int ret; + int fd; - if (!out || !input) + if (!input || !eloop || !cb) return -EINVAL; - device = malloc(sizeof(*device)); - if (!device) - return -ENOMEM; + if (input->eloop) + return -EALREADY; - memset(device, 0, sizeof(*device)); - device->ref = 1; - device->input = input; - device->rfd = -1; + fd = udev_monitor_get_fd(input->monitor); + ret = kmscon_eloop_new_fd(eloop, &input->monitor_fd, fd, + KMSCON_READABLE, device_changed, input); + if (ret) + return ret; - device->devnode = strdup(devnode); - if (!device->devnode) { - free(device); - return -ENOMEM; - } + kmscon_eloop_ref(eloop); + input->eloop = eloop; + input->cb = cb; + input->data = data; + + add_initial_devices(input); - *out = device; return 0; } -void kmscon_input_device_ref(struct kmscon_input_device *device) +void kmscon_input_disconnect_eloop(struct kmscon_input *input) { - if (!device) - return; - - ++device->ref; -} + struct kmscon_input_device *tmp; -void kmscon_input_device_unref(struct kmscon_input_device *device) -{ - if (!device || !device->ref) + if (!input || !input->eloop) return; - if (--device->ref) - return; + while (input->devices) { + tmp = input->devices; + input->devices = tmp->next; + kmscon_input_device_unref(tmp); + } - kmscon_input_device_close(device); - free(device->devnode); - free(device); + kmscon_eloop_rm_fd(input->monitor_fd); + input->monitor_fd = NULL; + kmscon_eloop_unref(input->eloop); + input->eloop = NULL; + input->cb = NULL; + input->data = NULL; } -int kmscon_input_device_open(struct kmscon_input_device *device) +void kmscon_input_sleep(struct kmscon_input *input) { - if (!device || !device->devnode) - return -EINVAL; - - if (device->rfd >= 0) - return -EALREADY; - - device->rfd = open(device->devnode, O_CLOEXEC, O_RDONLY); - if (device->rfd < 0) - return -errno; - - return 0; -} + struct kmscon_input_device *iter; -void kmscon_input_device_close(struct kmscon_input_device *device) -{ - if (!device) + if (!input) return; - if (device->rfd < 0) - return; + for (iter = input->devices; iter; iter = iter->next) + kmscon_input_device_sleep(iter); - kmscon_input_device_disconnect_eloop(device); - close(device->rfd); - device->rfd = -1; + input->state = INPUT_ASLEEP; } -int kmscon_input_device_connect_eloop(struct kmscon_input_device *device) +void kmscon_input_wake_up(struct kmscon_input *input) { + struct kmscon_input_device *iter, *prev, *tmp; int ret; - if (!device || !device->input || !device->input->loop) - return -EINVAL; - - if (device->fd) - return -EALREADY; - - ret = kmscon_eloop_new_fd(device->input->loop, &device->fd, - device->rfd, KMSCON_READABLE, - device_data_arrived, device); - if (ret) - return ret; - - return 0; -} + if (!input) + return; -static void device_data_arrived(struct kmscon_fd *fd, int mask, void *data) -{ - int i; - ssize_t len, n; - struct kmscon_input_device *device = data; - /* 16 is what xf86-input-evdev uses (NUM_EVENTS) */ - struct input_event ev[16]; + prev = NULL; + iter = input->devices; - len = sizeof(ev); - while (len == sizeof(ev)) { - /* we're always supposed to get full events */ - len = read(device->rfd, &ev, sizeof(ev)); - if (len <= 0 || len%sizeof(*ev) != 0) - break; + while (iter) { + ret = kmscon_input_device_wake_up(iter); + if (ret) { + if (!prev) + input->devices = iter->next; + else + prev->next = iter->next; - /* this shouldn't happen */ - if (device->input->state != INPUT_AWAKE) - continue; + tmp = iter; + iter = iter->next; - n = len/sizeof(*ev); - for (i=0; i < n; i++) - device->input->cb(ev[i].type, ev[i].code, ev[i].value); + log_warning("input: device %s does not wake up, " + "removing device\n", tmp->devnode); + kmscon_input_device_unref(tmp); + } else { + prev = iter; + iter = iter->next; + } } -} - -void kmscon_input_device_disconnect_eloop(struct kmscon_input_device *device) -{ - if (!device || !device->fd) - return; - kmscon_eloop_rm_fd(device->fd); - device->fd = NULL; -} - -void kmscon_input_device_sleep(struct kmscon_input_device *device) -{ - kmscon_input_device_close(device); + input->state = INPUT_AWAKE; } -int kmscon_input_device_wake_up(struct kmscon_input_device *device) +bool kmscon_input_is_asleep(struct kmscon_input *input) { - int ret; - - ret = kmscon_input_device_open(device); - if (ret && ret != -EALREADY) - return ret; - - ret = kmscon_input_device_connect_eloop(device); - if (ret && ret != -EALREADY) { - kmscon_input_device_close(device); - return ret; - } + if (!input) + return false; - return 0; + return input->state == INPUT_ASLEEP; } diff --git a/src/input.h b/src/input.h index bc39997..d77626f 100644 --- a/src/input.h +++ b/src/input.h @@ -47,18 +47,18 @@ #include #include - #include "eloop.h" struct kmscon_input; -struct kmscon_input_device; -typedef void (*kmscon_input_cb) (uint16_t type, uint16_t code, int32_t value); +typedef void (*kmscon_input_cb) (struct kmscon_input *input, uint16_t type, + uint16_t code, int32_t value, void *data); -int kmscon_input_new(struct kmscon_input **out, kmscon_input_cb cb); +int kmscon_input_new(struct kmscon_input **out); void kmscon_input_ref(struct kmscon_input *input); void kmscon_input_unref(struct kmscon_input *input); -int kmscon_input_connect_eloop(struct kmscon_input *input, struct kmscon_eloop *loop); +int kmscon_input_connect_eloop(struct kmscon_input *input, + struct kmscon_eloop *eloop, kmscon_input_cb cb, void *data); void kmscon_input_disconnect_eloop(struct kmscon_input *input); void kmscon_input_sleep(struct kmscon_input *input);