Linux console tools development discussion
 help / color / mirror / Atom feed
* [kbd] [PATCH v2] setleds: add option to reset state to the BIOS default
@ 2012-07-27  7:09 Petr Tesarik
  2012-07-29 21:10 ` Alexey Gladkov
  2012-08-02 20:36 ` Alexey Gladkov
  0 siblings, 2 replies; 4+ messages in thread
From: Petr Tesarik @ 2012-07-27  7:09 UTC (permalink / raw)
  To: kbd; +Cc: Alexey Gladkov

Hello folks,

please consider the following patch for kbd.

As you probably know, the Linux kernel resets the LED states to all off on 
boot. However, many users would like to keep the BIOS NumLock setting, and 
others get confused after they press one of the lock keys in the boot loader 
and it gets reset again when Linux initializes the terminal.

Because of that, many distros have used various ways to "improve" user 
experience: setting NumLock on by default, making OS-specific configuration 
options, or reading the BIOS area. The last option seems best to me, because 
it usually also preserves whatever you did in the boot loader. Unfortunately, 
there's no simple utility to read the state, so distros sometimes do insane 
things to get it (e.g. run hwinfo and grep only for the LED states and then 
run setleds with the appropriate arguments). Obviously, this approach isn't 
exactly fast...

Since setleds must be always used in the end, the most efficient solution is 
to add an option that resets the LED states to what they were before the Linux 
kernel booted.

Signed-off-by: Petr Tesarik <ptesarik@suse.cz>
---
 src/setleds.c |   61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 58 insertions(+), 3 deletions(-)

diff --git a/src/setleds.c b/src/setleds.c
index 3577aee..3defdea 100644
--- a/src/setleds.c
+++ b/src/setleds.c
@@ -11,18 +11,29 @@
 #include <fcntl.h>
 #include <linux/kd.h>
 #include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <unistd.h>
 #include "nls.h"
 #include "version.h"
 
+#if defined(__i386__) || defined(__x86_64__)
+# define HAVE_BIOS	1
+# define BIOS_KBD_ADDR	0x497
+# define BIOS_LED_SCR	0x01
+# define BIOS_LED_NUM	0x02
+# define BIOS_LED_CAP	0x04
+#endif
+
 static void attr_noreturn
 usage(void)
 {
     fprintf(stderr, _(
 "Usage:\n"
-"	setleds [-v] [-L] [-D] [-F] [[+|-][ num | caps | scroll %s]]\n"
+"	setleds [-v] [-L] [-D] [-F] [[+|-][ num | caps | scroll %s]%s]\n"
 "Thus,\n"
 "	setleds +caps -num\n"
 "will set CapsLock, clear NumLock and leave ScrollLock unchanged.\n"
+"%s"
 "The settings before and after the change (if any) are reported\n"
 "when the -v option is given or when no change is requested.\n"
 "Normally, setleds influences the vt flag settings\n"
@@ -32,9 +43,15 @@ usage(void)
 "that a subsequent reset will not change the flags.\n"
 ),
 #ifdef __sparc__
-    "| compose "
+    "| compose ",
+#else
+    "",
+#endif
+#ifdef HAVE_BIOS
+    " | bios ",
+"Specify \"bios\" to reset state to the BIOS default.\n"
 #else
-    ""
+    "", ""
 #endif
     );
     exit(1);
@@ -152,6 +169,37 @@ sunsetleds(arg_state char *cur_leds) {
 #endif
 }
 
+#ifdef HAVE_BIOS
+static void
+biosgetleds(char *cur_leds) {
+    int memfd;
+    long pagesz, mapoff;
+    char *map, bios_state;
+
+    memfd = open("/dev/mem", O_RDONLY);
+    if (memfd < 0) {
+	perror("/dev/mem");
+	fprintf(stderr, _("Error opening /dev/mem.\n"));
+	exit(1);
+    }
+    pagesz = sysconf(_SC_PAGESIZE);
+    mapoff = BIOS_KBD_ADDR & ~(pagesz-1);
+    map = mmap(NULL, pagesz, PROT_READ, MAP_SHARED, memfd, mapoff);
+    if (map == MAP_FAILED) {
+	perror("/dev/mem");
+	fprintf(stderr, _("Error mapping /dev/mem.\n"));
+	exit(1);
+    }
+    bios_state = map[BIOS_KBD_ADDR - mapoff];
+    *cur_leds =
+	(bios_state & BIOS_LED_SCR ? LED_SCR : 0) |
+	(bios_state & BIOS_LED_NUM ? LED_NUM : 0) |
+	(bios_state & BIOS_LED_CAP ? LED_CAP : 0);
+    munmap(map, pagesz);
+    close(memfd);
+}
+#endif	/* HAVE_BIOS */
+
 int
 main(int argc, char **argv) {
     int optL = 0, optD = 0, optF = 0, verbose = 0;
@@ -241,6 +289,13 @@ main(int argc, char **argv) {
 
     while(--argc) {
 	ap = *++argv;
+#ifdef HAVE_BIOS
+	if (!strcmp(ap, "bios")) {
+	  biosgetleds(&nval);
+	  ndef = LED_NUM | LED_CAP | LED_SCR;
+	  goto nxtarg;
+	}
+#endif
 	sign = 1;		/* by default: set */
 	if(*ap == '+')
 	  ap++;


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [kbd] [PATCH v2] setleds: add option to reset state to the BIOS default
  2012-07-27  7:09 [kbd] [PATCH v2] setleds: add option to reset state to the BIOS default Petr Tesarik
@ 2012-07-29 21:10 ` Alexey Gladkov
  2012-07-30 17:27   ` Petr Tesarik
  2012-08-02 20:36 ` Alexey Gladkov
  1 sibling, 1 reply; 4+ messages in thread
From: Alexey Gladkov @ 2012-07-29 21:10 UTC (permalink / raw)
  Cc: kbd

27.07.2012 11:09, Petr Tesarik wrote:

> +#if defined(__i386__) || defined(__x86_64__)
> +# define HAVE_BIOS	1

I think it's strange condition.
How your code will work on the EFI ?

-- 
Rgrds, legion



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [kbd] [PATCH v2] setleds: add option to reset state to the BIOS default
  2012-07-29 21:10 ` Alexey Gladkov
@ 2012-07-30 17:27   ` Petr Tesarik
  0 siblings, 0 replies; 4+ messages in thread
From: Petr Tesarik @ 2012-07-30 17:27 UTC (permalink / raw)
  To: Linux console tools development discussion

Hi Alexey,

glad you didn't smash the idea from an ideological standpoint.

Dne Ne 29. července 2012 23:10:19 Alexey Gladkov napsal(a):
> 27.07.2012 11:09, Petr Tesarik wrote:
> > +#if defined(__i386__) || defined(__x86_64__)
> > +# define HAVE_BIOS	1
> 
> I think it's strange condition.

It is, indeed. For one thing, some Itanium machines also have the standard PC 
BIOS Data Area (BDA), and they don't get this new feature. Unfortunately, some 
(early?) SGI Altix machines don't have physical memory at 0x400, and they 
don't handle accesses to non-existent RAM very well (read: freeze or reboot). 
OTOH detecting the machine model and maintaining a blacklist or whitelist of 
well-known IA-64 systems in setleds sounds like overkill, especially for a 
dying platform. So, I didn't include them.

> How your code will work on the EFI ?

Well, UEFI will work just fine, same with Intel's EFI, because they include a 
Compatibility Support Module (CSM), which (among other things) initializes 
legacy BDA. Yes, some machines don't do that properly (IIRC Phoenix issued an 
errata a few years ago exactly to fix LED states in the BDA).

Even Apple includes a CSM for their EFI implementation...

I would be a bit more concerned about embedded hardware like the one I used to 
have in one of my ancient D-Link routers in the early 2000's, which emulated 
an x86 on top of ICplus Corp. IC175. However, platforms like this usually run 
in a well-controlled environment, so the firmware writer will probably not 
even attemp to use this new setleds feature.

In any case, even if the BDA is not initialized properly, the worst thing that 
happens is that the LEDs are set incorrectly when you run "setleds bios".

Petr Tesarik
SUSE Linux


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [kbd] [PATCH v2] setleds: add option to reset state to the BIOS default
  2012-07-27  7:09 [kbd] [PATCH v2] setleds: add option to reset state to the BIOS default Petr Tesarik
  2012-07-29 21:10 ` Alexey Gladkov
@ 2012-08-02 20:36 ` Alexey Gladkov
  1 sibling, 0 replies; 4+ messages in thread
From: Alexey Gladkov @ 2012-08-02 20:36 UTC (permalink / raw)
  To: Petr Tesarik; +Cc: Jiri Kosina, Kirill A. Shutemov, Joshua Cov, kbd

On 27.07.2012 11:09, Petr Tesarik wrote:
> Hello folks,
> 
> please consider the following patch for kbd.
> 
> As you probably know, the Linux kernel resets the LED states to all off on 
> boot. However, many users would like to keep the BIOS NumLock setting, and 
> others get confused after they press one of the lock keys in the boot loader 
> and it gets reset again when Linux initializes the terminal.
> 
> Because of that, many distros have used various ways to "improve" user 
> experience: setting NumLock on by default, making OS-specific configuration 
> options, or reading the BIOS area. The last option seems best to me, because 
> it usually also preserves whatever you did in the boot loader. Unfortunately, 
> there's no simple utility to read the state, so distros sometimes do insane 
> things to get it (e.g. run hwinfo and grep only for the LED states and then 
> run setleds with the appropriate arguments). Obviously, this approach isn't 
> exactly fast...
> 
> Since setleds must be always used in the end, the most efficient solution is 
> to add an option that resets the LED states to what they were before the Linux 
> kernel booted.
> 
> Signed-off-by: Petr Tesarik <ptesarik@suse.cz>

It looks like what you propose has already been discussed several years ago:

http://article.gmane.org/gmane.linux.kernel/493362

I also found that some code is already in the kernel:

http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=b2d0b7a061bfddd27155c7dcd53f365d9dc0c7c3

As far as I could learn, it is possible to query the LEDs on the USB keyboard,
but for PS/2 this is not possible.

> ---
>  src/setleds.c |   61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
>  1 files changed, 58 insertions(+), 3 deletions(-)
> 
> diff --git a/src/setleds.c b/src/setleds.c
> index 3577aee..3defdea 100644
> --- a/src/setleds.c
> +++ b/src/setleds.c
> @@ -11,18 +11,29 @@
>  #include <fcntl.h>
>  #include <linux/kd.h>
>  #include <sys/ioctl.h>
> +#include <sys/mman.h>
> +#include <unistd.h>
>  #include "nls.h"
>  #include "version.h"
>  
> +#if defined(__i386__) || defined(__x86_64__)
> +# define HAVE_BIOS	1
> +# define BIOS_KBD_ADDR	0x497
> +# define BIOS_LED_SCR	0x01
> +# define BIOS_LED_NUM	0x02
> +# define BIOS_LED_CAP	0x04
> +#endif
> +
>  static void attr_noreturn
>  usage(void)
>  {
>      fprintf(stderr, _(
>  "Usage:\n"
> -"	setleds [-v] [-L] [-D] [-F] [[+|-][ num | caps | scroll %s]]\n"
> +"	setleds [-v] [-L] [-D] [-F] [[+|-][ num | caps | scroll %s]%s]\n"
>  "Thus,\n"
>  "	setleds +caps -num\n"
>  "will set CapsLock, clear NumLock and leave ScrollLock unchanged.\n"
> +"%s"
>  "The settings before and after the change (if any) are reported\n"
>  "when the -v option is given or when no change is requested.\n"
>  "Normally, setleds influences the vt flag settings\n"
> @@ -32,9 +43,15 @@ usage(void)
>  "that a subsequent reset will not change the flags.\n"
>  ),
>  #ifdef __sparc__
> -    "| compose "
> +    "| compose ",
> +#else
> +    "",
> +#endif
> +#ifdef HAVE_BIOS
> +    " | bios ",
> +"Specify \"bios\" to reset state to the BIOS default.\n"
>  #else
> -    ""
> +    "", ""
>  #endif
>      );
>      exit(1);
> @@ -152,6 +169,37 @@ sunsetleds(arg_state char *cur_leds) {
>  #endif
>  }
>  
> +#ifdef HAVE_BIOS
> +static void
> +biosgetleds(char *cur_leds) {
> +    int memfd;
> +    long pagesz, mapoff;
> +    char *map, bios_state;
> +
> +    memfd = open("/dev/mem", O_RDONLY);
> +    if (memfd < 0) {
> +	perror("/dev/mem");
> +	fprintf(stderr, _("Error opening /dev/mem.\n"));
> +	exit(1);
> +    }
> +    pagesz = sysconf(_SC_PAGESIZE);
> +    mapoff = BIOS_KBD_ADDR & ~(pagesz-1);
> +    map = mmap(NULL, pagesz, PROT_READ, MAP_SHARED, memfd, mapoff);
> +    if (map == MAP_FAILED) {
> +	perror("/dev/mem");
> +	fprintf(stderr, _("Error mapping /dev/mem.\n"));
> +	exit(1);
> +    }
> +    bios_state = map[BIOS_KBD_ADDR - mapoff];
> +    *cur_leds =
> +	(bios_state & BIOS_LED_SCR ? LED_SCR : 0) |
> +	(bios_state & BIOS_LED_NUM ? LED_NUM : 0) |
> +	(bios_state & BIOS_LED_CAP ? LED_CAP : 0);
> +    munmap(map, pagesz);
> +    close(memfd);
> +}
> +#endif	/* HAVE_BIOS */
> +
>  int
>  main(int argc, char **argv) {
>      int optL = 0, optD = 0, optF = 0, verbose = 0;
> @@ -241,6 +289,13 @@ main(int argc, char **argv) {
>  
>      while(--argc) {
>  	ap = *++argv;
> +#ifdef HAVE_BIOS
> +	if (!strcmp(ap, "bios")) {
> +	  biosgetleds(&nval);
> +	  ndef = LED_NUM | LED_CAP | LED_SCR;
> +	  goto nxtarg;
> +	}
> +#endif
>  	sign = 1;		/* by default: set */
>  	if(*ap == '+')
>  	  ap++;
> 


-- 
Rgrds, legion



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2012-08-02 20:36 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-07-27  7:09 [kbd] [PATCH v2] setleds: add option to reset state to the BIOS default Petr Tesarik
2012-07-29 21:10 ` Alexey Gladkov
2012-07-30 17:27   ` Petr Tesarik
2012-08-02 20:36 ` Alexey Gladkov

Linux console tools development discussion

This inbox may be cloned and mirrored by anyone:

	git clone --mirror http://lore.altlinux.org/kbd/0 kbd/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 kbd kbd/ http://lore.altlinux.org/kbd \
		kbd@lists.altlinux.org kbd@lists.altlinux.ru kbd@lists.altlinux.com
	public-inbox-index kbd

Example config snippet for mirrors.
Newsgroup available over NNTP:
	nntp://lore.altlinux.org/org.altlinux.lists.kbd


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git