shifter.c (5299B)
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/usb.h> 4 #include <linux/hid.h> 5 #include <linux/input.h> 6 #include <linux/slab.h> // For kmalloc 7 #include <linux/interrupt.h> 8 9 #define DEVICE_VENDOR_ID 0x1020 10 #define DEVICE_PRODUCT_ID 0x8863 11 #define DEVICE_NAME "ODOR-TRUCKSHIFT" 12 13 static struct usb_device_id usb_device_table[] = { 14 { USB_DEVICE(DEVICE_VENDOR_ID, DEVICE_PRODUCT_ID) }, 15 {} 16 }; 17 MODULE_DEVICE_TABLE(usb, usb_device_table); 18 19 struct truckshifter { 20 struct usb_device *udev; 21 struct input_dev *input_device; 22 unsigned char *input_buffer; 23 }; 24 25 static void parse_hid_report(struct truckshifter *truckshifter, unsigned char *data) { 26 // the magic byte 27 unsigned char button_data = data[0]; 28 29 if (button_data & 0x01) { 30 input_event(truckshifter->input_device, EV_KEY, BTN_0, 1); 31 } else { 32 input_event(truckshifter->input_device, EV_KEY, BTN_0, 0); 33 } 34 35 if (button_data & 0x02) { 36 input_event(truckshifter->input_device, EV_KEY, BTN_1, 1); 37 } else { 38 input_event(truckshifter->input_device, EV_KEY, BTN_1, 0); 39 } 40 41 if (button_data & 0x04) { 42 input_event(truckshifter->input_device, EV_KEY, BTN_2, 1); 43 } else { 44 input_event(truckshifter->input_device, EV_KEY, BTN_2, 0); 45 } 46 47 input_sync(truckshifter->input_device); 48 } 49 50 static void read_hid_report(struct urb *urb) { 51 struct truckshifter *truckshifter = urb->context; 52 53 if (urb->status) { 54 printk(KERN_ERR "USB HID report error: %d\n", urb->status); 55 return; 56 } 57 58 parse_hid_report(truckshifter, urb->transfer_buffer); 59 60 int retval = usb_submit_urb(urb, GFP_KERNEL); 61 if (retval) { 62 printk(KERN_ERR "Failed to resubmit URB\n"); 63 } 64 } 65 66 static int start_reading_hid_report(struct truckshifter *truckshifter) { 67 struct usb_interface *interface = truckshifter->udev->actconfig->interface[0]; 68 struct usb_endpoint_descriptor *endpoint; 69 struct urb *urb; 70 int pipe; 71 int max_packet_size; 72 73 endpoint = &interface->cur_altsetting->endpoint[0].desc; 74 pipe = usb_rcvintpipe(truckshifter->udev, endpoint->bEndpointAddress); 75 max_packet_size = usb_endpoint_maxp(endpoint); 76 77 urb = usb_alloc_urb(0, GFP_KERNEL); 78 if (!urb) { 79 printk(KERN_ERR "Failed to allocate URB\n"); 80 return -ENOMEM; 81 } 82 83 truckshifter->input_buffer = kmalloc(max_packet_size, GFP_KERNEL); 84 if (!truckshifter->input_buffer) { 85 printk(KERN_ERR "Failed to allocate buffer\n"); 86 usb_free_urb(urb); 87 return -ENOMEM; 88 } 89 90 usb_fill_int_urb(urb, truckshifter->udev, pipe, truckshifter->input_buffer, max_packet_size, 91 read_hid_report, truckshifter, endpoint->bInterval); 92 93 return usb_submit_urb(urb, GFP_KERNEL); 94 } 95 96 static int usb_driver_probe(struct usb_interface *interface, const struct usb_device_id *id) { 97 struct truckshifter *hid_device; 98 struct usb_device *dev = interface_to_usbdev(interface); 99 struct input_dev *input_device; 100 int retval; 101 102 hid_device = kzalloc(sizeof(struct truckshifter), GFP_KERNEL); 103 if (!hid_device) { 104 printk(KERN_ERR "Failed to allocate memory for HID device\n"); 105 return -ENOMEM; 106 } 107 108 hid_device->udev = dev; 109 110 input_device = input_allocate_device(); 111 if (!input_device) { 112 printk(KERN_ERR "Failed to allocate input device\n"); 113 kfree(hid_device); 114 return -ENOMEM; 115 } 116 117 hid_device->input_device = input_device; 118 119 input_device->name = DEVICE_NAME; 120 input_device->evbit[0] = BIT_MASK(EV_KEY); 121 input_device->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0); 122 input_device->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1); 123 input_device->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2); 124 125 retval = input_register_device(input_device); 126 if (retval) { 127 printk(KERN_ERR "Failed to register input device\n"); 128 input_free_device(input_device); 129 kfree(hid_device); 130 return retval; 131 } 132 133 retval = start_reading_hid_report(hid_device); 134 if (retval) { 135 printk(KERN_ERR "Failed to start reading HID report\n"); 136 input_unregister_device(input_device); 137 kfree(hid_device); 138 return retval; 139 } 140 141 usb_set_intfdata(interface, hid_device); 142 143 printk(KERN_INFO "ODOR-Truckshift connected\n"); 144 return 0; 145 } 146 147 static void usb_driver_disconnect(struct usb_interface *interface) { 148 struct truckshifter *hid_device = usb_get_intfdata(interface); 149 150 if (hid_device) { 151 if (hid_device->input_buffer) 152 kfree(hid_device->input_buffer); 153 input_unregister_device(hid_device->input_device); 154 kfree(hid_device); 155 } 156 157 printk(KERN_INFO "ODOR-Truckshift disconnected\n"); 158 } 159 160 static struct usb_driver custom_hid_driver = { 161 .name = "custom_hid_gamepad", 162 .id_table = usb_device_table, 163 .probe = usb_driver_probe, 164 .disconnect = usb_driver_disconnect, 165 }; 166 167 static int __init usb_driver_init(void) { 168 return usb_register(&custom_hid_driver); 169 } 170 171 static void __exit usb_driver_exit(void) { 172 usb_deregister(&custom_hid_driver); 173 } 174 175 module_init(usb_driver_init); 176 module_exit(usb_driver_exit); 177 178 MODULE_LICENSE("GPL"); 179 MODULE_AUTHOR("Jake Koroman <jakekoroman@proton.me>"); 180 MODULE_DESCRIPTION("Driver for the Labtec ODDOR-TRUCKSHIFT usb truck shifter");