From 6e1865152182b9bfeacacde181abdd895ffa69ba Mon Sep 17 00:00:00 2001 From: zakk4223 Date: Sun, 13 Mar 2022 03:21:19 -0400 Subject: [PATCH] Add support for rumble data from hps_io. Fix data truncation in spi_uio_cmd (#568) Co-authored-by: Zakk --- cfg.cpp | 2 ++ cfg.h | 1 + input.cpp | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ input.h | 6 ++++ spi.cpp | 2 +- user_io.h | 6 ++++ 6 files changed, 120 insertions(+), 1 deletion(-) diff --git a/cfg.cpp b/cfg.cpp index a9f59e9..f5ee87c 100644 --- a/cfg.cpp +++ b/cfg.cpp @@ -91,6 +91,7 @@ static const ini_var_t ini_vars[] = { "BT_AUTO_DISCONNECT", (void*)(&(cfg.bt_auto_disconnect)), UINT32, 0, 180 }, { "BT_RESET_BEFORE_PAIR", (void*)(&(cfg.bt_reset_before_pair)), UINT8, 0, 1 }, { "WAITMOUNT", (void*)(&(cfg.waitmount)), STRING, 0, sizeof(cfg.waitmount) - 1 }, + { "RUMBLE", (void *)(&(cfg.rumble)), UINT8, 0, 1}, }; static const int nvars = (int)(sizeof(ini_vars) / sizeof(ini_var_t)); @@ -349,5 +350,6 @@ void cfg_parse() cfg.controller_info = 6; cfg.browse_expand = 1; cfg.logo = 1; + cfg.rumble = 1; ini_parse(altcfg()); } diff --git a/cfg.h b/cfg.h index 7004ae0..80d9cff 100644 --- a/cfg.h +++ b/cfg.h @@ -69,6 +69,7 @@ typedef struct { char vfilter_vertical_default[1023]; char vfilter_scanlines_default[1023]; char shmask_default[1023]; + uint8_t rumble; } cfg_t; extern cfg_t cfg; diff --git a/input.cpp b/input.cpp index 2a19e5d..580c943 100644 --- a/input.cpp +++ b/input.cpp @@ -32,6 +32,7 @@ #define NUMPLAYERS 6 #define UINPUT_NAME "MiSTer virtual input" + char joy_bnames[NUMBUTTONS][32] = {}; int joy_bcount = 0; @@ -1167,6 +1168,9 @@ typedef struct int lightgun_req; int lightgun; + bool has_rumble; + uint16_t last_rumble; + ff_effect rumble_effect; int timeout; char mac[64]; @@ -1294,6 +1298,7 @@ static void INThandler(int code) #define test_bit(bit, array) (array [bit / 8] & (1 << (bit % 8))) + static char has_led(int fd) { unsigned char evtype_b[(EV_MAX + 7) / 8]; @@ -3953,12 +3958,31 @@ int input_test(int getchar) if (!strcmp(input[n].name, UINPUT_NAME)) { close(pool[n].fd); + pool[n].fd = -1; continue; } input[n].bind = -1; + int effects; + input[n].has_rumble = false; + if (cfg.rumble) + { + if (ioctl(fd, EVIOCGEFFECTS, &effects) >= 0) + { + unsigned char ff_features[(FF_MAX + 7) / 8] = {}; + + if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ff_features)), ff_features) != -1) + { + if (test_bit(FF_RUMBLE, ff_features)) { + input[n].rumble_effect.id = -1; + input[n].has_rumble = true; + } + } + } + } + // enable scroll wheel reading if (input[n].mouse) { @@ -4210,6 +4234,25 @@ int input_test(int getchar) while (1) { + if (cfg.rumble) + { + for (int pl = 1; pl < NUMPLAYERS; pl++) + { + if (!player_pad[pl].num || !player_pad[pl].has_rumble) continue; + uint16_t rumble_val = spi_uio_cmd(UIO_GET_RUMBLE0+(pl-1)); + if (player_pad[pl].last_rumble != rumble_val) + { + uint16_t strong_m, weak_m; + + strong_m = (rumble_val & 0xFF00) + (rumble_val >> 8); + weak_m = (rumble_val << 8) + (rumble_val & 0x00FF); + + rumble_player(pl, strong_m, weak_m, 0x7FFF); + player_pad[pl].last_rumble = rumble_val; + } + } + } + int return_value = poll(pool, NUMDEV + 3, timeout); if (!return_value) break; @@ -4234,10 +4277,13 @@ int input_test(int getchar) for (int pos = 0; pos < NUMDEV; pos++) { int i = pos; + + if ((pool[i].fd >= 0) && (pool[i].revents & POLLIN)) { if (!input[i].mouse) { + memset(&ev, 0, sizeof(ev)); if (read(pool[i].fd, &ev, sizeof(ev)) == sizeof(ev)) { @@ -5000,3 +5046,61 @@ void parse_buttons() joy_bcount++; } } + + +int rumble_input_device(int devnum, uint16_t strong_mag, uint16_t weak_mag, uint16_t duration, uint16_t delay) +{ + + int ioret = 0; + if (!input[devnum].has_rumble) return 0; + int fd = pool[devnum].fd; + if (!(fd >= 0)) return 0; + + if (!strong_mag && !weak_mag) //Stop rumble + { + if (input[devnum].rumble_effect.id== -1) return 1; //No uploaded effect + + ioret = ioctl(fd, EVIOCRMFF, input[devnum].rumble_effect.id); + input[devnum].rumble_effect.id = -1; //always set to -1 even if we fail to remove it? + return ioret != -1; + } else { + //Upload effect and then immediately play it + //If the effect id in the input struct is -1, it will be filled with the newly uploaded effect + //If it is filled with an already uploaded effect, the effect is modified in place + struct ff_effect *fef; + fef = &input[devnum].rumble_effect; + fef->type = FF_RUMBLE; + + fef->u.rumble.strong_magnitude = strong_mag; + fef->u.rumble.weak_magnitude = weak_mag; + fef->replay.length = duration; + fef->replay.delay = delay; + ioret = ioctl(fd, EVIOCSFF, fef); + if (ioret == -1) {printf("RUMBLE UPLOAD FAILED %s\n", strerror(errno)); return 0;} + //Play effect + struct input_event play_ev; + play_ev.type = EV_FF; + play_ev.code = input[devnum].rumble_effect.id; + play_ev.value = 1; + ioret = write(fd, (const void *)&play_ev, sizeof(play_ev)); + return ioret != -1; + } + return 0; +} + +int rumble_player(int pnum, uint16_t strong_mag, uint16_t weak_mag, uint16_t duration, uint16_t delay) +{ + + int dev_num = -1; + for (int i = 0; i < NUMDEV; i++) + { + if (input[i].num == pnum) + { + dev_num = i; + break; + } + } + + if (dev_num == -1) return 0; + return rumble_input_device(dev_num, strong_mag, weak_mag, duration, delay); +} diff --git a/input.h b/input.h index b063712..20acae6 100644 --- a/input.h +++ b/input.h @@ -113,4 +113,10 @@ void parse_buttons(); char *get_buttons(int type = 0); void set_ovr_buttons(char *s, int type); +int rumble_input_device(int devnum, uint16_t strong_mag, uint16_t weak_mag, uint16_t duration = 500, uint16_t delay = 0); +int rumble_player(int pnum, uint16_t strong_mag, uint16_t weak_mag, uint16_t duration = 500, uint16_t delay = 0); + + + + #endif diff --git a/spi.cpp b/spi.cpp index 5afcffb..943f446 100644 --- a/spi.cpp +++ b/spi.cpp @@ -111,7 +111,7 @@ uint16_t spi_uio_cmd_cont(uint8_t cmd) uint16_t spi_uio_cmd(uint8_t cmd) { - uint8_t res = spi_uio_cmd_cont(cmd); + uint16_t res = spi_uio_cmd_cont(cmd); DisableIO(); return res; } diff --git a/user_io.h b/user_io.h index bf4d640..2b2763b 100644 --- a/user_io.h +++ b/user_io.h @@ -70,6 +70,12 @@ #define UIO_CHK_UPLOAD 0x3C #define UIO_ASTICK_2 0x3D #define UIO_SHADOWMASK 0x3E +#define UIO_GET_RUMBLE0 0x3F +#define UIO_GET_RUMBLE1 0x40 +#define UIO_GET_RUMBLE2 0x41 +#define UIO_GET_RUMBLE3 0x42 +#define UIO_GET_RUMBLE4 0x43 +#define UIO_GET_RUMBLE5 0x44 // codes as used by 8bit for file loading from OSD #define FIO_FILE_TX 0x53