]> code.delx.au - pulseaudio/blob - polyp/module-mmkbd-evdev.c
758aaae5459feefc78be984aa2b317823b0c3fd7
[pulseaudio] / polyp / module-mmkbd-evdev.c
1 /* $Id$ */
2
3 /***
4 This file is part of polypaudio.
5
6 polypaudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2 of the License,
9 or (at your option) any later version.
10
11 polypaudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with polypaudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <assert.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <fcntl.h>
33
34 #include <linux/input.h>
35
36 #include "module.h"
37 #include "log.h"
38 #include "module-mmkbd-evdev-symdef.h"
39 #include "namereg.h"
40 #include "sink.h"
41 #include "xmalloc.h"
42 #include "modargs.h"
43 #include "util.h"
44
45 PA_MODULE_AUTHOR("Lennart Poettering")
46 PA_MODULE_DESCRIPTION("Multimedia keyboard support via Linux evdev")
47 PA_MODULE_VERSION(PACKAGE_VERSION)
48 PA_MODULE_USAGE("device=<evdev device> sink=<sink name>")
49
50 #define DEFAULT_DEVICE "/dev/input/event0"
51
52 static const char* const valid_modargs[] = {
53 "device",
54 "sink",
55 NULL,
56 };
57
58 struct userdata {
59 int fd;
60 struct pa_io_event *io;
61 char *sink_name;
62 struct pa_module *module;
63 float mute_toggle_save;
64 };
65
66 static void io_callback(struct pa_mainloop_api *io, struct pa_io_event *e, int fd, enum pa_io_event_flags events, void*userdata) {
67 struct userdata *u = userdata;
68 assert(io);
69 assert(u);
70
71 if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
72 pa_log(__FILE__": lost connection to evdev device.\n");
73 goto fail;
74 }
75
76 if (events & PA_IO_EVENT_INPUT) {
77 struct input_event e;
78
79 if (pa_loop_read(u->fd, &e, sizeof(e)) <= 0) {
80 pa_log(__FILE__": failed to read from event device: %s\n", strerror(errno));
81 goto fail;
82 }
83
84 if (e.type == EV_KEY && (e.value == 1 || e.value == 2)) {
85 enum { INVALID, UP, DOWN, MUTE_TOGGLE } volchange = INVALID;
86
87 pa_log_debug(__FILE__": key code=%u, value=%u\n", e.code, e.value);
88
89 switch (e.code) {
90 case KEY_VOLUMEDOWN: volchange = DOWN; break;
91 case KEY_VOLUMEUP: volchange = UP; break;
92 case KEY_MUTE: volchange = MUTE_TOGGLE; break;
93 }
94
95 if (volchange != INVALID) {
96 struct pa_sink *s;
97
98 if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1)))
99 pa_log(__FILE__": failed to get sink '%s'\n", u->sink_name);
100 else {
101 double v = pa_volume_to_user(s->volume);
102
103 switch (volchange) {
104 case UP: v += .05; break;
105 case DOWN: v -= .05; break;
106 case MUTE_TOGGLE: {
107
108 if (v > 0) {
109 u->mute_toggle_save = v;
110 v = 0;
111 } else
112 v = u->mute_toggle_save;
113 }
114 default:
115 ;
116 }
117
118 pa_sink_set_volume(s, pa_volume_from_user(v));
119 }
120 }
121 }
122 }
123
124 return;
125
126 fail:
127 u->module->core->mainloop->io_free(u->io);
128 u->io = NULL;
129
130 pa_module_unload_request(u->module);
131 }
132
133 #define test_bit(bit, array) (array[bit/8] & (1<<(bit%8)))
134
135 int pa__init(struct pa_core *c, struct pa_module*m) {
136 struct pa_modargs *ma = NULL;
137 struct userdata *u;
138 int version;
139 struct input_id input_id;
140 char name[256];
141 uint8_t evtype_bitmask[EV_MAX/8 + 1];
142 assert(c && m);
143
144 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
145 pa_log(__FILE__": Failed to parse module arguments\n");
146 goto fail;
147 }
148
149 m->userdata = u = pa_xmalloc(sizeof(struct userdata));
150 u->module = m;
151 u->io = NULL;
152 u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
153 u->fd = -1;
154 u->mute_toggle_save = 0;
155
156 if ((u->fd = open(pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), O_RDONLY)) < 0) {
157 pa_log(__FILE__": failed to open evdev device: %s\n", strerror(errno));
158 goto fail;
159 }
160
161 if (ioctl(u->fd, EVIOCGVERSION, &version) < 0) {
162 pa_log(__FILE__": EVIOCGVERSION failed: %s\n", strerror(errno));
163 goto fail;
164 }
165
166 pa_log_info(__FILE__": evdev driver version %i.%i.%i\n", version >> 16, (version >> 8) & 0xff, version & 0xff);
167
168 if(ioctl(u->fd, EVIOCGID, &input_id)) {
169 pa_log(__FILE__": EVIOCGID failed: %s\n", strerror(errno));
170 goto fail;
171 }
172
173 pa_log_info(__FILE__": evdev vendor 0x%04hx product 0x%04hx version 0x%04hx bustype %u\n",
174 input_id.vendor, input_id.product, input_id.version, input_id.bustype);
175
176 if(ioctl(u->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
177 pa_log(__FILE__": EVIOCGNAME failed: %s\n", strerror(errno));
178 goto fail;
179 }
180
181 pa_log_info(__FILE__": evdev device name: %s\n", name);
182
183 memset(evtype_bitmask, 0, sizeof(evtype_bitmask));
184 if (ioctl(u->fd, EVIOCGBIT(0, EV_MAX), evtype_bitmask) < 0) {
185 pa_log(__FILE__": EVIOCGBIT failed: %s\n", strerror(errno));
186 goto fail;
187 }
188
189 if (!test_bit(EV_KEY, evtype_bitmask)) {
190 pa_log(__FILE__": device has no keys.\n");
191 goto fail;
192 }
193
194 u->io = c->mainloop->io_new(c->mainloop, u->fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);
195
196 pa_modargs_free(ma);
197
198 return 0;
199
200 fail:
201
202 if (ma)
203 pa_modargs_free(ma);
204
205 pa__done(c, m);
206 return -1;
207 }
208
209 void pa__done(struct pa_core *c, struct pa_module*m) {
210 struct userdata *u;
211 assert(c);
212 assert(m);
213
214 if (!(u = m->userdata))
215 return;
216
217 if (u->io)
218 m->core->mainloop->io_free(u->io);
219
220 if (u->fd >= 0)
221 close(u->fd);
222
223 pa_xfree(u->sink_name);
224 pa_xfree(u);
225 }