1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2005-2006 Lennart Poettering 5 6 PulseAudio 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.1 of the License, 9 or (at your option) any later version. 10 11 PulseAudio 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 PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <stdio.h> 25#include <unistd.h> 26#include <string.h> 27#include <stdlib.h> 28 29#include <pulse/xmalloc.h> 30 31#include <pulsecore/module.h> 32#include <pulsecore/log.h> 33#include <pulsecore/namereg.h> 34#include <pulsecore/sink.h> 35#include <pulsecore/modargs.h> 36#include <pulsecore/macro.h> 37 38PA_MODULE_AUTHOR("Lennart Poettering"); 39PA_MODULE_DESCRIPTION("LIRC volume control"); 40PA_MODULE_VERSION(PACKAGE_VERSION); 41PA_MODULE_LOAD_ONCE(true); 42PA_MODULE_USAGE("config=<config file> sink=<sink name> appname=<lirc application name> volume_limit=<volume limit> volume_step=<volume change step>"); 43 44/* LIRC would provide it's own definition of PACKAGE_VERSION, include it after 45 * pulseaudio module definition block to prevent module loader version mismatch. 46 */ 47#include <lirc/lirc_client.h> 48 49static const char* const valid_modargs[] = { 50 "config", 51 "sink", 52 "appname", 53 "volume_limit", 54 "volume_step", 55 NULL, 56}; 57 58struct userdata { 59 int lirc_fd; 60 pa_io_event *io; 61 struct lirc_config *config; 62 char *sink_name; 63 pa_module *module; 64 float mute_toggle_save; 65 pa_volume_t volume_limit; 66 pa_volume_t volume_step; 67}; 68 69static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void*userdata) { 70 struct userdata *u = userdata; 71 char *name = NULL, *code = NULL; 72 73 pa_assert(io); 74 pa_assert(u); 75 76 if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) { 77 pa_log("Lost connection to LIRC daemon."); 78 goto fail; 79 } 80 81 if (events & PA_IO_EVENT_INPUT) { 82 char *c; 83 84 if (lirc_nextcode(&code) != 0 || !code) { 85 pa_log("lirc_nextcode() failed."); 86 goto fail; 87 } 88 89 c = pa_xstrdup(code); 90 c[strcspn(c, "\n\r")] = 0; 91 pa_log_debug("Raw IR code '%s'", c); 92 pa_xfree(c); 93 94 while (lirc_code2char(u->config, code, &name) == 0 && name) { 95 enum { 96 INVALID, 97 UP, 98 DOWN, 99 MUTE, 100 RESET, 101 MUTE_TOGGLE 102 } volchange = INVALID; 103 104 pa_log_info("Translated IR code '%s'", name); 105 106 if (strcasecmp(name, "volume-up") == 0) 107 volchange = UP; 108 else if (strcasecmp(name, "volume-down") == 0) 109 volchange = DOWN; 110 else if (strcasecmp(name, "mute") == 0) 111 volchange = MUTE; 112 else if (strcasecmp(name, "mute-toggle") == 0) 113 volchange = MUTE_TOGGLE; 114 else if (strcasecmp(name, "reset") == 0) 115 volchange = RESET; 116 117 if (volchange == INVALID) 118 pa_log_warn("Received unknown IR code '%s'", name); 119 else { 120 pa_sink *s; 121 122 if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) 123 pa_log("Failed to get sink '%s'", u->sink_name); 124 else { 125 pa_cvolume cv = *pa_sink_get_volume(s, false); 126 127 switch (volchange) { 128 case UP: 129 pa_cvolume_inc_clamp(&cv, u->volume_step, u->volume_limit); 130 pa_sink_set_volume(s, &cv, true, true); 131 break; 132 133 case DOWN: 134 pa_cvolume_dec(&cv, u->volume_step); 135 pa_sink_set_volume(s, &cv, true, true); 136 break; 137 138 case MUTE: 139 pa_sink_set_mute(s, true, true); 140 break; 141 142 case RESET: 143 pa_sink_set_mute(s, false, true); 144 break; 145 146 case MUTE_TOGGLE: 147 pa_sink_set_mute(s, !pa_sink_get_mute(s, false), true); 148 break; 149 150 case INVALID: 151 pa_assert_not_reached(); 152 } 153 } 154 } 155 } 156 } 157 158 pa_xfree(code); 159 160 return; 161 162fail: 163 u->module->core->mainloop->io_free(u->io); 164 u->io = NULL; 165 166 pa_module_unload_request(u->module, true); 167 168 pa_xfree(code); 169} 170 171int pa__init(pa_module*m) { 172 pa_modargs *ma = NULL; 173 struct userdata *u; 174 pa_volume_t volume_limit = PA_CLAMP_VOLUME(PA_VOLUME_NORM*3/2); 175 pa_volume_t volume_step = PA_VOLUME_NORM/20; 176 177 pa_assert(m); 178 179 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 180 pa_log("Failed to parse module arguments"); 181 goto fail; 182 } 183 184 if (pa_modargs_get_value_u32(ma, "volume_limit", &volume_limit) < 0) { 185 pa_log("Failed to parse volume limit"); 186 goto fail; 187 } 188 189 if (pa_modargs_get_value_u32(ma, "volume_step", &volume_step) < 0) { 190 pa_log("Failed to parse volume step"); 191 goto fail; 192 } 193 194 m->userdata = u = pa_xnew(struct userdata, 1); 195 u->module = m; 196 u->io = NULL; 197 u->config = NULL; 198 u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); 199 u->lirc_fd = -1; 200 u->mute_toggle_save = 0; 201 u->volume_limit = PA_CLAMP_VOLUME(volume_limit); 202 u->volume_step = PA_CLAMP_VOLUME(volume_step); 203 204 if ((u->lirc_fd = lirc_init((char*) pa_modargs_get_value(ma, "appname", "pulseaudio"), 1)) < 0) { 205 pa_log("lirc_init() failed."); 206 goto fail; 207 } 208 209 if (lirc_readconfig((char*) pa_modargs_get_value(ma, "config", NULL), &u->config, NULL) < 0) { 210 pa_log("lirc_readconfig() failed."); 211 goto fail; 212 } 213 214 u->io = m->core->mainloop->io_new(m->core->mainloop, u->lirc_fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u); 215 216 pa_modargs_free(ma); 217 218 return 0; 219 220fail: 221 222 if (ma) 223 pa_modargs_free(ma); 224 225 pa__done(m); 226 return -1; 227} 228 229void pa__done(pa_module*m) { 230 struct userdata *u; 231 pa_assert(m); 232 233 if (!(u = m->userdata)) 234 return; 235 236 if (u->io) 237 m->core->mainloop->io_free(u->io); 238 239 if (u->config) 240 lirc_freeconfig(u->config); 241 242 if (u->lirc_fd >= 0) 243 lirc_deinit(); 244 245 pa_xfree(u->sink_name); 246 pa_xfree(u); 247} 248