| 1 | /* Copyright (C) 2007 L. Donnie Smith <donnie.smith@gatech.edu> |
|---|
| 2 | * |
|---|
| 3 | * This program is free software; you can redistribute it and/or modify |
|---|
| 4 | * it under the terms of the GNU General Public License as published by |
|---|
| 5 | * the Free Software Foundation; either version 2 of the License, or |
|---|
| 6 | * (at your option) any later version. |
|---|
| 7 | * |
|---|
| 8 | * This program is distributed in the hope that it will be useful, |
|---|
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 11 | * GNU General Public License for more details. |
|---|
| 12 | * |
|---|
| 13 | * You should have received a copy of the GNU General Public License |
|---|
| 14 | * along with this program; if not, write to the Free Software |
|---|
| 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|---|
| 16 | * |
|---|
| 17 | */ |
|---|
| 18 | |
|---|
| 19 | #include <stdint.h> |
|---|
| 20 | #include <string.h> |
|---|
| 21 | |
|---|
| 22 | #include <fcntl.h> |
|---|
| 23 | #include <sys/ioctl.h> |
|---|
| 24 | #include <sys/types.h> |
|---|
| 25 | #include <unistd.h> |
|---|
| 26 | |
|---|
| 27 | #include <linux/input.h> |
|---|
| 28 | #include <linux/uinput.h> |
|---|
| 29 | |
|---|
| 30 | #include "conf.h" |
|---|
| 31 | #include "util.h" |
|---|
| 32 | |
|---|
| 33 | /* UInput */ |
|---|
| 34 | char *uinput_filename[] = {"/dev/uinput", "/dev/input/uinput", |
|---|
| 35 | "/dev/misc/uinput"}; |
|---|
| 36 | #define UINPUT_FILENAME_COUNT (sizeof(uinput_filename)/sizeof(char *)) |
|---|
| 37 | |
|---|
| 38 | int uinput_open(struct conf *conf) |
|---|
| 39 | { |
|---|
| 40 | unsigned int i; |
|---|
| 41 | int j; |
|---|
| 42 | int request; |
|---|
| 43 | |
|---|
| 44 | /* Open uinput device */ |
|---|
| 45 | for (i=0; i < UINPUT_FILENAME_COUNT; i++) { |
|---|
| 46 | if ((conf->fd = open(uinput_filename[i], O_RDWR)) >= 0) { |
|---|
| 47 | break; |
|---|
| 48 | } |
|---|
| 49 | } |
|---|
| 50 | if (conf->fd < 0) { |
|---|
| 51 | wminput_err("unable to open uinput"); |
|---|
| 52 | return -1; |
|---|
| 53 | } |
|---|
| 54 | |
|---|
| 55 | if (write(conf->fd, &conf->dev, sizeof conf->dev) != sizeof conf->dev) { |
|---|
| 56 | wminput_err("error on uinput device setup"); |
|---|
| 57 | close(conf->fd); |
|---|
| 58 | return -1; |
|---|
| 59 | } |
|---|
| 60 | |
|---|
| 61 | if (conf->ff) { |
|---|
| 62 | if (ioctl(conf->fd, UI_SET_EVBIT, EV_FF) < 0) { |
|---|
| 63 | wminput_err("error on uinput ioctl"); |
|---|
| 64 | close(conf->fd); |
|---|
| 65 | return -1; |
|---|
| 66 | } |
|---|
| 67 | if (ioctl(conf->fd, UI_SET_FFBIT, FF_RUMBLE) < 0) { |
|---|
| 68 | wminput_err("error on uinput ioctl"); |
|---|
| 69 | close(conf->fd); |
|---|
| 70 | return -1; |
|---|
| 71 | } |
|---|
| 72 | } |
|---|
| 73 | |
|---|
| 74 | if (ioctl(conf->fd, UI_SET_EVBIT, EV_KEY) < 0) { |
|---|
| 75 | wminput_err("error on uinput ioctl"); |
|---|
| 76 | close(conf->fd); |
|---|
| 77 | return -1; |
|---|
| 78 | } |
|---|
| 79 | |
|---|
| 80 | for (i=0; i < CONF_WM_BTN_COUNT; i++) { |
|---|
| 81 | if (conf->wiimote_bmap[i].active) { |
|---|
| 82 | if (ioctl(conf->fd, UI_SET_KEYBIT, conf->wiimote_bmap[i].action) |
|---|
| 83 | < 0) { |
|---|
| 84 | wminput_err("error on uinput ioctl"); |
|---|
| 85 | close(conf->fd); |
|---|
| 86 | return -1; |
|---|
| 87 | } |
|---|
| 88 | } |
|---|
| 89 | } |
|---|
| 90 | for (i=0; i < CONF_NC_BTN_COUNT; i++) { |
|---|
| 91 | if (conf->nunchuk_bmap[i].active) { |
|---|
| 92 | if (ioctl(conf->fd, UI_SET_KEYBIT, conf->nunchuk_bmap[i].action) |
|---|
| 93 | < 0) { |
|---|
| 94 | wminput_err("error on uinput ioctl"); |
|---|
| 95 | close(conf->fd); |
|---|
| 96 | return -1; |
|---|
| 97 | } |
|---|
| 98 | } |
|---|
| 99 | } |
|---|
| 100 | for (i=0; i < CONF_CC_BTN_COUNT; i++) { |
|---|
| 101 | if (conf->classic_bmap[i].active) { |
|---|
| 102 | if (ioctl(conf->fd, UI_SET_KEYBIT, conf->classic_bmap[i].action) |
|---|
| 103 | < 0) { |
|---|
| 104 | wminput_err("error on uinput ioctl"); |
|---|
| 105 | close(conf->fd); |
|---|
| 106 | return -1; |
|---|
| 107 | } |
|---|
| 108 | } |
|---|
| 109 | } |
|---|
| 110 | for (i=0; i < CONF_AXIS_COUNT; i++) { |
|---|
| 111 | if (conf->amap[i].active) { |
|---|
| 112 | if (ioctl(conf->fd, UI_SET_EVBIT, conf->amap[i].axis_type) < 0) { |
|---|
| 113 | wminput_err("error uinput ioctl"); |
|---|
| 114 | close(conf->fd); |
|---|
| 115 | return -1; |
|---|
| 116 | } |
|---|
| 117 | request = (conf->amap[i].axis_type == EV_ABS) ? UI_SET_ABSBIT |
|---|
| 118 | : UI_SET_RELBIT; |
|---|
| 119 | if (ioctl(conf->fd, request, conf->amap[i].action) < 0) { |
|---|
| 120 | wminput_err("error uinput ioctl"); |
|---|
| 121 | close(conf->fd); |
|---|
| 122 | return -1; |
|---|
| 123 | } |
|---|
| 124 | if ((conf->amap[i].flags & CONF_POINTER) && |
|---|
| 125 | (conf->amap[i].axis_type == EV_ABS) && |
|---|
| 126 | ((conf->amap[i].action == ABS_X) || |
|---|
| 127 | (conf->amap[i].action == ABS_Y) || |
|---|
| 128 | (conf->amap[i].action == ABS_Z))) { |
|---|
| 129 | if (ioctl(conf->fd, UI_SET_EVBIT, EV_REL) < 0) { |
|---|
| 130 | wminput_err("error uinput ioctl"); |
|---|
| 131 | close(conf->fd); |
|---|
| 132 | return -1; |
|---|
| 133 | } |
|---|
| 134 | if (ioctl(conf->fd, UI_SET_RELBIT, conf->amap[i].action) < 0) { |
|---|
| 135 | wminput_err("error uinput ioctl"); |
|---|
| 136 | close(conf->fd); |
|---|
| 137 | return -1; |
|---|
| 138 | } |
|---|
| 139 | } |
|---|
| 140 | } |
|---|
| 141 | } |
|---|
| 142 | for (i=0; i < CONF_MAX_PLUGINS; i++) { |
|---|
| 143 | if (conf->plugins[i].name) { |
|---|
| 144 | for (j=0; j < conf->plugins[i].info->button_count; j++) { |
|---|
| 145 | if (conf->plugins[i].bmap[j].active) { |
|---|
| 146 | if (ioctl(conf->fd, UI_SET_KEYBIT, |
|---|
| 147 | conf->plugins[i].bmap[j].action) < 0) { |
|---|
| 148 | wminput_err("error on uinput ioctl"); |
|---|
| 149 | close(conf->fd); |
|---|
| 150 | return -1; |
|---|
| 151 | } |
|---|
| 152 | } |
|---|
| 153 | } |
|---|
| 154 | for (j=0; j < conf->plugins[i].info->axis_count; j++) { |
|---|
| 155 | if (conf->plugins[i].amap[j].active) { |
|---|
| 156 | if (ioctl(conf->fd, UI_SET_EVBIT, |
|---|
| 157 | conf->plugins[i].amap[j].axis_type) < 0) { |
|---|
| 158 | wminput_err("error uinput ioctl"); |
|---|
| 159 | close(conf->fd); |
|---|
| 160 | return -1; |
|---|
| 161 | } |
|---|
| 162 | request = (conf->plugins[i].amap[j].axis_type == EV_ABS) |
|---|
| 163 | ? UI_SET_ABSBIT |
|---|
| 164 | : UI_SET_RELBIT; |
|---|
| 165 | if (ioctl(conf->fd, request, |
|---|
| 166 | conf->plugins[i].amap[j].action) < 0) { |
|---|
| 167 | wminput_err("error uinput ioctl"); |
|---|
| 168 | close(conf->fd); |
|---|
| 169 | return -1; |
|---|
| 170 | } |
|---|
| 171 | if ((conf->plugins[i].amap[j].flags & CONF_POINTER) && |
|---|
| 172 | (conf->plugins[i].amap[j].axis_type == EV_ABS) && |
|---|
| 173 | ((conf->plugins[i].amap[j].action == ABS_X) || |
|---|
| 174 | (conf->plugins[i].amap[j].action == ABS_Y) || |
|---|
| 175 | (conf->plugins[i].amap[j].action == ABS_Z))) { |
|---|
| 176 | if (ioctl(conf->fd, UI_SET_EVBIT, EV_REL) < 0) { |
|---|
| 177 | wminput_err("error uinput ioctl"); |
|---|
| 178 | close(conf->fd); |
|---|
| 179 | return -1; |
|---|
| 180 | } |
|---|
| 181 | if (ioctl(conf->fd, UI_SET_RELBIT, |
|---|
| 182 | conf->plugins[i].amap[j].action) < 0) { |
|---|
| 183 | wminput_err("error uinput ioctl"); |
|---|
| 184 | close(conf->fd); |
|---|
| 185 | return -1; |
|---|
| 186 | } |
|---|
| 187 | } |
|---|
| 188 | } |
|---|
| 189 | } |
|---|
| 190 | } |
|---|
| 191 | } |
|---|
| 192 | |
|---|
| 193 | if (ioctl(conf->fd, UI_DEV_CREATE) < 0) { |
|---|
| 194 | wminput_err("Error on uinput dev create"); |
|---|
| 195 | close(conf->fd); |
|---|
| 196 | return -1; |
|---|
| 197 | } |
|---|
| 198 | |
|---|
| 199 | return 0; |
|---|
| 200 | } |
|---|
| 201 | |
|---|
| 202 | int uinput_close(struct conf *conf) |
|---|
| 203 | { |
|---|
| 204 | if (close(conf->fd)) { |
|---|
| 205 | wminput_err("Error on uinput close"); |
|---|
| 206 | return -1; |
|---|
| 207 | } |
|---|
| 208 | |
|---|
| 209 | return 0; |
|---|
| 210 | } |
|---|
| 211 | |
|---|
| 212 | int send_event(struct conf *conf, __u16 type, __u16 code, __s32 value) |
|---|
| 213 | { |
|---|
| 214 | struct input_event event; |
|---|
| 215 | |
|---|
| 216 | memset(&event, 0, sizeof(event)); |
|---|
| 217 | event.type = type; |
|---|
| 218 | event.code = code; |
|---|
| 219 | event.value = value; |
|---|
| 220 | |
|---|
| 221 | if (write(conf->fd, &event, sizeof(event)) != sizeof(event)) { |
|---|
| 222 | wminput_err("Error on send_event"); |
|---|
| 223 | return -1; |
|---|
| 224 | } |
|---|
| 225 | |
|---|
| 226 | return 0; |
|---|
| 227 | } |
|---|
| 228 | |
|---|
| 229 | void *uinput_listen(struct uinput_listen_data *data) |
|---|
| 230 | { |
|---|
| 231 | size_t len; |
|---|
| 232 | struct input_event event; |
|---|
| 233 | struct uinput_ff_upload upload; |
|---|
| 234 | struct uinput_ff_erase erase; |
|---|
| 235 | |
|---|
| 236 | do { |
|---|
| 237 | if ((len = read(data->conf->fd, &event, sizeof event)) != |
|---|
| 238 | sizeof event) { |
|---|
| 239 | wminput_err("Error on uinput read"); |
|---|
| 240 | continue; |
|---|
| 241 | } |
|---|
| 242 | |
|---|
| 243 | switch (event.type) { |
|---|
| 244 | case EV_UINPUT: |
|---|
| 245 | switch (event.code) { |
|---|
| 246 | case UI_FF_UPLOAD: |
|---|
| 247 | erase.request_id = event.value; |
|---|
| 248 | if (ioctl(data->conf->fd, UI_BEGIN_FF_UPLOAD, &upload) < 0) { |
|---|
| 249 | wminput_err("Error on ff upload begin"); |
|---|
| 250 | } |
|---|
| 251 | if (cwiid_set_rumble(data->wiimote, 1)) { |
|---|
| 252 | wminput_err("Error setting rumble"); |
|---|
| 253 | } |
|---|
| 254 | if (ioctl(data->conf->fd, UI_END_FF_UPLOAD, &upload) < 0) { |
|---|
| 255 | wminput_err("Error on ff upload end"); |
|---|
| 256 | } |
|---|
| 257 | break; |
|---|
| 258 | case UI_FF_ERASE: |
|---|
| 259 | erase.request_id = event.value; |
|---|
| 260 | if (ioctl(data->conf->fd, UI_BEGIN_FF_ERASE, &erase) < 0) { |
|---|
| 261 | wminput_err("Error on ff erase begin"); |
|---|
| 262 | } |
|---|
| 263 | if (cwiid_set_rumble(data->wiimote, 0)) { |
|---|
| 264 | wminput_err("Error clearing rumble"); |
|---|
| 265 | } |
|---|
| 266 | if (ioctl(data->conf->fd, UI_END_FF_ERASE, &erase) < 0) { |
|---|
| 267 | wminput_err("Error on ff erase end"); |
|---|
| 268 | } |
|---|
| 269 | break; |
|---|
| 270 | default: |
|---|
| 271 | break; |
|---|
| 272 | } |
|---|
| 273 | break; |
|---|
| 274 | default: |
|---|
| 275 | break; |
|---|
| 276 | } |
|---|
| 277 | } while (-1); |
|---|
| 278 | } |
|---|