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 (4847B)


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