#include "output_dev.h" #include "queue.h" #include "message.h" #include #include int create_output_dev(const char* uinput_path, const char* name, output_dev_type_t type) { int fd = open(uinput_path, O_WRONLY | O_NONBLOCK); if(fd < 0) { fd = -1; close(fd); goto create_output_dev_err; } struct uinput_setup dev = {0}; if (strlen(name) < UINPUT_MAX_NAME_SIZE) { strcpy(dev.name, name); } else { strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE-1); } dev.id.bustype = BUS_VIRTUAL; dev.id.vendor = OUTPUT_DEV_VENDOR_ID; dev.id.product = OUTPUT_DEV_PRODUCT_ID; dev.id.version = OUTPUT_DEV_VERSION; if (ioctl(fd, /*UI_SET_PHYS_STR*/ 18, PHYS_STR) != 0) { fprintf(stderr, "Controller and gyroscope will NOT be recognized as a single device!\n"); } switch (type) { case output_dev_imu: { ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_ACCELEROMETER); ioctl(fd, UI_SET_EVBIT, EV_ABS); //ioctl(fd, UI_SET_EVBIT, EV_KEY); ioctl(fd, UI_SET_EVBIT, EV_MSC); ioctl(fd, UI_SET_MSCBIT, MSC_TIMESTAMP); ioctl(fd, UI_SET_ABSBIT, ABS_X); ioctl(fd, UI_SET_ABSBIT, ABS_Y); ioctl(fd, UI_SET_ABSBIT, ABS_Z); ioctl(fd, UI_SET_ABSBIT, ABS_RX); ioctl(fd, UI_SET_ABSBIT, ABS_RY); ioctl(fd, UI_SET_ABSBIT, ABS_RZ); //ioctl(fd, UI_SET_KEYBIT, BTN_TRIGGER); //ioctl(fd, UI_SET_KEYBIT, BTN_THUMB); struct uinput_abs_setup devAbsX = {0}; devAbsX.code = ABS_X; devAbsX.absinfo.minimum = -ACCEL_RANGE; devAbsX.absinfo.maximum = ACCEL_RANGE; devAbsX.absinfo.resolution = 255; // 255 units = 1g devAbsX.absinfo.fuzz = 5; devAbsX.absinfo.flat = 0; if(ioctl(fd, UI_ABS_SETUP, &devAbsX) < 0) { fd = -1; close(fd); goto create_output_dev_err; } struct uinput_abs_setup devAbsY = {0}; devAbsY.code = ABS_Y; devAbsY.absinfo.minimum = -ACCEL_RANGE; devAbsY.absinfo.maximum = ACCEL_RANGE; devAbsY.absinfo.resolution = 255; // 255 units = 1g devAbsY.absinfo.fuzz = 5; devAbsY.absinfo.flat = 0; if(ioctl(fd, UI_ABS_SETUP, &devAbsY) < 0) { fd = -1; close(fd); goto create_output_dev_err; } struct uinput_abs_setup devAbsZ = {0}; devAbsZ.code = ABS_Z; devAbsZ.absinfo.minimum = -ACCEL_RANGE; devAbsZ.absinfo.maximum = ACCEL_RANGE; devAbsZ.absinfo.resolution = 255; // 255 units = 1g devAbsZ.absinfo.fuzz = 5; devAbsZ.absinfo.flat = 0; if(ioctl(fd, UI_ABS_SETUP, &devAbsZ) < 0) { fd = -1; close(fd); goto create_output_dev_err; } struct uinput_abs_setup devAbsRX = {0}; devAbsRX.code = ABS_RX; devAbsRX.absinfo.minimum = -GYRO_RANGE; devAbsRX.absinfo.maximum = GYRO_RANGE; devAbsRX.absinfo.resolution = 1; // 1 unit = 1 degree/s devAbsRX.absinfo.fuzz = 0; devAbsRX.absinfo.flat = GYRO_DEADZONE; if(ioctl(fd, UI_ABS_SETUP, &devAbsRX) < 0) { fd = -1; close(fd); goto create_output_dev_err; } struct uinput_abs_setup devAbsRY = {0}; devAbsRY.code = ABS_RY; devAbsRY.absinfo.minimum = -GYRO_RANGE; devAbsRY.absinfo.maximum = GYRO_RANGE; devAbsRY.absinfo.resolution = 1; // 1 unit = 1 degree/s devAbsRY.absinfo.fuzz = 0; devAbsRY.absinfo.flat = GYRO_DEADZONE; if(ioctl(fd, UI_ABS_SETUP, &devAbsRY) < 0) { fd = -1; close(fd); goto create_output_dev_err; } struct uinput_abs_setup devAbsRZ = {0}; devAbsRZ.code = ABS_RZ; devAbsRZ.absinfo.minimum = -GYRO_RANGE; devAbsRZ.absinfo.maximum = GYRO_RANGE; devAbsRZ.absinfo.resolution = 1; // 1 unit = 1 degree/s devAbsRZ.absinfo.fuzz = 0; devAbsRZ.absinfo.flat = GYRO_DEADZONE; if(ioctl(fd, UI_ABS_SETUP, &devAbsRZ) < 0) { fd = -1; close(fd); goto create_output_dev_err; } if(ioctl(fd, UI_DEV_SETUP, &dev) < 0) { fd = -1; close(fd); goto create_output_dev_err; } if(ioctl(fd, UI_DEV_CREATE) < 0) { fd = -1; close(fd); goto create_output_dev_err; } break; } case output_dev_gamepad: { //ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_BUTTONPAD); ioctl(fd, UI_SET_EVBIT, EV_ABS); ioctl(fd, UI_SET_EVBIT, EV_KEY); ioctl(fd, UI_SET_EVBIT, EV_MSC); ioctl(fd, UI_SET_EVBIT, EV_SYN); #if defined(INCLUDE_TIMESTAMP) ioctl(fd, UI_SET_MSCBIT, MSC_TIMESTAMP); #endif ioctl(fd, UI_SET_ABSBIT, ABS_X); ioctl(fd, UI_SET_ABSBIT, ABS_Y); ioctl(fd, UI_SET_ABSBIT, ABS_Z); ioctl(fd, UI_SET_ABSBIT, ABS_RX); ioctl(fd, UI_SET_ABSBIT, ABS_RY); ioctl(fd, UI_SET_ABSBIT, ABS_RZ); ioctl(fd, UI_SET_ABSBIT, ABS_HAT0X); ioctl(fd, UI_SET_ABSBIT, ABS_HAT0Y); ioctl(fd, UI_SET_ABSBIT, ABS_HAT2X); ioctl(fd, UI_SET_ABSBIT, ABS_HAT2Y); ioctl(fd, UI_SET_KEYBIT, BTN_SOUTH); ioctl(fd, UI_SET_KEYBIT, BTN_EAST); ioctl(fd, UI_SET_KEYBIT, BTN_NORTH); ioctl(fd, UI_SET_KEYBIT, BTN_WEST); ioctl(fd, UI_SET_KEYBIT, BTN_TL); ioctl(fd, UI_SET_KEYBIT, BTN_TR); ioctl(fd, UI_SET_KEYBIT, BTN_TL2); ioctl(fd, UI_SET_KEYBIT, BTN_TR2); ioctl(fd, UI_SET_KEYBIT, BTN_SELECT); ioctl(fd, UI_SET_KEYBIT, BTN_START); ioctl(fd, UI_SET_KEYBIT, BTN_MODE); ioctl(fd, UI_SET_KEYBIT, BTN_THUMBL); ioctl(fd, UI_SET_KEYBIT, BTN_THUMBR); ioctl(fd, UI_SET_KEYBIT, BTN_GEAR_DOWN); ioctl(fd, UI_SET_KEYBIT, BTN_GEAR_UP); ioctl(fd, UI_SET_KEYBIT, BTN_DPAD_UP); ioctl(fd, UI_SET_KEYBIT, BTN_DPAD_DOWN); ioctl(fd, UI_SET_KEYBIT, BTN_DPAD_LEFT); ioctl(fd, UI_SET_KEYBIT, BTN_DPAD_RIGHT); ioctl(fd, UI_SET_KEYBIT, KEY_F12); //ioctl(fd, UI_SET_KEYBIT, KEY_F15); //ioctl(fd, UI_SET_KEYBIT, KEY_F16); //ioctl(fd, UI_SET_KEYBIT, KEY_F17); //ioctl(fd, UI_SET_KEYBIT, KEY_F18); //ioctl(fd, UI_SET_KEYBIT, KEY_PROG1); //ioctl(fd, UI_SET_KEYBIT, KEY_PROG2); const struct uinput_abs_setup devAbsX = { .code = ABS_X, .absinfo = { .value = 0, .minimum = -32768, .maximum = +32768, .resolution = 1, .fuzz = 16, .flat = 128, } }; if(ioctl(fd, UI_ABS_SETUP, &devAbsX) < 0) { fd = -1; close(fd); goto create_output_dev_err; } struct uinput_abs_setup devAbsY = { .code = ABS_Y, .absinfo = { .value = 0, .minimum = -32768, .maximum = +32768, .resolution = 1, .fuzz = 16, .flat = 128, } }; if(ioctl(fd, UI_ABS_SETUP, &devAbsY) < 0) { fd = -1; close(fd); goto create_output_dev_err; } struct uinput_abs_setup devAbsZ = { .code = ABS_Z, .absinfo = { .value = 0, .minimum = 0, .maximum = 255, .resolution = 1, //.fuzz = 16, //.flat = 128, } }; if(ioctl(fd, UI_ABS_SETUP, &devAbsZ) < 0) { fd = -1; close(fd); goto create_output_dev_err; } struct uinput_abs_setup devAbsRX = { .code = ABS_RX, .absinfo = { .value = 0, .minimum = -32768, .maximum = +32768, .resolution = 1, .fuzz = 16, .flat = 128, } }; if(ioctl(fd, UI_ABS_SETUP, &devAbsRX) < 0) { fd = -1; close(fd); goto create_output_dev_err; } struct uinput_abs_setup devAbsRY = { .code = ABS_RY, .absinfo = { .value = -1, .minimum = -32768, .maximum = +32768, .resolution = 1, .fuzz = 16, .flat = 128, } }; if(ioctl(fd, UI_ABS_SETUP, &devAbsRY) < 0) { fd = -1; close(fd); goto create_output_dev_err; } struct uinput_abs_setup devAbsRZ = { .code = ABS_RZ, .absinfo = { .value = 0, .minimum = 0, .maximum = 255, .resolution = 1, //.fuzz = 16, //.flat = 128, } }; if(ioctl(fd, UI_ABS_SETUP, &devAbsRZ) < 0) { fd = -1; close(fd); goto create_output_dev_err; } struct uinput_abs_setup devAbsHat0X = { .code = ABS_HAT0X, .absinfo = { .value = 0, .minimum = -1, .maximum = 1, .resolution = 1, //.fuzz = 16, //.flat = 128, } }; if(ioctl(fd, UI_ABS_SETUP, &devAbsHat0X) < 0) { fd = -1; close(fd); goto create_output_dev_err; } struct uinput_abs_setup devAbsHat0Y = { .code = ABS_HAT0Y, .absinfo = { .value = 0, .minimum = -1, .maximum = 1, .resolution = 1, //.fuzz = 16, //.flat = 128, } }; if(ioctl(fd, UI_ABS_SETUP, &devAbsHat0Y) < 0) { fd = -1; close(fd); goto create_output_dev_err; } struct uinput_abs_setup devAbsHat2X = { .code = ABS_HAT2X, .absinfo = { .value = 0, .minimum = 0, .maximum = 255, .resolution = 51, //.fuzz = 16, //.flat = 128, } }; if(ioctl(fd, UI_ABS_SETUP, &devAbsHat2X) < 0) { fd = -1; close(fd); goto create_output_dev_err; } struct uinput_abs_setup devAbsHat2Y = { .code = ABS_HAT2Y, .absinfo = { .value = 0, .minimum = 0, .maximum = 255, .resolution = 51, //.fuzz = 16, //.flat = 128, } }; if(ioctl(fd, UI_ABS_SETUP, &devAbsHat2Y) < 0) { fd = -1; close(fd); goto create_output_dev_err; } if(ioctl(fd, UI_DEV_SETUP, &dev) < 0) { fd = -1; close(fd); goto create_output_dev_err; } if(ioctl(fd, UI_DEV_CREATE) < 0) { fd = -1; close(fd); goto create_output_dev_err; } break; } default: // error close(fd); fd = -1; goto create_output_dev_err; } create_output_dev_err: return fd; } void *output_dev_thread_func(void *ptr) { output_dev_t *out_dev = (output_dev_t*)ptr; struct timeval now = {0}; const int fd = out_dev->fd; #if defined(INCLUDE_TIMESTAMP) gettimeofday(&now, NULL); __time_t secAtInit = now.tv_sec; __time_t usecAtInit = now.tv_usec; #endif for (;;) { void *raw_ev; const int pop_res = queue_pop_timeout(out_dev->queue, &raw_ev, 1000); if (pop_res == 0) { message_t *const msg = (message_t*)raw_ev; for (uint32_t i = 0; i < msg->ev_count; ++i) { struct input_event ev = { .code = msg->ev[i].code, .type = msg->ev[i].type, .value = msg->ev[i].value, .time = msg->ev[i].time, }; if ((msg->flags & INPUT_FILTER_FLAGS_PRESERVE_TIME) == 0) { gettimeofday(&now, NULL); ev.time = now; } /* if ((ev.type == EV_KEY) && (ev.code == KEY_PROG1)) { // To be wired to F16 ev.code = KEY_F12; } */ #if defined(INCLUDE_OUTPUT_DEBUG) printf( "Output: Received event %s (%s): %d\n", libevdev_event_type_get_name(ev.type), libevdev_event_code_get_name(ev.type, ev.code), ev.value ); #endif const ssize_t written = write(fd, (void*)&ev, sizeof(ev)); if (written != sizeof(ev)) { fprintf( stderr, "Error writing event %s %s %d: written %ld bytes out of %ld\n", libevdev_event_type_get_name(ev.type), libevdev_event_code_get_name(ev.type, ev.code), ev.value, written, sizeof(ev) ); } } #if defined(INCLUDE_TIMESTAMP) gettimeofday(&now, NULL); const struct input_event timestamp_ev = { .code = MSC_TIMESTAMP, .type = EV_MSC, .value = (now.tv_sec - secAtInit)*1000000 + (now.tv_usec - usecAtInit), .time = now, }; const ssize_t timestamp_written = write(fd, (void*)×tamp_ev, sizeof(timestamp_ev)); if (timestamp_written != sizeof(timestamp_ev)) { fprintf(stderr, "Error in sync: written %ld bytes out of %ld\n", timestamp_written, sizeof(timestamp_ev)); } #endif gettimeofday(&now, NULL); const struct input_event syn_ev = { .code = SYN_REPORT, .type = EV_SYN, .value = 0, .time = now, }; const ssize_t sync_written = write(fd, (void*)&syn_ev, sizeof(syn_ev)); if (sync_written != sizeof(syn_ev)) { fprintf(stderr, "Error in sync: written %ld bytes out of %ld\n", sync_written, sizeof(syn_ev)); } // from now on it's forbidden to use this memory msg->flags |= MESSAGE_FLAGS_HANDLE_DONE; } else if (pop_res == -1) { // timed out read } else { fprintf(stderr, "Cannot read from input queue: %d\n", pop_res); continue; } const uint32_t flags = out_dev->crtl_flags; if (flags & OUTPUT_DEV_CTRL_FLAG_EXIT) { out_dev->crtl_flags &= ~OUTPUT_DEV_CTRL_FLAG_EXIT; break; } } ioctl(fd, UI_DEV_DESTROY); close(fd); return NULL; }