aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Watson <urchlay@slackware.uk>2025-05-06 07:18:21 -0400
committerB. Watson <urchlay@slackware.uk>2025-05-06 07:18:21 -0400
commitdf054a1821264dfd339afac61656c6a2261fafbf (patch)
treec60b6b356e480d055fdd067c794d6cbd66d63ab2
parent67ebbac0be7ad917e794ca5ea0496d0a1ead83b8 (diff)
downloadmarsond-df054a1821264dfd339afac61656c6a2261fafbf.tar.gz
much work (run as a daemon, drop priviliges, set priority, etc).
-rw-r--r--Makefile2
-rw-r--r--marsond.1116
-rw-r--r--marsond.c142
-rw-r--r--marsond.rst84
-rw-r--r--rc.marsond4
-rw-r--r--usage.c2
6 files changed, 292 insertions, 58 deletions
diff --git a/Makefile b/Makefile
index 4ef8ee2..f1432ba 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/marsond.1 b/marsond.1
index 6bb016b..667e38f 100644
--- a/marsond.1
+++ b/marsond.1
@@ -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.
.
diff --git a/marsond.c b/marsond.c
index 885a3e2..f872baf 100644
--- a/marsond.c
+++ b/marsond.c
@@ -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/
diff --git a/rc.marsond b/rc.marsond
index 3de1c23..dfacf40 100644
--- a/rc.marsond
+++ b/rc.marsond
@@ -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
diff --git a/usage.c b/usage.c
index ac836e8..e597aa1 100644
--- a/usage.c
+++ b/usage.c
@@ -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.",