odor-truckshift-driver

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

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");