Created
August 29, 2022 05:02
-
-
Save pr0xy-t/c3a719bdfb184442989c3a84285562c9 to your computer and use it in GitHub Desktop.
Scroll speed xN for Elecom mouse
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include<linux/kernel.h> | |
| #include<linux/slab.h> | |
| #include<linux/module.h> | |
| #include<linux/init.h> | |
| #include<linux/usb.h> | |
| #include<linux/usb/input.h> | |
| #include<asm/unaligned.h> | |
| #include<linux/limits.h> | |
| #define SPEED_RAITIO 10 | |
| static const struct usb_device_id devices[] = | |
| { | |
| { | |
| .idVendor = 0x056e, | |
| .idProduct = 0x011a, | |
| .bInterfaceNumber = 0, | |
| .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT | USB_DEVICE_ID_MATCH_INT_NUMBER | |
| }, | |
| {} | |
| }; | |
| static void report_handler(u8 *buf, struct input_dev *input) | |
| { | |
| input_report_key(input, BTN_LEFT, buf[0] & 0x01); | |
| input_report_key(input, BTN_RIGHT, buf[0]& 0x02); | |
| input_report_key(input, BTN_MIDDLE, buf[0]& 0x04); | |
| input_report_key(input, BTN_SIDE, buf[0]& 0x08); | |
| input_report_key(input, BTN_EXTRA, buf[0]& 0x10); | |
| if(buf[2] & 0x0f){ | |
| // move left | |
| input_report_rel(input, REL_X, (s8)(buf[1])); | |
| }else{ | |
| // move right | |
| input_report_rel(input, REL_X, buf[1]); | |
| } | |
| if(buf[3] == 0xff){ | |
| // move up | |
| input_report_rel(input, REL_Y, (s8)(0x100 - ((0x100 - buf[2])>>4) )); | |
| }else{ | |
| // move down | |
| input_report_rel(input, REL_Y, (buf[2]>>4)); | |
| } | |
| if(buf[4] == 0xff){ | |
| // wheel down | |
| input_report_rel(input, REL_WHEEL, (s8)(buf[4] * SPEED_RAITIO) ); | |
| }else if(buf[4] == 0x01){ | |
| // wheel up | |
| input_report_rel(input, REL_WHEEL, buf[4] * SPEED_RAITIO); | |
| } | |
| input_sync(input); | |
| } | |
| static void urb_complete(struct urb *urb) | |
| { | |
| switch (urb->status) | |
| { | |
| case 0: | |
| report_handler(urb->transfer_buffer, urb->context); | |
| usb_submit_urb(urb, GFP_ATOMIC); | |
| return; | |
| case -ECONNRESET: | |
| case -ENOENT: | |
| case -ESHUTDOWN: | |
| printk("[speed_mouse] urb shutting down with %d\n", urb->status); | |
| return; | |
| default: | |
| printk("[speed_mouse] urb status %d received\n", urb->status); | |
| usb_submit_urb(urb, GFP_ATOMIC); | |
| return; | |
| } | |
| } | |
| static int input_open(struct input_dev *input) | |
| { | |
| struct urb *urb = input_get_drvdata(input); | |
| return usb_submit_urb(urb, GFP_KERNEL); | |
| } | |
| static void input_close(struct input_dev *input) | |
| { | |
| struct urb *urb = input_get_drvdata(input); | |
| usb_kill_urb(urb); | |
| } | |
| static void setup_input_dev(struct input_dev *input, struct usb_interface *intf, char *phys) | |
| { | |
| input->name = "Scroll speed xN for Elecom mouse"; | |
| input->phys = phys; | |
| input->open = input_open; | |
| input->close = input_close; | |
| input->dev.parent = &intf->dev; | |
| usb_to_input_id(interface_to_usbdev(intf), &input->id); | |
| input_set_capability(input, EV_KEY, BTN_LEFT); | |
| input_set_capability(input, EV_KEY, BTN_RIGHT); | |
| input_set_capability(input, EV_KEY, BTN_MIDDLE); | |
| input_set_capability(input, EV_KEY, BTN_SIDE); | |
| input_set_capability(input, EV_KEY, BTN_EXTRA); | |
| input_set_capability(input, EV_REL, REL_X); | |
| input_set_capability(input, EV_REL, REL_Y); | |
| input_set_capability(input, EV_REL, REL_WHEEL); | |
| } | |
| static int dev_probe(struct usb_interface *intf, const struct usb_device_id *id) | |
| { | |
| struct usb_device *dev = interface_to_usbdev(intf); | |
| struct usb_endpoint_descriptor *epd = &intf->cur_altsetting->endpoint[0].desc; | |
| unsigned int pipe = usb_rcvintpipe(dev, epd->bEndpointAddress); | |
| struct urb *urb; | |
| void *buf; | |
| dma_addr_t dma; | |
| int res; | |
| struct input_dev *input; | |
| char *phys; | |
| printk("[speed_mouse] probe\n"); | |
| printk("[speed_mouse] idVendor=0x%04x\n", dev->descriptor.idVendor); | |
| printk("[speed_mouse] idProduct=0x%04x\n", dev->descriptor.idProduct); | |
| printk("[speed_mouse] bInterfaceNumber=%d\n", intf->cur_altsetting->desc.bInterfaceNumber); | |
| printk("[speed_mouse] bEndpointAddress=0x%02x\n", epd->bEndpointAddress); | |
| printk("[speed_mouse] wMaxPacketSize=%d\n", epd->wMaxPacketSize); | |
| printk("[speed_mouse] bInternal=%d\n", epd->bInterval); | |
| urb = usb_alloc_urb(0, GFP_KERNEL); | |
| if(urb == NULL){ | |
| printk("[speed_mouse] usb_alloc_urb() failed\n"); | |
| res = -ENOMEM; | |
| goto failed0; | |
| } | |
| usb_set_intfdata(intf, urb); | |
| buf = usb_alloc_coherent(dev, epd->wMaxPacketSize, GFP_KERNEL, &dma); | |
| if(buf == NULL){ | |
| printk("[speed_mouse] usb_alloc_coherent() failed\n"); | |
| res = -ENOMEM; | |
| goto failed1; | |
| } | |
| usb_fill_int_urb(urb, dev, pipe, buf, epd->wMaxPacketSize, urb_complete, NULL, epd->bInterval); | |
| urb->transfer_dma = dma; | |
| urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | |
| input = devm_input_allocate_device(&intf->dev); | |
| if (input == NULL) { | |
| printk("[speed_mouse] devm_input_allocate_device() failed.\n"); | |
| res = -ENOMEM; | |
| goto failed2; | |
| } | |
| input_set_drvdata(input, urb); | |
| phys = kmalloc(PATH_MAX, GFP_KERNEL); | |
| if (phys == NULL) { | |
| printk("[speed_mouse] kmalloc() failed\n"); | |
| res = -ENOMEM; | |
| goto failed2; | |
| } | |
| usb_make_path(dev, phys, PATH_MAX); | |
| strlcat(phys, "/input0", PATH_MAX); | |
| setup_input_dev(input, intf, phys); | |
| urb->context = input; | |
| res = input_register_device(input); | |
| if (res) { | |
| printk("[speed_mouse] input_register_device() failed\n"); | |
| goto failed3; | |
| } | |
| return 0; | |
| failed3: | |
| kfree(phys); | |
| failed2: | |
| usb_free_coherent(dev, epd->wMaxPacketSize, buf, dma); | |
| failed1: | |
| usb_free_urb(urb); | |
| failed0: | |
| usb_set_intfdata(intf, NULL); | |
| return res; | |
| } | |
| static void dev_disconnect(struct usb_interface *intf) | |
| { | |
| struct urb *urb = usb_get_intfdata(intf); | |
| if (urb) { | |
| usb_kill_urb(urb); | |
| usb_free_coherent( | |
| urb->dev, | |
| urb->transfer_buffer_length, | |
| urb->transfer_buffer, | |
| urb->transfer_dma); | |
| usb_free_urb(urb); | |
| } | |
| printk("[speed_mouse] disconnect\n"); | |
| } | |
| static struct usb_driver driver = | |
| { | |
| .name = "Scroll speed xN for Elecom mouse", | |
| .id_table = devices, | |
| .probe = dev_probe, | |
| .disconnect = dev_disconnect, | |
| }; | |
| static int __init mod_init(void) | |
| { | |
| int ret; | |
| printk("[speed_mouse] init\n"); | |
| ret = usb_register(&driver); | |
| if(ret){ | |
| printk("[speed_mouse] usb_register() failed\n"); | |
| return ret; | |
| } | |
| return 0; | |
| } | |
| static void __exit mod_exit(void) | |
| { | |
| printk("[speed_mouse] exit\n"); | |
| usb_deregister(&driver); | |
| } | |
| module_init(mod_init); | |
| module_exit(mod_exit); | |
| MODULE_DEVICE_TABLE(usb ,devices); | |
| MODULE_LICENSE("GPL"); | |
| MODULE_AUTHOR("pr0xy"); | |
| MODULE_DESCRIPTION("Scroll speed xN for Elecom mouse"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment