combjs

annotate combjs.c @ 0:15293518d92b

first version, combined joystick seems to be working, max 32 buttons.
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 13 Jul 2011 07:12:57 +0300
parents
children dd02002227a2
rev   line source
nuclear@0 1 /*
nuclear@0 2 combjs - combine multiple joysticks in one virtual joystick
nuclear@0 3 Copyright (C) 2011 John Tsiombikas <nuclear@member.fsf.org>
nuclear@0 4
nuclear@0 5 This program is free software: you can redistribute it and/or modify
nuclear@0 6 it under the terms of the GNU General Public License as published by
nuclear@0 7 the Free Software Foundation, either version 3 of the License, or
nuclear@0 8 (at your option) any later version.
nuclear@0 9
nuclear@0 10 This program is distributed in the hope that it will be useful,
nuclear@0 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
nuclear@0 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
nuclear@0 13 GNU General Public License for more details.
nuclear@0 14
nuclear@0 15 You should have received a copy of the GNU General Public License
nuclear@0 16 along with this program. If not, see <http://www.gnu.org/licenses/>.
nuclear@0 17 */
nuclear@0 18
nuclear@0 19 #include <stdio.h>
nuclear@0 20 #include <stdlib.h>
nuclear@0 21 #include <string.h>
nuclear@0 22 #include <errno.h>
nuclear@0 23 #include <limits.h>
nuclear@0 24 #include <signal.h>
nuclear@0 25 #include <unistd.h>
nuclear@0 26 #include <fcntl.h>
nuclear@0 27 #include <sys/select.h>
nuclear@0 28 #include <sys/ioctl.h>
nuclear@0 29 #include <linux/joystick.h>
nuclear@0 30 #include <linux/input.h>
nuclear@0 31 #include <linux/uinput.h>
nuclear@0 32
nuclear@0 33 #define DBG_MAX_BN 32
nuclear@0 34
nuclear@0 35 struct joystick {
nuclear@0 36 char *name;
nuclear@0 37 int fd;
nuclear@0 38 int num_bn, num_axes;
nuclear@0 39 int bn_offset, axis_offset;
nuclear@0 40
nuclear@0 41 struct joystick *next;
nuclear@0 42 };
nuclear@0 43
nuclear@0 44 void cleanup(void);
nuclear@0 45 int create_virt_js(const char *uinput_dev, const char *devname);
nuclear@0 46 int handle_event(struct joystick *js, struct js_event *ev);
nuclear@0 47 int add_joystick(const char *devfile);
nuclear@0 48 void sig_handler(int sig);
nuclear@0 49 int proc_cmdline(int argc, char **argv);
nuclear@0 50 void print_usage(const char *argv0);
nuclear@0 51
nuclear@0 52
nuclear@0 53 int vfd = -1;
nuclear@0 54 const char *virt_dev_name = "combjs - virtual multi-joystick combiner";
nuclear@0 55 const char *uinput_dev_name = "/dev/uinput";
nuclear@0 56 struct joystick *jslist, *jstail;
nuclear@0 57 int num_vaxes, num_vbn;
nuclear@0 58
nuclear@0 59
nuclear@0 60 int main(int argc, char **argv)
nuclear@0 61 {
nuclear@0 62 signal(SIGINT, sig_handler);
nuclear@0 63 signal(SIGQUIT, sig_handler);
nuclear@0 64 signal(SIGTERM, sig_handler);
nuclear@0 65
nuclear@0 66 if(proc_cmdline(argc, argv) == -1) {
nuclear@0 67 return 1;
nuclear@0 68 }
nuclear@0 69
nuclear@0 70 if((vfd = create_virt_js(uinput_dev_name, virt_dev_name)) == -1) {
nuclear@0 71 cleanup();
nuclear@0 72 return 1;
nuclear@0 73 }
nuclear@0 74
nuclear@0 75 for(;;) {
nuclear@0 76 fd_set rdset;
nuclear@0 77 struct joystick *js = jslist;
nuclear@0 78 int maxfd = jstail->fd; /* XXX precalc */
nuclear@0 79
nuclear@0 80 FD_ZERO(&rdset);
nuclear@0 81 while(js) {
nuclear@0 82 FD_SET(js->fd, &rdset);
nuclear@0 83 if(js->fd > maxfd) {
nuclear@0 84 maxfd = js->fd;
nuclear@0 85 }
nuclear@0 86 js = js->next;
nuclear@0 87 }
nuclear@0 88
nuclear@0 89 while(select(maxfd + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR);
nuclear@0 90
nuclear@0 91 js = jslist;
nuclear@0 92 while(js) {
nuclear@0 93 if(FD_ISSET(js->fd, &rdset)) {
nuclear@0 94 struct js_event ev;
nuclear@0 95 if(read(js->fd, &ev, sizeof ev) > 0) {
nuclear@0 96 handle_event(js, &ev);
nuclear@0 97 }
nuclear@0 98 }
nuclear@0 99 js = js->next;
nuclear@0 100 }
nuclear@0 101 }
nuclear@0 102
nuclear@0 103 return 0;
nuclear@0 104 }
nuclear@0 105
nuclear@0 106 void cleanup(void)
nuclear@0 107 {
nuclear@0 108 while(jslist) {
nuclear@0 109 struct joystick *js = jslist;
nuclear@0 110 jslist = jslist->next;
nuclear@0 111
nuclear@0 112 close(js->fd);
nuclear@0 113 free(js->name);
nuclear@0 114 free(js);
nuclear@0 115 }
nuclear@0 116
nuclear@0 117 if(vfd >= 0) {
nuclear@0 118 ioctl(vfd, UI_DEV_DESTROY);
nuclear@0 119 close(vfd);
nuclear@0 120 }
nuclear@0 121 }
nuclear@0 122
nuclear@0 123 int create_virt_js(const char *uinput_dev, const char *devname)
nuclear@0 124 {
nuclear@0 125 int i, fd;
nuclear@0 126 struct uinput_user_dev uidev;
nuclear@0 127
nuclear@0 128 if((fd = open(uinput_dev, O_WRONLY | O_NONBLOCK)) == -1) {
nuclear@0 129 perror("failed to open uinput device");
nuclear@0 130 return -1;
nuclear@0 131 }
nuclear@0 132
nuclear@0 133 if(ioctl(fd, UI_SET_EVBIT, EV_ABS) == -1) {
nuclear@0 134 close(fd);
nuclear@0 135 return -1;
nuclear@0 136 }
nuclear@0 137 if(ioctl(fd, UI_SET_EVBIT, EV_KEY) == -1) {
nuclear@0 138 close(fd);
nuclear@0 139 return -1;
nuclear@0 140 }
nuclear@0 141 if(ioctl(fd, UI_SET_EVBIT, EV_SYN) == -1) {
nuclear@0 142 close(fd);
nuclear@0 143 return -1;
nuclear@0 144 }
nuclear@0 145
nuclear@0 146 memset(&uidev, 0, sizeof uidev);
nuclear@0 147 strncpy(uidev.name, devname, UINPUT_MAX_NAME_SIZE);
nuclear@0 148 uidev.id.bustype = BUS_USB;
nuclear@0 149 uidev.id.vendor = 0x1234;
nuclear@0 150 uidev.id.product = 0xfedc;
nuclear@0 151 uidev.id.version = 1;
nuclear@0 152
nuclear@0 153 for(i=0; i<num_vbn; i++) {
nuclear@0 154 if(i >= DBG_MAX_BN) break;
nuclear@0 155 ioctl(fd, UI_SET_KEYBIT, i + BTN_0);
nuclear@0 156 }
nuclear@0 157
nuclear@0 158 for(i=0; i<num_vaxes; i++) {
nuclear@0 159 ioctl(fd, UI_SET_ABSBIT, i);
nuclear@0 160 uidev.absmin[i] = SHRT_MIN;
nuclear@0 161 uidev.absmax[i] = SHRT_MAX;
nuclear@0 162 }
nuclear@0 163
nuclear@0 164 if(write(fd, &uidev, sizeof uidev) == -1) {
nuclear@0 165 perror("failed to set virtual device parameters");
nuclear@0 166 close(fd);
nuclear@0 167 return -1;
nuclear@0 168 }
nuclear@0 169
nuclear@0 170 if(ioctl(fd, UI_DEV_CREATE) == -1) {
nuclear@0 171 perror("failed to create the virtual device");
nuclear@0 172 close(fd);
nuclear@0 173 return -1;
nuclear@0 174 }
nuclear@0 175 return fd;
nuclear@0 176 }
nuclear@0 177
nuclear@0 178 int handle_event(struct joystick *js, struct js_event *evin)
nuclear@0 179 {
nuclear@0 180 struct input_event evout;
nuclear@0 181
nuclear@0 182 memset(&evout, 0, sizeof evout);
nuclear@0 183
nuclear@0 184 if(evin->type & JS_EVENT_AXIS) {
nuclear@0 185 evout.type = EV_ABS;
nuclear@0 186 evout.code = evin->number + js->axis_offset;
nuclear@0 187 evout.value = evin->value;
nuclear@0 188
nuclear@0 189 } else if(evin->type & JS_EVENT_BUTTON) {
nuclear@0 190 evout.type = EV_KEY;
nuclear@0 191 evout.code = evin->number + js->bn_offset + BTN_0;
nuclear@0 192 evout.value = evin->value;
nuclear@0 193
nuclear@0 194 if(evout.code >= BTN_0 + DBG_MAX_BN) return 0;
nuclear@0 195
nuclear@0 196 } else {
nuclear@0 197 return 0;
nuclear@0 198 }
nuclear@0 199
nuclear@0 200 if(write(vfd, &evout, sizeof evout) == -1) {
nuclear@0 201 perror("failed to generate virtual joystick event");
nuclear@0 202 return -1;
nuclear@0 203 }
nuclear@0 204 return 0;
nuclear@0 205 }
nuclear@0 206
nuclear@0 207 int add_joystick(const char *devfile)
nuclear@0 208 {
nuclear@0 209 struct joystick *js;
nuclear@0 210 char buf[512];
nuclear@0 211
nuclear@0 212 if(!(js = calloc(1, sizeof *js))) {
nuclear@0 213 perror("failed to allocate memory");
nuclear@0 214 goto err;
nuclear@0 215 }
nuclear@0 216
nuclear@0 217 if((js->fd = open(devfile, O_RDONLY | O_NONBLOCK)) == -1) {
nuclear@0 218 fprintf(stderr, "failed to open %s: %s\n", devfile, strerror(errno));
nuclear@0 219 goto err;
nuclear@0 220 }
nuclear@0 221
nuclear@0 222 if(ioctl(js->fd, JSIOCGNAME(sizeof buf), buf) == -1) {
nuclear@0 223 fprintf(stderr, "failed to get joystick name for %s: %s\n", devfile, strerror(errno));
nuclear@0 224 strcpy(buf, "<unknown>");
nuclear@0 225 }
nuclear@0 226 if(!(js->name = malloc(strlen(buf) + 1))) {
nuclear@0 227 perror("failed to allocate memory");
nuclear@0 228 goto err;
nuclear@0 229 }
nuclear@0 230 strcpy(js->name, buf);
nuclear@0 231
nuclear@0 232 if(ioctl(js->fd, JSIOCGBUTTONS, &js->num_bn) == -1) {
nuclear@0 233 fprintf(stderr, "failed to get joystick %s's number of buttons: %s\n", devfile, strerror(errno));
nuclear@0 234 goto err;
nuclear@0 235 }
nuclear@0 236 if(ioctl(js->fd, JSIOCGAXES, &js->num_axes) == -1) {
nuclear@0 237 fprintf(stderr, "failed to get joystick %s's number of axes: %s\n", devfile, strerror(errno));
nuclear@0 238 goto err;
nuclear@0 239 }
nuclear@0 240
nuclear@0 241 if(!jstail) {
nuclear@0 242 jslist = jstail = js;
nuclear@0 243 } else {
nuclear@0 244 js->axis_offset = jstail->axis_offset + jstail->num_axes;
nuclear@0 245 js->bn_offset = jstail->bn_offset + jstail->num_bn;
nuclear@0 246 jstail->next = js;
nuclear@0 247 jstail = js;
nuclear@0 248 }
nuclear@0 249
nuclear@0 250 num_vaxes = js->axis_offset + js->num_axes;
nuclear@0 251 num_vbn = js->bn_offset + js->num_bn;
nuclear@0 252
nuclear@0 253 printf("added joystick %s: %s\n", devfile, js->name);
nuclear@0 254 printf(" - %d axes (offset: %d)\n", js->num_axes, js->axis_offset);
nuclear@0 255 printf(" - %d buttons (offset: %d)\n", js->num_bn, js->bn_offset);
nuclear@0 256 return 0;
nuclear@0 257
nuclear@0 258 err:
nuclear@0 259 if(js) {
nuclear@0 260 close(js->fd);
nuclear@0 261 free(js->name);
nuclear@0 262 free(js);
nuclear@0 263 }
nuclear@0 264 return -1;
nuclear@0 265 }
nuclear@0 266
nuclear@0 267 void sig_handler(int sig)
nuclear@0 268 {
nuclear@0 269 cleanup();
nuclear@0 270 exit(0);
nuclear@0 271 }
nuclear@0 272
nuclear@0 273 int proc_cmdline(int argc, char **argv)
nuclear@0 274 {
nuclear@0 275 int i;
nuclear@0 276
nuclear@0 277 for(i=1; i<argc; i++) {
nuclear@0 278 if(argv[i][0] == '-') {
nuclear@0 279 if(argv[i][2] == 0) {
nuclear@0 280 switch(argv[i][1]) {
nuclear@0 281 case 'h':
nuclear@0 282 print_usage(argv[0]);
nuclear@0 283 exit(0);
nuclear@0 284
nuclear@0 285 default:
nuclear@0 286 fprintf(stderr, "invalid argument: %s\n", argv[i]);
nuclear@0 287 print_usage(argv[0]);
nuclear@0 288 return -1;
nuclear@0 289 }
nuclear@0 290 } else {
nuclear@0 291 fprintf(stderr, "invalid argument: %s\n", argv[i]);
nuclear@0 292 print_usage(argv[0]);
nuclear@0 293 }
nuclear@0 294 } else {
nuclear@0 295 if(add_joystick(argv[i]) == -1) {
nuclear@0 296 return -1;
nuclear@0 297 }
nuclear@0 298 }
nuclear@0 299 }
nuclear@0 300
nuclear@0 301 if(!jslist) {
nuclear@0 302 fprintf(stderr, "no joystick devices specified\n");
nuclear@0 303 print_usage(argv[0]);
nuclear@0 304 return -1;
nuclear@0 305 }
nuclear@0 306
nuclear@0 307 return 0;
nuclear@0 308 }
nuclear@0 309
nuclear@0 310 void print_usage(const char *argv0)
nuclear@0 311 {
nuclear@0 312 printf("usage: %s <dev 0> [dev1] ... [dev n]\n", argv0);
nuclear@0 313 }