NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

port-mac68k/60692: mac68k: adb_direct polls round-robin, halving the mouse sample rate



>Number:         60692
>Category:       port-mac68k
>Synopsis:       mac68k: adb_direct polls round-robin, halving the mouse sample rate
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    port-mac68k-maintainer
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Sep 08 10:10:00 +0000 2026
>Originator:     Ray Tran
>Release:        11.0
>Organization:
Diamond Creek Digital
>Environment:
adb_guess_next_device() is called after every transaction, so the
	  poll rate is divided evenly between all attached ADB devices
>Description:
adb_direct.c steps to the next device in the device table after every
completed transaction, whatever happened.  With a keyboard and a mouse
attached -- the ordinary case -- the mouse therefore receives every
second poll, and its sample rate is half what the bus could deliver.

That is not how the ADB host is supposed to schedule polls.  Apple's
Technical Note HW01 ("ADB - The Untold Story: Space Aliens Ate My
Mouse") describes the intended behaviour:

  "When an SRQ is not asserted, the host will continually poll the last
   device to send it data, sending it talk register 0 commands
   periodically.  This is done under the assumption that this is likely
   to be the next place the user interacts; if the user types a
   character, they are very likely to type another soon.  On current
   hosts, this can happen up to 150 times a second."

and, for the case where another device wants attention:

  "It will then begin polling those addresses which it knows hold
   devices.  If a device does not have any data, the host will move on
   to the next address, asking each device in turn, until SRQ is no
   longer asserted, indicating that all pending data has been fetched."

Walking the device list is what SRQ is for, and adb_direct.c already
detects SRQ and does exactly that.  The unconditional call in the normal
completion path duplicates it, which makes the SRQ branch redundant and
spreads the poll rate across every device instead of concentrating it on
the one the user is actually using.

The devices cooperate with this scheme by design; the same note says a
device "should only respond with data when sent a talk register 0
command if it has new data ... it should not respond at all, allowing
the bus to time out", so polling an idle device repeatedly costs a bus
timeout and nothing else.
>How-To-Repeat:
On a machine with both an ADB keyboard and an ADB mouse, measure the
interval between mouse position updates while moving the pointer
steadily.  Measured on a Macintosh Centris 650 with a
histogram in the display driver, over 1714 samples (NetBSD 10.1 at the
time; the driver is unchanged in 11.0 and -current): the gaps form two
clusters, 2-6 ms (44%) and 20-34 ms (29%), with almost nothing near the
15 ms frame period.  That is a per-device sample rate around 40 Hz on a
bus the same note says can poll at up to 150 Hz, against a 66.7 Hz
display -- the pointer is under-sampled, and moving it at any speed
produces visible judder in large steps.
>Fix:
Keep polling the device that last had data, as the note describes, and
leave the walking of the device list to the SRQ path that already exists.

A bound is added that the documented design does not need: after
ADB_MAX_SAME_POLLS consecutive polls of one device the driver moves on
regardless.  Apple's scheme relies on SRQ being noticed every time, and
that puts keyboard responsiveness entirely on this driver's SRQ
detection; the limit means a missed SRQ costs a device a bounded delay
rather than starving it.  At the rates involved that is well under a
tenth of a second, and it does not affect the common case at all.

Tested on a Macintosh Centris 650 running NetBSD 11.0 with an ADB
keyboard and a 100 dpi ADB mouse; applies to -current (11.99.8) with no
fuzz, and that kernel drives adb0/akbd0/ams0 under qemu -M q800.

Applies with "patch -p1" from the top of usr/src; verified with -F0
(no fuzz) against NetBSD-current 11.99.8 (20260830003849Z) and 11.0.

--- a/sys/arch/mac68k/dev/adb_direct.c
+++ b/sys/arch/mac68k/dev/adb_direct.c
@@ -245,6 +245,22 @@
 int	adbSendTalk = 0;	/* the intr routine is sending the talk, not
 				 * the user (II) */
 int	adbPolling = 0;		/* we are polling for service request */
+
+static int adbSamePolls = 0;
+
+#define ADB_MAX_SAME_POLLS	20
 int	adbPollCmd = 0;		/* the last poll command we sent */
 
 u_char	adbInputBuffer[ADB_MAX_MSG_LENGTH];	/* data input buffer */
@@ -844,6 +860,7 @@
 					printf_intr(" xSRQ! ");
 #endif
 				adb_guess_next_device();
+				adbSamePolls = 0;	/* walking the list */
 #ifdef ADB_DEBUG
 				if (adb_debug & 0x80)
 					printf_intr("try 0x%0x ",
@@ -905,7 +922,36 @@
 				if (adb_debug & 0x80)
 					printf_intr("XXending ");
 #endif
-				adb_guess_next_device();
+				/*
+				 * Keep polling the device that last had
+				 * something to say, rather than stepping
+				 * to the next one every single time.				 
+				 */
+				if (++adbSamePolls >= ADB_MAX_SAME_POLLS) {
+					adb_guess_next_device();
+					adbSamePolls = 0;
+				}
 				adbOutputBuffer[0] = 1;
 				adbOutputBuffer[1] = ((adbLastDevice & 0x0f) << 4) | 0x0c;
 				adbSentChars = 0;	/* nothing sent yet */




Home | Main Index | Thread Index | Old Index