Skip to content

Instantly share code, notes, and snippets.

@Gadgetoid
Last active August 25, 2023 01:50
Show Gist options
  • Star 41 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Gadgetoid/5a8ceb714de8e630059d30612503653f to your computer and use it in GitHub Desktop.
Save Gadgetoid/5a8ceb714de8e630059d30612503653f to your computer and use it in GitHub Desktop.
Pi 400 KB

Raspberry Pi 400 as a USB HID Keyboard

⚠️ This gist has been deprecated. See this repo instead: https://github.com/Gadgetoid/pi400kb

Hook your Pi 400 up to your PC somehow, using a USB Type-C cable into the power port. Anker make good ones- I used a 3m white one for my tests.

Setup

Add dtoverlay=dwc2 to /boot/config.txt

Reboot!

Run sudo modprobe libcomposite

RUn wget https://gist.github.com/Gadgetoid/5a8ceb714de8e630059d30612503653f/raw/35fde8da7fcd88e7ccd3913c729f2b14bbd4a0a7/pi400kb

Run sudo ./pi400kb

YOUR PI 400 IS NOW A FREAKING KEYBOARD FOR YOUR PC WHAAAAT!?

Your keyboard input will be detached from your Pi while it's forwarded to your host computer.

Press Ctrl + Raspberry to exit and restore your keyboard on the Pi.

#include "gadget-hid.h"
#include <errno.h>
#include <stdio.h>
#include <linux/usb/ch9.h>
#include <usbg/usbg.h>
#include <usbg/function/hid.h>
#include <usbg/function/midi.h>
static char report_desc[] = {
0x05, 0x01,
0x09, 0x06,
0xA1, 0x01,
0x05, 0x07,
0x19, 0xe0,
0x29, 0xe7,
0x15, 0x00,
0x25, 0x01,
0x75, 0x01,
0x95, 0x08,
0x81, 0x02,
0x95, 0x01,
0x75, 0x08,
0x81, 0x01,
0x95, 0x03,
0x75, 0x01,
0x05, 0x08,
0x19, 0x01,
0x29, 0x03,
0x91, 0x02,
0x95, 0x05,
0x75, 0x01,
0x91, 0x01,
0x95, 0x06,
0x75, 0x08,
0x15, 0x00,
0x26, 0xff,
0x00, 0x05,
0x07, 0x19,
0x00, 0x2a,
0xff, 0x00,
0x81, 0x00, // Input (Data, Array, Abs)
0xc0 // End collection
};
int initUSB() {
int ret = -EINVAL;
int usbg_ret;
struct usbg_gadget_attrs g_attrs = {
.bcdUSB = 0x0200,
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = 64, /* Max allowed ep0 packet size */
.idVendor = VENDOR,
.idProduct = PRODUCT,
.bcdDevice = 0x0001, /* Verson of device */
};
struct usbg_gadget_strs g_strs = {
.serial = "0123456789", /* Serial number */
.manufacturer = "Pimoroni", /* Manufacturer */
.product = "Keybow" /* Product string */
};
struct usbg_config_strs c_strs = {
.configuration = "1xHID"
};
struct usbg_f_midi_attrs midi_attrs = {
.index = 1,
.id = "usb1",
.buflen = 128,
.qlen = 16,
.in_ports = 1,
.out_ports = 1
};
struct usbg_f_hid_attrs f_attrs = {
.protocol = 1,
.report_desc = {
.desc = report_desc,
.len = sizeof(report_desc),
},
.report_length = 16,
.subclass = 0,
};
usbg_ret = usbg_init("/sys/kernel/config", &s);
if (usbg_ret != USBG_SUCCESS) {
fprintf(stderr, "Error on usbg init\n");
fprintf(stderr, "Error: %s : %s\n", usbg_error_name(usbg_ret),
usbg_strerror(usbg_ret));
goto out1;
}
usbg_ret = usbg_create_gadget(s, "g1", &g_attrs, &g_strs, &g);
if (usbg_ret != USBG_SUCCESS) {
fprintf(stderr, "Error creating gadget\n");
fprintf(stderr, "Error: %s : %s\n", usbg_error_name(usbg_ret),
usbg_strerror(usbg_ret));
goto out2;
}
usbg_ret = usbg_create_function(g, USBG_F_HID, "usb0", &f_attrs, &f_hid);
if (usbg_ret != USBG_SUCCESS) {
fprintf(stderr, "Error creating function: USBG_F_HID\n");
fprintf(stderr, "Error: %s : %s\n", usbg_error_name(usbg_ret),
usbg_strerror(usbg_ret));
goto out2;
}
usbg_ret = usbg_create_config(g, 1, "config", NULL, &c_strs, &c);
if (usbg_ret != USBG_SUCCESS) {
fprintf(stderr, "Error creating config\n");
fprintf(stderr, "Error: %s : %s\n", usbg_error_name(usbg_ret),
usbg_strerror(usbg_ret));
goto out2;
}
usbg_ret = usbg_add_config_function(c, "keyboard", f_hid);
if (usbg_ret != USBG_SUCCESS) {
fprintf(stderr, "Error adding function: keyboard\n");
fprintf(stderr, "Error: %s : %s\n", usbg_error_name(usbg_ret),
usbg_strerror(usbg_ret));
goto out2;
}
usbg_ret = usbg_enable_gadget(g, DEFAULT_UDC);
if (usbg_ret != USBG_SUCCESS) {
fprintf(stderr, "Error enabling gadget\n");
fprintf(stderr, "Error: %s : %s\n", usbg_error_name(usbg_ret),
usbg_strerror(usbg_ret));
goto out2;
}
ret = 0;
out2:
usbg_cleanup(s);
s = NULL;
out1:
return ret;
}
int cleanupUSB(){
if(g){
usbg_disable_gadget(g);
usbg_rm_gadget(g, USBG_RM_RECURSE);
}
if(s){
usbg_cleanup(s);
}
return 0;
}
#include <linux/usb/ch9.h>
#include <usbg/usbg.h>
#include <usbg/function/hid.h>
#define VENDOR 0x04d9
#define PRODUCT 0x0007
#define HID_REPORT_SIZE 8
usbg_state *s;
usbg_gadget *g;
usbg_config *c;
usbg_function *f_hid;
int initUSB();
int cleanupUSB();
CFLAGS_ALL=-I../libusbgx/build/include -I../bcm2835-1.68/build/include -L../bcm2835-1.68/build/lib -I../lua-5.4.0/src -L../libusbgx/build/lib -L../libserialport/build/lib -L../lua-5.4.0/src -lpng -lz -lpthread -llua -lm -lbcm2835 -ldl
pi400: CFLAGS+=-static $(CFLAGS_ALL) -lusbgx -lconfig -DPI400_USB
pi400: pi400.c gadget-hid.c
$(CC) $^ $(CFLAGS) -o $@
pi400test: CFLAGS+=-static $(CFLAGS_ALL) -lusbgx -lconfig
pi400test: pi400.c gadget-hid.c
$(CC) $^ $(CFLAGS) -o $@
clean:
-rm pi400
-rm pi400test
#include "pi400.h"
#include "gadget-hid.h"
#include <sys/ioctl.h>
#include <linux/hidraw.h>
#include <linux/input.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdio.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <unistd.h>
#define KEYBOARD_DEV "/dev/input/by-id/usb-_Raspberry_Pi_Internal_Keyboard-event-kbd"
#define HID_REPORT_SIZE 8
#define GRAB 1
#define UNGRAB 0
int hid_output;
volatile int running = 0;
int key_index = 0;
void signal_handler(int dummy) {
running = 0;
}
int find_hidraw_device() {
int fd;
int ret;
struct hidraw_devinfo hidinfo;
char path[20];
for(int x = 0; x < 16; x++){
sprintf(path, "/dev/hidraw%d", x);
if ((fd = open(path, O_RDWR | O_NONBLOCK)) == -1) {
continue;
}
ret = ioctl(fd, HIDIOCGRAWINFO, &hidinfo);
if(hidinfo.vendor == VENDOR && hidinfo.product == PRODUCT) {
printf("Found keyboard at: %s\n", path);
return fd;
}
close(fd);
}
return -1;
}
int main() {
int ret;
int fd;
int uinput_fd;
unsigned char buf[HID_REPORT_SIZE];
fd = find_hidraw_device();
if(fd == -1) {
printf("Failed to open keyboard device\n");
return 1;
}
ret = initUSB();
uinput_fd = open(KEYBOARD_DEV, O_RDONLY);
ioctl(uinput_fd, EVIOCGRAB, UNGRAB);
usleep(500000);
ioctl(uinput_fd, EVIOCGRAB, GRAB);
do {
hid_output = open("/dev/hidg0", O_WRONLY | O_NDELAY);
} while (hid_output == -1 && errno == EINTR);
if (hid_output == -1){
printf("Error opening /dev/hidg0 for writing.\n");
return 1;
}
printf("Running...\n");
running = 1;
signal(SIGINT, signal_handler);
while (running){
int c = read(fd, buf, HID_REPORT_SIZE);
if(c != HID_REPORT_SIZE){
continue;
}
for(int x = 0; x < HID_REPORT_SIZE; x++)
{
printf("%x ", buf[x]);
}
printf("\n");
write(hid_output, buf, HID_REPORT_SIZE);
usleep(1000);
if(buf[0] == 0x09){
running = 0;
break;
}
}
for(int x = 0; x < HID_REPORT_SIZE; x++){
buf[x] = 0;
};
write(hid_output, buf, HID_REPORT_SIZE);
ioctl(uinput_fd, EVIOCGRAB, UNGRAB);
close(uinput_fd);
printf("Cleanup USB\n");
cleanupUSB();
return 0;
}
#include <bcm2835.h>
#include <pthread.h>
int initUSB();
int main();
void sendHIDReport();
This file has been truncated, but you can view the full file.
@Gadgetoid
Copy link
Author

Why can't the physical hardware itself, be presented to the USB bus of the client system, as a HID device? It's already presented as a HID device to the underlying OS itself, when booting natively on the 400. IOW, USB-A (400 side) to USB-A (client machine), should present itself as a HID device to the receiving machine. No need to power it up, boot it or run an OS on the device itself.

Is this a joke?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment