odor-truckshift-driver

a simple usb driver for the ODOR-TRUCKSHIFT
git clone git://git.jakekoroman.com/odor-truckshift-driver
Log | Files | Refs | README

commit bfe77dbe567b556b530f54f420e878bbdb8a17a5
parent bc329fef93b08a7c7e23e984fd8c36c8c0f411d9
Author: Jake Koroman <jakekoroman@proton.me>
Date:   Tue, 11 Feb 2025 18:38:20 -0500

shifter.c: make device show up as a joystick to work within wine/proton.

Diffstat:
Mshifter.c | 247+++++++++++++++++++++++++++++++++++++++----------------------------------------
1 file changed, 123 insertions(+), 124 deletions(-)

diff --git a/shifter.c b/shifter.c @@ -3,7 +3,7 @@ #include <linux/usb.h> #include <linux/hid.h> #include <linux/input.h> -#include <linux/slab.h> // For kmalloc +#include <linux/slab.h> #include <linux/interrupt.h> #define DEVICE_VENDOR_ID 0x1020 @@ -11,165 +11,164 @@ #define DEVICE_NAME "ODOR-TRUCKSHIFT" static struct usb_device_id usb_device_table[] = { - { USB_DEVICE(DEVICE_VENDOR_ID, DEVICE_PRODUCT_ID) }, - {} + { USB_DEVICE(DEVICE_VENDOR_ID, DEVICE_PRODUCT_ID) }, + {} }; MODULE_DEVICE_TABLE(usb, usb_device_table); struct truckshifter { - struct usb_device *udev; - struct input_dev *input_device; - unsigned char *input_buffer; + struct usb_device *udev; + struct input_dev *input_device; + unsigned char *input_buffer; }; static void parse_hid_report(struct truckshifter *truckshifter, unsigned char *data) { - // the magic byte - unsigned char button_data = data[0]; + unsigned char button_data = data[0]; if (button_data & 0x01) { - input_event(truckshifter->input_device, EV_KEY, BTN_0, 1); - } else { - input_event(truckshifter->input_device, EV_KEY, BTN_0, 0); - } - - if (button_data & 0x02) { - input_event(truckshifter->input_device, EV_KEY, BTN_1, 1); - } else { - input_event(truckshifter->input_device, EV_KEY, BTN_1, 0); - } - - if (button_data & 0x04) { - input_event(truckshifter->input_device, EV_KEY, BTN_2, 1); - } else { - input_event(truckshifter->input_device, EV_KEY, BTN_2, 0); - } - - input_sync(truckshifter->input_device); + input_event(truckshifter->input_device, EV_KEY, BTN_A, 1); + } else { + input_event(truckshifter->input_device, EV_KEY, BTN_A, 0); + } + + if (button_data & 0x02) { + input_event(truckshifter->input_device, EV_KEY, BTN_B, 1); + } else { + input_event(truckshifter->input_device, EV_KEY, BTN_B, 0); + } + + if (button_data & 0x04) { + input_event(truckshifter->input_device, EV_KEY, BTN_X, 1); + } else { + input_event(truckshifter->input_device, EV_KEY, BTN_X, 0); + } + + input_sync(truckshifter->input_device); } static void read_hid_report(struct urb *urb) { struct truckshifter *truckshifter = urb->context; - if (urb->status) { - printk(KERN_ERR "USB HID report error: %d\n", urb->status); - return; - } + if (urb->status) { + printk(KERN_ERR "USB HID report error: %d\n", urb->status); + return; + } - parse_hid_report(truckshifter, urb->transfer_buffer); + parse_hid_report(truckshifter, urb->transfer_buffer); - int retval = usb_submit_urb(urb, GFP_KERNEL); - if (retval) { - printk(KERN_ERR "Failed to resubmit URB\n"); - } + int retval = usb_submit_urb(urb, GFP_KERNEL); + if (retval) { + printk(KERN_ERR "Failed to resubmit URB\n"); + } } static int start_reading_hid_report(struct truckshifter *truckshifter) { - struct usb_interface *interface = truckshifter->udev->actconfig->interface[0]; - struct usb_endpoint_descriptor *endpoint; - struct urb *urb; - int pipe; - int max_packet_size; - - endpoint = &interface->cur_altsetting->endpoint[0].desc; - pipe = usb_rcvintpipe(truckshifter->udev, endpoint->bEndpointAddress); - max_packet_size = usb_endpoint_maxp(endpoint); - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - printk(KERN_ERR "Failed to allocate URB\n"); - return -ENOMEM; - } - - truckshifter->input_buffer = kmalloc(max_packet_size, GFP_KERNEL); - if (!truckshifter->input_buffer) { - printk(KERN_ERR "Failed to allocate buffer\n"); - usb_free_urb(urb); - return -ENOMEM; - } - - usb_fill_int_urb(urb, truckshifter->udev, pipe, truckshifter->input_buffer, max_packet_size, - read_hid_report, truckshifter, endpoint->bInterval); - - return usb_submit_urb(urb, GFP_KERNEL); + struct usb_interface *interface = truckshifter->udev->actconfig->interface[0]; + struct usb_endpoint_descriptor *endpoint; + struct urb *urb; + int pipe; + int max_packet_size; + + endpoint = &interface->cur_altsetting->endpoint[0].desc; + pipe = usb_rcvintpipe(truckshifter->udev, endpoint->bEndpointAddress); + max_packet_size = usb_endpoint_maxp(endpoint); + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + printk(KERN_ERR "Failed to allocate URB\n"); + return -ENOMEM; + } + + truckshifter->input_buffer = kmalloc(max_packet_size, GFP_KERNEL); + if (!truckshifter->input_buffer) { + printk(KERN_ERR "Failed to allocate buffer\n"); + usb_free_urb(urb); + return -ENOMEM; + } + + usb_fill_int_urb(urb, truckshifter->udev, pipe, truckshifter->input_buffer, max_packet_size, + read_hid_report, truckshifter, endpoint->bInterval); + + return usb_submit_urb(urb, GFP_KERNEL); } static int usb_driver_probe(struct usb_interface *interface, const struct usb_device_id *id) { - struct truckshifter *hid_device; - struct usb_device *dev = interface_to_usbdev(interface); - struct input_dev *input_device; - int retval; - - hid_device = kzalloc(sizeof(struct truckshifter), GFP_KERNEL); - if (!hid_device) { - printk(KERN_ERR "Failed to allocate memory for HID device\n"); - return -ENOMEM; - } - - hid_device->udev = dev; - - input_device = input_allocate_device(); - if (!input_device) { - printk(KERN_ERR "Failed to allocate input device\n"); - kfree(hid_device); - return -ENOMEM; - } - - hid_device->input_device = input_device; - - input_device->name = DEVICE_NAME; - input_device->evbit[0] = BIT_MASK(EV_KEY); - input_device->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0); - input_device->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1); - input_device->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2); - - retval = input_register_device(input_device); - if (retval) { - printk(KERN_ERR "Failed to register input device\n"); - input_free_device(input_device); - kfree(hid_device); - return retval; - } - - retval = start_reading_hid_report(hid_device); - if (retval) { - printk(KERN_ERR "Failed to start reading HID report\n"); - input_unregister_device(input_device); - kfree(hid_device); - return retval; - } - - usb_set_intfdata(interface, hid_device); - - printk(KERN_INFO "ODOR-Truckshift connected\n"); - return 0; + struct truckshifter *truckshifter; + struct usb_device *dev = interface_to_usbdev(interface); + struct input_dev *input_device; + int retval; + + truckshifter = kzalloc(sizeof(struct truckshifter), GFP_KERNEL); + if (!truckshifter) { + printk(KERN_ERR "Failed to allocate memory for HID device\n"); + return -ENOMEM; + } + + truckshifter->udev = dev; + + input_device = input_allocate_device(); + if (!input_device) { + printk(KERN_ERR "Failed to allocate input device\n"); + kfree(truckshifter); + return -ENOMEM; + } + + truckshifter->input_device = input_device; + + input_device->name = DEVICE_NAME; + input_device->evbit[0] = BIT_MASK(EV_KEY); + input_device->keybit[BIT_WORD(BTN_A)] |= BIT_MASK(BTN_A); + input_device->keybit[BIT_WORD(BTN_B)] |= BIT_MASK(BTN_B); + input_device->keybit[BIT_WORD(BTN_X)] |= BIT_MASK(BTN_X); + + retval = input_register_device(input_device); + if (retval) { + printk(KERN_ERR "Failed to register "DEVICE_NAME" \n"); + input_free_device(input_device); + kfree(truckshifter); + return retval; + } + + retval = start_reading_hid_report(truckshifter); + if (retval) { + printk(KERN_ERR "Failed to start reading HID report\n"); + input_unregister_device(input_device); + kfree(truckshifter); + return retval; + } + + usb_set_intfdata(interface, truckshifter); + + printk(KERN_INFO DEVICE_NAME" connected\n"); + return 0; } static void usb_driver_disconnect(struct usb_interface *interface) { - struct truckshifter *hid_device = usb_get_intfdata(interface); + struct truckshifter *hid_device = usb_get_intfdata(interface); - if (hid_device) { - if (hid_device->input_buffer) - kfree(hid_device->input_buffer); - input_unregister_device(hid_device->input_device); - kfree(hid_device); - } + if (hid_device) { + if (hid_device->input_buffer) + kfree(hid_device->input_buffer); + input_unregister_device(hid_device->input_device); + kfree(hid_device); + } - printk(KERN_INFO "ODOR-Truckshift disconnected\n"); + printk(KERN_INFO DEVICE_NAME" disconnected\n"); } static struct usb_driver custom_hid_driver = { - .name = DEVICE_NAME, - .id_table = usb_device_table, - .probe = usb_driver_probe, - .disconnect = usb_driver_disconnect, + .name = DEVICE_NAME, + .id_table = usb_device_table, + .probe = usb_driver_probe, + .disconnect = usb_driver_disconnect, }; static int __init usb_driver_init(void) { - return usb_register(&custom_hid_driver); + return usb_register(&custom_hid_driver); } static void __exit usb_driver_exit(void) { - usb_deregister(&custom_hid_driver); + usb_deregister(&custom_hid_driver); } module_init(usb_driver_init);