#include "linux/delay.h" #include "linux/input-event-codes.h" #include "linux/input.h" #include "linux/kern_levels.h" #include "linux/printk.h" #include #include #include #include #include #include #include "startbit.h" #define IR_SLEEP 150 struct point { int x; int y; int is_ir; }; struct accel { int x; int y; int z; }; struct wiimouse_dev { struct hid_device* hdev; struct input_dev* in_dev; struct point points[4]; struct accel accel; }; static void parse_accel(struct wiimouse_dev* wdev, __u8 data[5]) { // Byte 0 of button data contains 2 lsbs of X - 10 bit precision // Byte 1 contains lsbs of Y and Z - 9 bit precision wdev->accel.x = (data[2] << 2) | (data[0] & 0b01100000) >> 5; wdev->accel.y = data[3]; // Get bit 0 int y_bit0 = data[3] & 0x01; // Get bit 1 int y_bit1 = data[1] & 0b00100000 >> 5; // Clear last bit wdev->accel.y &= 0b11111110; // Apply bit 1 (pre-shift) wdev->accel.y |= y_bit1; // Shift to left by 1 wdev->accel.y <<= 1; wdev->accel.y |= y_bit0; wdev->accel.z = data[4]; // Get bit 0 int z_bit0 = data[4] & 0x01; // Get bit 1 int z_bit1 = data[1] & 0b01000000 >> 6; // Clear last bit wdev->accel.z &= 0b11111110; // Apply bit 1 (pre-shift) wdev->accel.z |= z_bit1; // Shift to left by 1 wdev->accel.z <<= 1; wdev->accel.z |= z_bit0; } static void parse_extended_mode_ir(struct wiimouse_dev* wdev, __u8 data[10]) { wdev->points[0].x = data[0] << 2 | ((data[2] & 0x30) >> 4); wdev->points[0].y = data[1] << 2 | ((data[2] & 0xc0) >> 6); wdev->points[1].x = data[3] << 2 | (data[2] & 0x03); wdev->points[1].y = data[4] << 2 | ((data[2] & 0x0c) >> 2); wdev->points[2].x = data[0 + 5] << 2 | ((data[2 + 5] & 0x30) >> 4); wdev->points[2].y = data[1 + 5] << 2 | ((data[2 + 5] & 0xc0) >> 6); wdev->points[3].x = data[3 + 5] << 2 | (data[2 + 5] & 0x03); wdev->points[3].y = data[4 + 5] << 2 | ((data[2 + 5] & 0x0c) >> 2); } /* * There can only be 6 combinations of points and one of them is the IR bar * p1, p2 | p1, p3 | p1, p4 | p2, p3 | p2, p4 | p3, p4 | * Or (4 * 3) / 2 = 6 * We need to find a line between 2 points of min distance such that: * 1. px and py are the only points on the line * 2. no other points are within some X distance of the line * * These points are normalized to rotation of controller */ static void get_ir_bar_points(struct wiimouse_dev* wdev) { } static const struct hid_device_id wiimouse_ids[] = { {HID_BLUETOOTH_DEVICE(NINTENDO_WIIMOTE_VENDOR_ID, NINTENDO_WIIMOTE_PRODUCT_ID)}, {} }; static int wiimote_ir_init(struct hid_device* hdev) { int ret = 0; __u8 enable_ir1[2] = { 0x13, 0x04}; if ((ret = hid_hw_output_report(hdev, (__u8*)enable_ir1, 2)) < 0) { printk(KERN_DEBUG "enable ir 1"); return ret; } msleep(IR_SLEEP); __u8 enable_ir2[2] = { 0x1a, 0x04}; if ((ret = hid_hw_output_report(hdev, (__u8*)enable_ir2, 2)) < 0) { printk(KERN_DEBUG "enable ir 2"); return ret; } msleep(IR_SLEEP); // Can be 0x08 or 0x01?? Weird behavior with wiimote... Wii writes 0x01 while libgc does former... // Using 0x08 here (wii logic) which seems to work __u8 enable[22] = { 0x16, 0x04, 0xb0, 0x00, 0x30, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; // Max sensitivity __u8 sensitive_1[22] = { 0x16, 0x04, 0xb0, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; __u8 sensitive_2[22] = { 0x16, 0x04, 0xb0, 0x00, 0x1a, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; // Mode is 0x03 - extended mode __u8 mode[22] = { 0x16, 0x04, 0xb0, 0x00, 0x33, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; // Final rewrite to complete init __u8 final[22] = { 0x16, 0x04, 0xb0, 0x00, 0x30, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; if ((ret = hid_hw_output_report(hdev, (__u8*)enable, 22)) < 0) { printk(KERN_DEBUG "eeprom 1"); return ret; } msleep(IR_SLEEP); if ((ret = hid_hw_output_report(hdev, (__u8*)sensitive_1, 22)) < 0) { printk(KERN_DEBUG "sensitive 1"); return ret; } msleep(IR_SLEEP); if ((ret = hid_hw_output_report(hdev, (__u8*)sensitive_2, 22)) < 0) { printk(KERN_DEBUG "sensitive 2"); return ret; } msleep(IR_SLEEP); if ((ret = hid_hw_output_report(hdev, (__u8*)mode, 22)) < 0) { printk(KERN_DEBUG "mode"); return ret; } msleep(IR_SLEEP); if ((ret = hid_hw_output_report(hdev, (__u8*)final, 22)) < 0) { printk(KERN_DEBUG "eeprom 2"); return ret; } msleep(IR_SLEEP); ret = 0; return ret; } /* * wiimote_init * Initialize certain aspects of the wiimote such as the LEDs and sensors by * sending appropriate HID reports to the device. Returns 0 on success, non-zero * on error * Details about the report details can be found in https://wiibrew.org/wiki/Wiimote */ static int wiimote_init(struct hid_device* hdev) { int ret = 0; __u8 rumble[2] = { HID_REPORT_ID_WIIMOTE_RUMBLE_ENABLE, HID_REPORT_DATA_WIIMOTE_RUMBLE_ENABLE }; if ((ret = hid_hw_output_report(hdev, (__u8*)rumble, 2)) < 0) return ret; __u8 led_p1[2] = { HID_REPORT_ID_WIIMOTE_LED_ENABLE, HID_REPORT_DATA_WIIMOTE_LED_ENABLE_P1 }; if ((ret = hid_hw_output_report(hdev, (__u8*)led_p1, 2)) < 0) return ret; // 0x33 - Core Buttons and Accelerometer with 12 IR bytes // Format: (a1) 33 BB BB AA AA AA II II II II II II II II II II II II // 0x04 means continous reporting __u8 report_mode[3] = { 0x12, 0x04, 0x33 }; if ((ret = hid_hw_output_report(hdev, (__u8*)report_mode, 3)) < 0) return ret; return ret; } static int wiimote_event(struct hid_device* hdev, struct hid_report* report, u8 *data, int size) { printk(KERN_DEBUG "Report ID: %02x, Size: %d", report->id, size); printk(KERN_DEBUG "Button Data: %02x, %02x", data[1], data[2]); printk(KERN_DEBUG "Accel Data: %02x,%02x,%02x", data[3], data[4], data[5]); printk(KERN_DEBUG "IR Data: %02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x", data[6], data[7], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15]); struct wiimouse_dev* wdev = hid_get_drvdata(hdev); //Parse and save IR data parse_extended_mode_ir(wdev, data + 6); parse_accel(wdev, data); if (data[2] & 0x08) { printk(KERN_DEBUG "A button was pressed\n"); struct input_dev* in_dev = wdev->in_dev; input_report_key(in_dev, BTN_LEFT, 1); input_sync(in_dev); input_report_key(in_dev, BTN_LEFT, 0); input_sync(in_dev); } if (data[2] & 0x80) { printk(KERN_DEBUG "HOME button was pressed, recalibrating IR\n"); wiimote_ir_init(hdev); } return 0; } MODULE_DEVICE_TABLE(hid, wiimouse_ids); static int wiimouse_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret = 0; struct wiimouse_dev* wdev; wdev = devm_kzalloc(&hdev->dev, sizeof(struct wiimouse_dev), GFP_KERNEL); wdev->hdev = hdev; struct input_dev* in_dev = input_allocate_device(); if(in_dev == NULL) { return -1; } wdev->in_dev = in_dev; in_dev->name = "Wiimouse"; in_dev->id.vendor = NINTENDO_WIIMOTE_VENDOR_ID; in_dev->id.product = NINTENDO_WIIMOTE_PRODUCT_ID; in_dev->id.version = 0x0000; set_bit(EV_KEY, in_dev->evbit); set_bit(BTN_LEFT, in_dev->keybit); ret = input_register_device(in_dev); if (ret) { input_free_device(in_dev); return ret; } printk(KERN_DEBUG "wiimouse-mouse: Starting parse"); /* Init hid device, setup input subsystem etc. */ ret = hid_parse(hdev); if (ret) return ret; ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) return ret; wiimote_init(hdev); printk(KERN_DEBUG "starting ir init"); if ((ret = wiimote_ir_init(hdev)) != 0) return ret; hid_set_drvdata(hdev, wdev); /* Init done, device ready to use */ printk(KERN_DEBUG "Hello I'm a wiimouse!"); return 0; } static void wiimote_remove(struct hid_device* hdev) { struct wiimouse_dev* wdev = hid_get_drvdata(hdev); input_unregister_device(wdev->in_dev); } static void wiimote_report(struct hid_device *hdev, struct hid_report *report) { printk(KERN_DEBUG "Report ID: %02x\n", report->id); } static int wiimote_mapping(struct hid_device *hdev, struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { //printk(KERN_DEBUG "ID: %s", hidinput->name); return 0; } static struct hid_driver wiimouse_driver = { .name = "wiimouse-mouse", .id_table = wiimouse_ids, .probe = wiimouse_probe, .raw_event = wiimote_event, .report = wiimote_report, .remove = wiimote_remove, .input_mapping = wiimote_mapping }; module_hid_driver(wiimouse_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Aaditya Dhruv"); MODULE_DESCRIPTION("Use your wiimote as a simple mouse");