Nous avons vu dans le premier article de cette série comment rediriger les flux audio du téléphone Android vers la carte son de la raspberry-pi.
Ce deuxième article va s'intéresser à une autre possibilité d'utilisation de la connexion USB entre les deux appareils : la possibilité de rediriger un clavier et une souris branchés sur la carte raspberry-pi pour les utiliser sur le téléphone Android.
Rediriger le clavier et la souris vers le téléphone
La plupart des docks pour téléphones portables permettent de contrôler la musique grâce à des touches multimédia (play, pause, next etc...) Android généralise cette capacité en permettant de gérer tout périphérique de type HID (Human Interface Device, c'est à dire les claviers, les souris, mais aussi les joysticks et gamepads). Bien sur, le bus USB ne permet pas aux esclaves de parler entre eux. Ils ne peuvent parler qu'avec l'hôte or, dans notre configuration, l'hôte est la carte raspberry-pi. Celle-ci devra donc rediriger les événements USB des périphériques vers le téléphone. Nous verrons comment le protocole AoA nous permets de le faire.
Les périphériques HID se décrivent eux mêmes sur le bus USB. Les touches et boutons disponibles, la sensibilité de la souris sont codés dans un descripteur que l'on peut demander au périphérique. Ces périphériques envoient ensuite des événements à l’hôte qui peut les traiter comme il le souhaite.
Le protocol AoA 2.0 autorise l'envoi des informations de configuration et des événements HID de l'hôte vers le téléphone Android. En d'autre terme la raspberry pi peut se comporter comme un clavier ou une souris pour le téléphone. Nous nous contenterons de transmettre les informations d'un véritable périphérique vers le téléphone, mais rien n’empêcherait d'émuler un clavier ou une souris entièrement de façon logiciel.
Tout d'abord il faut récupérer la description des capacités du périphérique HID auprès de celui-ci. L'instruction libusb à utiliser est la suivante :
hid->descriptor_size = libusb_control_transfer( hid->handles.handle, LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_DT_REPORT << 8, 0,hid->descriptor,256,0);
(Ici, hid->handles.handle est le handle du périphérique HID, pas celui du téléphone)
Nous envoyons ensuite ces informations au téléphone Android. Cela se fait en deux étapes. Tout d'abord, nous déclarons l'existence du périphérique au téléphone Android et nous lui donnons un identifiant unique (ici codé en dur à 1). Nous transmettons dans ce même message la taille du descripteur HID.
response = libusb_control_transfer(android->handles.handle,0x40,54,1,hid->descriptor_size,NULL,0,0);
Ensuite nous transmettons le descripteur lui même.
response = libusb_control_transfer(android->handles.handle,0x40,56,1,0,hid->descriptor,hid->descriptor_size,0);
Il ne nous reste plus qu'à récupérer les paquets venant du périphérique HID et les rediriger vers le périphérique Android. Nous allons utiliser l'API asynchrone de libusb pour cela.
Tout d'abord libusb doit se mettre en réception des paquets venant du périphérique HID et avoir un callback callback_hid à appeler lorsqu'un paquet est reçu :
struct libusb_transfer *hid_transfer = libusb_alloc_transfer(0); unsigned char * keybuf = malloc(hid->packet_size); libusb_fill_interrupt_transfer(hid_transfer, hid->handles.handle, hid->endpoint_in , keybuf, hid->packet_size, callback_hid, android, 0); int rc = libusb_submit_transfer(hid_transfer);
Ensuite, le callback lui-même doit renvoyer le paquet à l'identique vers le périphérique Android
static void callback_hid(struct libusb_transfer *transfer) { android_device * android = transfer->user_data; int rc = 0; if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { struct libusb_transfer *android_transfer = libusb_alloc_transfer(0); unsigned char * keybuf = malloc(transfer->actual_length+LIBUSB_CONTROL_SETUP_SIZE); memcpy(keybuf+LIBUSB_CONTROL_SETUP_SIZE,transfer->buffer,transfer->actual_length); libusb_fill_control_setup(keybuf,0x40,57,1,0,transfer->actual_length); libusb_fill_control_transfer(android_transfer, android->handles.handle, keybuf, NULL, NULL, 0); android_transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; int rc = libusb_submit_transfer(android_transfer); rc = libusb_submit_transfer(transfer); if(rc) printf("USB error : %s\n",libusb_error_name(rc)); } else if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) { //printf("partial transfer \n"); rc = libusb_submit_transfer(transfer); if(rc) printf("USB error : %s\n",libusb_error_name(rc)); } }
Et voila. Une fois que la boucle principal de libusb est activée notre clavier ou notre souris seront redirigés vers le périphérique android.
Code source
Ce programme recherche un périphérique HID (il prends le premier qu'il trouve) et un périphérique Android configuré (à nouveau, il prend le premier qu'il trouve).
Si il a trouvé un périphérique HID il enverra les paquets vers le périphérique Android.
Attention. Ce programme va prendre la main sur un périphérique HID, le déconnectant totalement du kernel. Si le clavier principal de votre ordinateur est un clavier USB vous risquez de perdre la main sur votre ordinateur principal. Il suffit de déconnecter et reconnecter votre périphérique pour que le kernel le récupère.
Ce programme doit être lancé en tant que root pour pouvoir prendre la main sur les périphériques USB.
Notez que ce programme sert également d'exemple pour le troisième article de cette série et qu'il contient donc la gestion des périphériques USB spécifiques.