Envie um sinal específico para um teclado USB externo? Ou, como corrijo o fdopen ()?

6

Algumas informações básicas: Estou tentando fazer com que a luz de maiúsculas e minúsculas em um teclado externo acenda quando eu executar um comando na linha de comando. Idealmente seria roteirizado, para que eu possa usá-lo para alertas e coisas (eu desmontei o teclado).

Eu recebi este comando que parece que deve funcionar:

fd=fdopen("/dev/console"); ioctl(fd, 0x4B32, 0x04);

Quando tento executar este comando no OS X ou em um servidor Ubuntu que tenho, isso acontece:

me@server1:~$ fd=fdopen("/dev/console"); ioctl(fd, 0x4B32, 0x04);
-bash: syntax error near unexpected token '('
me@server1:~$ fd=fdopen(/dev/console);
-bash: syntax error near unexpected token '('

Então parece que o problema está na primeira parte. Preciso instalar um conjunto de software / utilitários? Como posso controlar manualmente a luz de maiúsculas no meu teclado externo?

    
por Undo 18.04.2014 / 16:12

3 respostas

6

Seu problema é que você tenta inserir o código C em um prompt do shell, isso não funciona por razões óbvias. Você poderia colocá-lo em um arquivo C correto, compilá-lo e obter um binário válido que você poderia executar:

#include <linux/kd.h>

#include <sys/ioctl.h>

#include <fcntl.h>
#include <unistd.h>

#include <err.h>
#include <stdio.h>
#include <stdlib.h>

static void
usage(char *argv0)
{
    fprintf(stderr, "Usage: %s <on|off>\n", argv0);
    exit(EXIT_FAILURE);
}

int
main(int argc, char *argv[])
{
    int fd;
    int on;
    unsigned char state;

    if (argc != 2)
        usage(argv[0]);

    if (strcmp(argv[1], "on") == 0)
        on = 1;
    else if(strcmp(argv[1], "off") == 0)
        on = 0;
    else
        usage(argv[0]);


    fd = open("/dev/console", O_RDWR);
    if (fd == -1)
        err(EXIT_FAILURE, "open /dev/console");

    if (ioctl(fd, KDGETLED, &state) == -1)
        err(EXIT_FAILURE, "KDGETLED");

    if (on)
        state |= LED_CAP;
    else
        state &= ~LED_CAP;

    if (ioctl(fd, KDSETLED, state) == -1)
        err(EXIT_FAILURE, "KDSETLED");

    close(fd);

    return 0;
}

Coloque isso em um arquivo chamado, por exemplo caps.c e compila:

$ gcc -o caps caps.c

Então você pode executá-lo como

$ ./caps on

para ligar o LED ou

$ ./caps off

para desligá-lo (alternar é deixado como um exercício para o leitor).

Observação: para abrir /dev/console , você precisará de direitos de superusuário.

Ainda outra nota: Isso também não impede que seu terminal ou servidor X altere o LED CapsLock ocasionalmente (por exemplo, quando é pressionado). Isso também não funciona no OS X, mas apenas no Linux, já que não há uma maneira padronizada de fazer isso. Por último, você não pode alterar LEDs em vários teclados individualmente.

    
por 18.04.2014 / 16:20
3

Esse código C não funcionaria de qualquer forma - fdopen() requer mais de um argumento e retorna um fluxo de arquivos, não um descritor de arquivos.

Eu me lembro de fazer isso alguns anos atrás, pensei via Xlib, mas não consegui encontrar meu código antigo (ocasionalmente a coleção é removida) mas imaginei que alguém deve ter um aplicativo simples para isso no github ou algo assim. Baixa e eis que a primeira página que encontrei foi esta que, embora não tenha memória de postar isso, foi escrito por mim há quatro anos atrás ("akashiraffee" era um nome que eu usava muito online na época).

Na verdade, ele não usa o Xlib - ele usa ioctl() , como você estava tentando fazer - então deve funcionar sem a GUI. Eu arrumei e tentei aqui com um teclado de 5 LEDs:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/kd.h>
#include <sys/ioctl.h>

int main (void) {
    int tty = open("/dev/console", 0), led;
    unsigned long int arg;

    if (tty < 3) {
        perror("open: ");
        return -1;
    }

    if (ioctl(tty,KDGKBTYPE, &arg) > 0) perror("ioctl: ");
    if (arg == KB_101) puts("You have a 101 key keyboard.");

    for (led = 1; led < 9; led++) {
        if (ioctl(tty,KDSETLED, led) > 0) perror("ioctl led on: ");
        printf("LED %d on...hit enter", led);
        getchar();
        if (ioctl(tty,KDSETLED, led+0xff) > 0) perror("ioctl led off: ");
        printf("off (hit enter)\n");
        getchar();
    }

    close(tty);

    return 0;
} 

Compile que gcc whatever.c -o testleds then ./testleds (note que você precisa de privilégios de superusuário ). Os primeiros 7 funcionaram para mim; alguns deles eram LEDs individuais e alguns deles são combinações.

I disassembled the keyboard

Claro, você precisa que o ENTER funcione para testar isso. Se isso não acontecer, deixe um comentário e eu mudarei para testar automaticamente usando um atraso. Se funcionar no final, eu ficaria feliz em transformá-lo em algo que você pode usar para alternar as luzes por número (por exemplo, toggleLED 3 ).

    
por 18.04.2014 / 17:01
1

Linux

O que você postou é um snippet de código C. (E nem mesmo um trabalho.) Não vai funcionar em um prompt de shell.

Não há interface de shell para ioctl , mas você pode usar Perl.

#!/usr/bin/perl
require "sys/ioctl.ph";
if (@ARGV) {
    $ioctl = 0x4b32; # KDSETLED
    $ARGV[0] =~ /^[0-8]+$/ or die "$0: $ARGV[0]: invalid argument";
    $arg = int($ARGV[0]);
} else {
    $ioctl = 0x4b31; # KDGETLED
    $arg = "?";
}
sysopen CONSOLE, "/dev/console", "r" or die "$0: /dev/console: $!\n";
ioctl CONSOLE, $ioctl, $arg or die "$0: ioctl: $!\n";
print ord($arg), "\n" unless @ARGV;

Ou Python:

#!/usr/bin/env python
import array, fcntl, os, sys
console = open("/dev/console", "r")
if len(sys.argv) > 1:
    fcntl.ioctl(console, 0x4b32, int(sys.argv[1])) # KDSETLED
else:
    arg = array.array('B', [0xff]) # KDGETLED
    fcntl.ioctl(console, 0x4b31, arg, True)
    print arg[0]

Qualquer programa imprime as configurações atuais do LED, se executado sem argumento, e altera o LED se passar por um argumento. As configurações de LED são expressas como a soma de 1 para Scroll Lock, 2 para Num Lock e 4 para Caps Lock. Você pode passar o argumento 8 para redefinir os LEDs para corresponder às configurações de bloqueio do sistema operacional.

Em vez de chamar seu próprio programa, você também pode chamar o utilitário setleds do padrão ferramentas de consola .

Você precisa estar logado em um console de modo de texto (não sob X e não remotamente), ou para executar como root.

Linux, teclado específico

Você também pode ligar e desligar os LEDs de um determinado dispositivo de entrada acessando esse dispositivo em vez do dispositivo do console. O ioctl é diferente: você precisa usar a interface evdev e enviar EV_LED events .

A maneira mais fácil de usar essa interface é instalar o pacote evdev Python ( pip install evdev ).

</dev/input/by-id/usb-_USB_Keyboard-event-kbd \
python -c 'import evdev; dev = evdev.InputDevice("/dev/stdin"); print dev.leds()'

Consulte "Como obter e configurar estados de LED" no tutorial para obter mais exemplos.

OSX

No Mac OS X, você pode acessar os LEDs do teclado por meio do Interface HID . Você pode encontrar exemplos e explicações em Mac OS X Internals ou em Origami Psíquico .

    
por 19.04.2014 / 02:12

Tags