diff options
author | B. Watson <urchlay@slackware.uk> | 2025-05-06 07:18:21 -0400 |
---|---|---|
committer | B. Watson <urchlay@slackware.uk> | 2025-05-06 07:18:21 -0400 |
commit | df054a1821264dfd339afac61656c6a2261fafbf (patch) | |
tree | c60b6b356e480d055fdd067c794d6cbd66d63ab2 | |
parent | 67ebbac0be7ad917e794ca5ea0496d0a1ead83b8 (diff) | |
download | marsond-df054a1821264dfd339afac61656c6a2261fafbf.tar.gz |
much work (run as a daemon, drop priviliges, set priority, etc).
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | marsond.1 | 116 | ||||
-rw-r--r-- | marsond.c | 142 | ||||
-rw-r--r-- | marsond.rst | 84 | ||||
-rw-r--r-- | rc.marsond | 4 | ||||
-rw-r--r-- | usage.c | 2 |
6 files changed, 292 insertions, 58 deletions
@@ -33,7 +33,7 @@ all: marsond marsond: marsond.c usage.c -usage.c: mkusage.pl marsond.rst +usage.c: mkusage.pl marsond.1 perl mkusage.pl marsond.rst > usage.c marsond.1: marsond.rst @@ -29,42 +29,109 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .. .TH "MARSOND" 1 "2025-05-06" "0.1" "SlackBuilds.org" .SH NAME -marsond \- Fix Enter key on Marson/USBLinux/MT606-1 adaptors +marsond \- Fix Enter key timing on Marson/USBLinux/MT606-1 PS/2-USB adaptors .SH SYNOPSIS .sp -marsond [\fB\-d\fP \fIdelay\-ms*\fP] [\fB\-k\fP \fIkeyboard\-device\fP] [\fB\-v\fP] | [\fB\-\-help\fP] | [\fB\-\-version\fP] | [\fB\-V\fP] +marsond [\fB\-d\fP \fIdelay\-ms*\fP] [\fB\-f\fP] [\fB\-k\fP \fIkeyboard\-device\fP] [\fB\-v\fP] | [\fB\-\-help\fP] | [\fB\-\-version\fP] | [\fB\-V\fP] .SH DESCRIPTION +.sp +\fBmarsond\fP fixes an issue with a particular model of PS/2 => USB +keyboard adaptor. The symptom: in games and emulators, the Enter key +only works sometimes. This is caused by the firmware in the adaptor +sending the keypress and release events only 8 milliseconds apart. +.sp +The problem happens with any SDL application (SDL1 or SDL2), as well +as other graphics libraries. It happens regardless of what PS/2 +keyboard you have plugged in, what computer you have the adaptor +plugged into, or what OS that computer is running. +.sp +The specific hardware that has the Enter key problem for me is USB +vendor ID 04b4, product ID 0101, "Marson Keyboard and Mouse Link +Ver:ps2120L". It has a label on the front that says "USBLink", and on +the back, the model number is "MT606\-1". It looks like: +.INDENT 0.0 +.INDENT 3.5 +\fI\%https://slackware.uk/~urchlay/sdl\-usblink\-hack/marson\-front.jpg\fP +.sp +\fI\%https://slackware.uk/~urchlay/sdl\-usblink\-hack/marson\-back.jpg\fP +.UNINDENT +.UNINDENT +.sp +I\(aqm not sure if any other USB keyboard adaptors are affected. If you +have the problem on some other adaptor, and \fBmarsond\fP fixes it, +please contact me so I can add the model number to this man page. +.sp +\fBmarsond\fP uses the Linux kernel\(aqs \fBuinput\fP layer to "grab" the +keyboard, create a new virtual keyboard device, and pass events from +the real keyboard to the virtual one. When it see the key release +event for the Enter key, it simply pauses for a few milliseconds +before delivering it. +.sp +This should work with everything that uses the keyboard. It definitely +does, with SDL 1 and 2 running under X (and SDL 1 on the console, +too). I don\(aqt use Wayland, so I haven\(aqt tested it there. If you do, +please contact me and let me know if it works for you. .SH OPTIONS +.sp +Options can be "bundled": \fB\-vf\fP is the same as \fB\-v\fP \fB\-f\fP\&. .INDENT 0.0 .TP .BI \-d \ delay\-ms Amount of time in milliseconds to delay the Enter key release events. Default: 30. +.UNINDENT +.\" delay time for Enter key relase (default 30). +. +.INDENT 0.0 +.TP +.B \-f +Run in the foreground; do not detach from the terminal or become a +daemon. In this mode, \fBmarsond\fP can be killed with \fI^C\fP\&. +.UNINDENT +.\" run in foreground, not as a daemon. +. +.INDENT 0.0 .TP .BI \-k \ keyboard\-device Input device for the keyboard adaptor. Default: /dev/input/by\-id/usb\-Marson_Marson_Keyboard_and_Mouse_Link_Ver:ps2120L\-event\-kbd +.UNINDENT +.\" keyboard device (usually under /dev/input/by-id/). +. +.INDENT 0.0 .TP .B \-v -Verbose debugging mode. Prints copious trace information to \fBstderr\fP, and -does not run in the background. +Verbose debugging mode. Prints copious trace information to \fBstderr\fP\&. +Debugging mode is turned off when \fBmarsond\fP forks itself into the +background as a daemon. If you want to debug the event loop, combine +\fB\-v\fP and \fB\-f\fP\&. +.UNINDENT +.\" verbose debugging, foreground operation. +. +.INDENT 0.0 .TP -.B \-H\fP,\fB \-\-help +.B \-h\fP,\fB \-\-help Shows built\-in usage message and exits. +.UNINDENT +.\" this help text. +. +.INDENT 0.0 .TP .B \-V\fP,\fB \-\-version Shows version number and exits. .UNINDENT -.\" other sections we might want, uncomment as needed. -. -.\" FILES -. -.\" ===== -. -.\" ENVIRONMENT -. -.\" =========== +.\" show version number. . +.SH ENVIRONMENT +.INDENT 0.0 +.TP +.B MARSOND_USER +After initialization, \fBmarsond\fP will drop privileges by +setting its user ID to this user\(aqs. Default: \fInobody\fP +.TP +.B MARSOND_GROUP +Group to run as, after dropping privileges. Default: \fInogroup\fP +.UNINDENT .\" EXIT STATUS . .\" =========== @@ -79,15 +146,22 @@ Shows version number and exits. . .SH COPYRIGHT .sp -See the file /usr/doc/PRGNAM\-0.1/COPYING for license information. +WTFPL. Short version: do WTF you want. Full version: +.INDENT 0.0 +.INDENT 3.5 +\fI\%http://www.wtfpl.net/txt/copying/\fP +.UNINDENT +.UNINDENT .SH AUTHORS -.sp -PRGNAM was written by WHOEVER. -.sp -This man page written for the SlackBuilds.org project -by B. Watson, and is licensed under the WTFPL. +.INDENT 0.0 +.IP B. 3 +Watson <\fI\%urchlay@slackware.uk\fP> +.UNINDENT .SH SEE ALSO .sp -The PRGNAM homepage: \fI\%http://www.PRGNAM.org/\fP +The homepage: \fI\%https://slackware.uk/~urchlay/repos/marsond\fP +.sp +Another solution to the problem, less complete, but should be portable +to non\-Linux OSes: \fI\%https://slackware.uk/~urchlay/sdl\-usblink\-hack/\fP .\" Generated by docutils manpage writer. . @@ -7,6 +7,12 @@ #include <fcntl.h> #include <errno.h> #include <stdarg.h> +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> + +#include <sys/resource.h> +#include <sys/mman.h> #include <linux/input.h> #include <linux/uinput.h> @@ -17,9 +23,13 @@ #define DEFAULT_KBD "/dev/input/by-id/usb-Marson_Marson_Keyboard_and_Mouse_Link_Ver:ps2120L-event-kbd" +#define DEFAULT_USER "nobody" +#define DEFAULT_GROUP "nobody" + const char *self; int debugging = 0; /* -v */ +int foreground = 0; /* -v */ const char *keyboard_dev = DEFAULT_KBD; /* -k */ int delay_ms = DEFAULT_DELAY_MS; /* -d */ @@ -78,12 +88,13 @@ void parse_args(int argc, char **argv) { } } - while( (opt = getopt(argc, argv, "d:hk:vV")) != -1) { + while( (opt = getopt(argc, argv, "d:hfk:vV")) != -1) { switch(opt) { - case 'v': debugging++; break; case 'd': delay_ms = atoi(optarg); break; - case 'k': keyboard_dev = optarg; break; + case 'f': foreground++; break; case 'h': print_help(); exit(0); break; + case 'k': keyboard_dev = optarg; break; + case 'v': debugging++; break; case 'V': version(); exit(0); break; case '?': die("invalid option (try --help)"); break; default: print_help(); exit(1); break; @@ -93,6 +104,64 @@ void parse_args(int argc, char **argv) { if(delay_ms < 1) die("invalid -d argument"); } +/* since every keystroke passes through this daemon, we *don't* + want it to be swapped out. it would suck if you couldn't type + because you were compiling something huge... note that we have to + do this after all the forking, see mlockall(2) for details. */ +void lock_memory(void) { + if(mlockall(MCL_CURRENT) >= 0) { + debug("mlockall() succeeded"); + } else { + warn("can't lock memory: %s", strerror(errno)); + } +} + +/* TODO: look into using *actual* realtime scheduling, as + described in sched(7). */ +void set_realtime(void) { + if(setpriority(PRIO_PROCESS, 0, -20) >= 0) { + debug("setpriority() succeeded"); + } else { + warn("can't setpriority(): %s", strerror(errno)); + } +} + +void drop_privs(void) { + const char *user, *group; + struct passwd *pw; + struct group *gr; + gid_t gid; + + user = getenv("MARSON_USER"); + + if(!user) user = DEFAULT_USER; + + if( !(pw = getpwnam(user)) ) { + die("no such user '%s'", user); + } + + if( (group = getenv("MARSON_GROUP")) ) { + if( !(gr = getgrnam(user)) ) { + die("no such group '%s'", group); + } + gid = gr->gr_gid; + } else { + gid = pw->pw_gid; + } + + debug("about to drop privileges to user/group %d/%d", pw->pw_uid, gid); + + if(setgid(gid) < 0) { + die("can't change group ID to %d: %s", gid, strerror(errno)); + } + + if(setuid(pw->pw_uid) < 0) { + die("can't change user ID to %d: %s", pw->pw_uid, strerror(errno)); + } + + debug("now running as user/group %d/%d", getuid(), getgid()); +} + void daemonize(void) { int pid; @@ -117,10 +186,28 @@ void daemonize(void) { } /* we are the grandchild here */ + + /* nothing left to say, close stdin/out/err and cd to / so at + shutdown time, umount won't trip over our cwd. */ chdir("/"); close(0); close(1); close(2); + + /* nowhere for debug messages to go, so turn it off */ + debugging = 0; + + lock_memory(); + set_realtime(); + drop_privs(); +} + +void set_evbit(int outfd, int event) { + if(ioctl(outfd, UI_SET_EVBIT, event) >= 0) { + debug("UI_SET_EVBIT %d OK"); + } else { + die("UI_SET_EVBIT %d failed: %s", event, strerror(errno)); + } } int main(int argc, char **argv) { @@ -151,12 +238,12 @@ int main(int argc, char **argv) { die("failed to grab %s: %s", keyboard_dev, strerror(errno)); } - if(ioctl(outfd, UI_SET_EVBIT, EV_KEY) >= 0) { - debug("UI_SET_EVBIT OK"); - } else { - die("UI_SET_EVBIT failed: %s", strerror(errno)); - } + set_evbit(outfd, EV_KEY); /* get and send keystroke events... */ + set_evbit(outfd, EV_SYN); /* ...sync events... */ + set_evbit(outfd, EV_MSC); /* ...and miscellaneous. */ + /* the other event types will never be sent by a keyboard. */ + /* we want all possible keystrokes */ for(i = 0; i < KEY_MAX; i++) { if(ioctl(outfd, UI_SET_KEYBIT, i) >= 0) { /* we don't wanna be *that* verbose */ @@ -185,28 +272,47 @@ int main(int argc, char **argv) { die("UI_DEV_CREATE failed: %s", strerror(errno)); } - if(!debugging) daemonize(); - /* note: don't call die() unless debugging is true, since daemonize() - closes our stderr (there's nowhere for the error message to print). */ + /* this must be done while still running as root... */ + if(foreground) { + lock_memory(); + set_realtime(); + drop_privs(); + } else { + daemonize(); + } + /* note: don't call debug() or die() unless foreground is true, since + daemonize() closes our stderr (there's nowhere for the messages + to print). */ while(1) { if(read(infd, &ev, sizeof(ev)) < 0) { - if(debugging) + if(foreground) { die("read from %s failed: %s", keyboard_dev, strerror(errno)); + } else { + exit(1); + } } - debug("event: type %d, code %d, value %d\n", - ev.type, ev.code, ev.value); + if(foreground) { + debug("got event: type %d, code %d, value %d\n", + ev.type, ev.code, ev.value); + } + /* value == 0 means key release (a 1 would be a press) */ if(ev.type == EV_KEY && ev.code == KEY_ENTER && ev.value == 0) { - debug("got Enter key release, delaying %d ms", delay_ms); + if(foreground) { + debug("!!! got Enter key release, delaying %d ms", delay_ms); + } usleep(delay_ms * 1000); } if(write(outfd, &ev, sizeof(ev)) < 0) { - if(debugging) + if(foreground) { die("write to /dev/uinput failed: %s", strerror(errno)); + } else { + exit(1); + } } } @@ -231,4 +337,8 @@ event: type 0, code 0, value 0 event: type 4, code 4, value 458792 event: type 1, code 28, value 0 event: type 0, code 0, value 0 + +Onlt the type 1 messages concern us, but we have to pass +them all through. code 28 is KEY_ENTER, value is 1 for press, +0 for release. */ diff --git a/marsond.rst b/marsond.rst index 4dfee82..2df75bb 100644 --- a/marsond.rst +++ b/marsond.rst @@ -6,9 +6,9 @@ marsond ======= -------------------------------------------------- -Fix Enter key on Marson/USBLinux/MT606-1 adaptors -------------------------------------------------- +----------------------------------------------------------------- +Fix Enter key timing on Marson/USBLinux/MT606-1 PS/2-USB adaptors +----------------------------------------------------------------- :Manual section: 1 :Manual group: SlackBuilds.org @@ -18,19 +18,61 @@ Fix Enter key on Marson/USBLinux/MT606-1 adaptors SYNOPSIS ======== -marsond [**-d** *delay-ms**] [**-k** *keyboard-device*] [**-v**] | [**--help**] | [**--version**] | [**-V**] +marsond [**-d** *delay-ms**] [**-f**] [**-k** *keyboard-device*] [**-v**] | [**--help**] | [**--version**] | [**-V**] DESCRIPTION =========== +**marsond** fixes an issue with a particular model of PS/2 => USB +keyboard adaptor. The symptom: in games and emulators, the Enter key +only works sometimes. This is caused by the firmware in the adaptor +sending the keypress and release events only 8 milliseconds apart. + +The problem happens with any SDL application (SDL1 or SDL2), as well +as other graphics libraries. It happens regardless of what PS/2 +keyboard you have plugged in, what computer you have the adaptor +plugged into, or what OS that computer is running. + +The specific hardware that has the Enter key problem for me is USB +vendor ID 04b4, product ID 0101, "Marson Keyboard and Mouse Link +Ver:ps2120L". It has a label on the front that says "USBLink", and on +the back, the model number is "MT606-1". It looks like: + + https://slackware.uk/~urchlay/sdl-usblink-hack/marson-front.jpg + + https://slackware.uk/~urchlay/sdl-usblink-hack/marson-back.jpg + +I'm not sure if any other USB keyboard adaptors are affected. If you +have the problem on some other adaptor, and **marsond** fixes it, +please contact me so I can add the model number to this man page. + +**marsond** uses the Linux kernel's **uinput** layer to "grab" the +keyboard, create a new virtual keyboard device, and pass events from +the real keyboard to the virtual one. When it see the key release +event for the Enter key, it simply pauses for a few milliseconds +before delivering it. + +This should work with everything that uses the keyboard. It definitely +does, with SDL 1 and 2 running under X (and SDL 1 on the console, +too). I don't use Wayland, so I haven't tested it there. If you do, +please contact me and let me know if it works for you. + OPTIONS ======= +Options can be "bundled": **-vf** is the same as **-v** **-f**. + -d delay-ms Amount of time in milliseconds to delay the Enter key release events. Default: 30. - .. delay time for Enter key relase (default 30). +.. delay time for Enter key relase (default 30). + +-f + Run in the foreground; do not detach from the terminal or become a + daemon. In this mode, **marsond** can be killed with *^C*. + +.. run in foreground, not as a daemon. -k keyboard-device Input device for the keyboard adaptor. Default: @@ -39,8 +81,10 @@ OPTIONS .. keyboard device (usually under /dev/input/by-id/). -v - Verbose debugging mode. Prints copious trace information to **stderr**, and - does not run in the background. + Verbose debugging mode. Prints copious trace information to **stderr**. + Debugging mode is turned off when **marsond** forks itself into the + background as a daemon. If you want to debug the event loop, combine + **-v** and **-f**. .. verbose debugging, foreground operation. @@ -54,13 +98,15 @@ OPTIONS .. show version number. -.. other sections we might want, uncomment as needed. +ENVIRONMENT +=========== -.. FILES -.. ===== +MARSOND_USER + After initialization, **marsond** will drop privileges by + setting its user ID to this user's. Default: *nobody* -.. ENVIRONMENT -.. =========== +MARSOND_GROUP + Group to run as, after dropping privileges. Default: *nogroup* .. EXIT STATUS .. =========== @@ -74,17 +120,19 @@ OPTIONS COPYRIGHT ========= -See the file /usr/doc/PRGNAM-|version|/COPYING for license information. +WTFPL. Short version: do WTF you want. Full version: + + http://www.wtfpl.net/txt/copying/ AUTHORS ======= -PRGNAM was written by WHOEVER. - -This man page written for the SlackBuilds.org project -by B. Watson, and is licensed under the WTFPL. +B. Watson <urchlay@slackware.uk> SEE ALSO ======== -The PRGNAM homepage: http://www.PRGNAM.org/ +The homepage: https://slackware.uk/~urchlay/repos/marsond + +Another solution to the problem, less complete, but should be portable +to non-Linux OSes: https://slackware.uk/~urchlay/sdl-usblink-hack/ @@ -22,8 +22,8 @@ # initialization. You can use a different user and group by # uncommenting and modifying these: -#MARSON_USER=nobody -#MARSON_GROUP=nogroup +#export MARSON_USER=nobody +#export MARSON_GROUP=nogroup PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin export PATH @@ -1,4 +1,6 @@ const char *helptext[] = { + " -d delay-ms: delay time for Enter key relase (default 30).", + " -f: run in foreground, not as a daemon.", " -k keyboard-device: keyboard device (usually under /dev/input/by-id/).", " -v: verbose debugging, foreground operation.", " -h, --help: this help text.", |