📄 mouse.c
字号:
/* * USB HID boot protocol mouse support based on MS BusMouse driver, psaux * driver, and Linus's skeleton USB mouse driver. Fixed up a lot by Linus. * * Brad Keryan 4/3/1999 * * version 0.50 GA 2000/1/15 * now really survives re-plugging with force=1 * improved error handling * * version 0.40 Georg Acher 1999/10/30 * URBification for UHCI-Acher/Fliegl/Sailer * * version 0.30? Paul Ashton 1999/08/19 - Fixed behaviour on mouse * disconnect and suspend/resume. Added module parameter "force=1" * to allow opening of the mouse driver before mouse has been plugged * in (enables consistent XF86Config settings). Fixed module use count. * Documented missing blocking/non-blocking read handling (not fixed). * * version 0.20: Linus rewrote read_mouse() to do PS/2 and do it * correctly. Events are added together, not queued, to keep the rodent sober. * * version 0.02: Hmm, the mouse seems drunk because I'm queueing the events. * This is wrong: when an application (like X or gpm) reads the mouse device, * it wants to find out the mouse's current position, not its recent history. * The button thing turned out to be UHCI not flipping data toggle, so half the * packets were thrown out. * * version 0.01: Switched over to busmouse protocol, and changed the minor * number to 32 (same as uusbd's hidbp driver). Buttons work more sanely now, * but it still doesn't generate button events unless you move the mouse. * * version 0.0: Driver emulates a PS/2 mouse, stealing /dev/psaux (sorry, I * know that's not very nice). Moving in the X and Y axes works. Buttons don't * work right yet: X sees a lot of MotionNotify/ButtonPress/ButtonRelease * combos when you hold down a button and drag the mouse around. Probably has * some additional bugs on an SMP machine. */#include <linux/version.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/signal.h>#include <linux/errno.h>#include <linux/random.h>#include <linux/poll.h>#include <linux/init.h>#include <linux/malloc.h>#include <linux/module.h>#include "usb.h"#define MODSTR "mouse.c: "struct mouse_state { unsigned char buttons; /* current button state */ long dx; /* dx, dy, dz are change since last read */ long dy; long dz; int present; /* this mouse is plugged in */ int active; /* someone is has this mouse's device open */ int ready; /* the mouse has changed state since the last read */ int suspended; /* mouse disconnected */ wait_queue_head_t wait; /* for polling */ struct fasync_struct *fasync; /* later, add a list here to support multiple mice */ /* but we will also need a list of file pointers to identify it */ /* FIXME: move these to a per-mouse structure */ struct usb_device *dev; /* host controller this mouse is on */ void* irq_handle; /* host controller's IRQ transfer handle */ char* buffer; urb_t* urb; int stalled_flag; __u8 bEndpointAddress; /* these are from the endpoint descriptor */ __u8 bInterval; /* ... used when calling usb_request_irq */};static struct mouse_state static_mouse_state;spinlock_t usb_mouse_lock = SPIN_LOCK_UNLOCKED;static int force=0; /* allow the USB mouse to be opened even if not there (yet) */MODULE_PARM(force,"i");int xxx=0;//static int mouse_irq(int state, void *__buffer, int len, void *dev_id)static void mouse_irq(urb_t *urb){ signed char *data = urb->transfer_buffer; int state=urb->status; int len=urb->actual_length; /* finding the mouse is easy when there's only one */ struct mouse_state *mouse = urb->context; if (state) printk(KERN_DEBUG "%s(%d):state %d, bp %p, len %d\n", __FILE__, __LINE__, state, data, len); //printk("mouseirq: %i\n",xxx++); /* * USB_ST_NOERROR is the normal case. * USB_ST_REMOVED occurs if mouse disconnected or suspend/resume * USB_ST_INTERNALERROR occurs if system suspended then mouse removed * followed by resume. On UHCI could then occur every second * In both cases, suspend the mouse * On other states, ignore */ switch (state) { case USB_ST_REMOVED: printk(KERN_DEBUG "%s(%d): Suspending\n", __FILE__, __LINE__); mouse->suspended = 1; // FIXME stop interrupt! return; case -EPIPE: case -EILSEQ: case -EPROTO: case -ENOSR: case -EREMOTEIO: mouse->stalled_flag = 1; // try to recover later wake_up_interruptible(&mouse->wait); return; case USB_ST_NOERROR: break; default: return; /* ignore */ } /* if a mouse moves with no one listening, do we care? no */ if(!mouse->active) return; /* if the USB mouse sends an interrupt, then something noteworthy must have happened */ mouse->buttons = data[0] & 0x0f; mouse->dx += data[1]; /* data[] is signed, so this works */ mouse->dy -= data[2]; /* y-axis is reversed */ mouse->dz -= data[3]; mouse->ready = 1; add_mouse_randomness((mouse->buttons << 24) + (mouse->dz << 16 ) + (mouse->dy << 8) + mouse->dx); wake_up_interruptible(&mouse->wait); if (mouse->fasync)#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,20) kill_fasync(mouse->fasync, SIGIO, POLL_IN);#else kill_fasync(mouse->fasync, SIGIO);#endif return;}static int fasync_mouse(int fd, struct file *filp, int on){ int retval; struct mouse_state *mouse = &static_mouse_state; retval = fasync_helper(fd, filp, on, &mouse->fasync); if (retval < 0) return retval; return 0;}static int release_mouse(struct inode * inode, struct file * file){ struct mouse_state *mouse = &static_mouse_state; fasync_mouse(-1, file, 0); printk(KERN_DEBUG "%s(%d): MOD_DEC\n", __FILE__, __LINE__); MOD_DEC_USE_COUNT; if (--mouse->active == 0) { mouse->suspended = 0; /* stop polling the mouse while its not in use */ usb_unlink_urb(mouse->urb); } return 0;}static int open_mouse(struct inode * inode, struct file * file){ struct mouse_state *mouse = &static_mouse_state; int ret; unsigned int pipe; printk(KERN_DEBUG "%s(%d): open_mouse\n", __FILE__, __LINE__); /* * First open may fail since mouse_probe() may get called after this * if module load is in response to the open * mouse_probe() sets mouse->present. This open can be delayed by * specifying force=1 in module load * This helps if you want to insert the USB mouse after starting X */ mouse->ready=0; if (!mouse->present) { if (force) /* always load the driver even if no mouse (yet) */ { printk(KERN_DEBUG "%s(%d): forced open\n", __FILE__, __LINE__); mouse->suspended = 1; } else return -EINVAL; } /* prevent the driver from being unloaded while its in use */ printk(KERN_DEBUG "%s(%d): MOD_INC\n", __FILE__, __LINE__); /* Increment use count even if already active */ MOD_INC_USE_COUNT; if (mouse->active++) return 0; /* flush state */ mouse->buttons = mouse->dx = mouse->dy = mouse->dz = 0; if (!mouse->present) /* only get here if force == 1 */ return 0; /* start the usb controller's polling of the mouse */ pipe = usb_rcvintpipe(mouse->dev, mouse->bEndpointAddress); FILL_INT_URB(mouse->urb,mouse->dev,pipe, mouse->buffer, usb_maxpacket(mouse->urb->dev, pipe, usb_pipeout(pipe)), mouse_irq,mouse,mouse->bInterval); ret=usb_submit_urb(mouse->urb); // ret = usb_request_irq(mouse->dev, usb_rcvctrlpipe(mouse->dev, mouse->bEndpointAddress), // mouse_irq, mouse->bInterval, // NULL, &mouse->irq_handle); if (ret) { printk (KERN_WARNING "usb-mouse: usb_submit_urb(INT) failed (0x%x)\n", ret); MOD_DEC_USE_COUNT; return ret; } return 0;}static ssize_t write_mouse(struct file * file, const char * buffer, size_t count, loff_t *ppos){ return -EINVAL;}/* * Look like a PS/2 mouse, please.. * In XFree86 (3.3.5 tested) you must select Protocol "NetMousePS/2", * then use your wheel as Button 4 and 5 via ZAxisMapping 4 5. * The PS/2 protocol is fairly strange, but * oh, well, it's at least common.. */static ssize_t read_mouse(struct file * file, char * buffer, size_t count, loff_t *ppos){ DECLARE_WAITQUEUE(wait, current); int retval = 0; static int state = 0; struct mouse_state *mouse = &static_mouse_state; if (!force && !mouse->present) return 0; if (!mouse->ready) { if (file->f_flags & O_NONBLOCK) return -EAGAIN; add_wait_queue(&mouse->wait, &wait);repeat: set_current_state(TASK_INTERRUPTIBLE); if (!mouse->ready && !signal_pending(current)) { schedule(); if (!mouse->present) { if (force) goto repeat; else return 0; } if (mouse->stalled_flag) { int pipe; printk("mouse: Clearing error condition\n"); // usb_unlink_urb(mouse->urb); usb_clear_halt(mouse->dev,usb_rcvintpipe(mouse->dev, mouse->bEndpointAddress)); wait_ms(100); usb_clear_halt(mouse->dev,usb_rcvintpipe(mouse->dev, mouse->bEndpointAddress));#if 0 pipe = usb_rcvintpipe(mouse->dev, mouse->bEndpointAddress); FILL_INT_URB(mouse->urb,mouse->dev,pipe, mouse->buffer, usb_maxpacket(mouse->urb->dev, pipe, usb_pipeout(pipe)), mouse_irq,mouse,mouse->bInterval); mouse->stalled_flag=0; usb_submit_urb(mouse->urb);#endif } goto repeat; } current->state = TASK_RUNNING; remove_wait_queue(&mouse->wait, &wait); } if (signal_pending(current)) return -ERESTARTSYS; if (count) { mouse->ready = 0; switch (state) { case 0: { /* buttons and sign */ int buttons = mouse->buttons; mouse->buttons = 0; if (mouse->dx < 0) buttons |= 0x10; if (mouse->dy < 0) buttons |= 0x20; put_user(buttons, buffer); buffer++; retval++; state = 1; if (!--count) break; } case 1: { /* dx */ int dx = mouse->dx; mouse->dx = 0; put_user(dx, buffer); buffer++; retval++; state = 2; if (!--count) break; } case 2: { /* dy */ int dy = mouse->dy; mouse->dy = 0; put_user(dy, buffer); buffer++; retval++; state = 0; if (!--count) break; } /* * SUBTLE: * * The only way to get here is to do a read() of * more than 3 bytes: if you read a byte at a time * you will just ever see states 0-2, for backwards * compatibility. * * So you can think of this as a packet interface, * where you have arbitrary-sized packets, and you * only ever see the first three bytes when you read * them in small chunks. */ { /* fallthrough - dz */ int dz = mouse->dz; mouse->dz = 0; put_user(dz, buffer); buffer++; retval++; state = 0; } break; } } return retval;}static unsigned int mouse_poll(struct file *file, poll_table * wait){ struct mouse_state *mouse = &static_mouse_state; poll_wait(file, &mouse->wait, wait); if (mouse->ready) return POLLIN | POLLRDNORM; return 0;}struct file_operations usb_mouse_fops = { NULL, /* mouse_seek */ read_mouse, write_mouse, NULL, /* mouse_readdir */ mouse_poll, /* mouse_poll */ NULL, /* mouse_ioctl */ NULL, /* mouse_mmap */ open_mouse, NULL, /* flush */ release_mouse, NULL, fasync_mouse,};void* mouse_probe(struct usb_device *dev,unsigned int ifnum){ struct usb_interface_descriptor *interface; struct usb_endpoint_descriptor *endpoint; struct mouse_state *mouse = &static_mouse_state; int ret; unsigned int pipe; if (mouse->present) { printk("mouse.c: Only one USB-mouse supported\n"); return NULL; } printk("mouse probe for if %i\n",ifnum); /* Is it a mouse interface? */ interface = &dev->actconfig->interface[ifnum].altsetting[0]; if (interface->bInterfaceClass != 3) return NULL; if (interface->bInterfaceSubClass != 1) return NULL; if (interface->bInterfaceProtocol != 2) return NULL; /* Multiple endpoints? What kind of mutant ninja-mouse is this? */ if (interface->bNumEndpoints != 1) return NULL; endpoint = &interface->endpoint[0]; /* Output endpoint? Curiousier and curiousier.. */ if (!(endpoint->bEndpointAddress & 0x80)) return NULL; /* If it's not an interrupt endpoint, we'd better punt! */ if ((endpoint->bmAttributes & 3) != 3) return NULL; printk("USB mouse found\n"); /* these are used to request the irq when the mouse is opened */ mouse->dev = dev; mouse->bEndpointAddress = endpoint->bEndpointAddress; mouse->bInterval = endpoint->bInterval; mouse->present = 1; mouse->stalled_flag=0; /* This appears to let USB mouse survive disconnection and */ /* APM suspend/resume */ usb_clear_halt(mouse->dev,usb_rcvintpipe(mouse->dev, mouse->bEndpointAddress)); if (mouse->suspended) { printk(KERN_DEBUG "%s(%d): mouse resume\n", __FILE__, __LINE__); /* restart the usb controller's polling of the mouse */ pipe = usb_rcvintpipe(mouse->dev, mouse->bEndpointAddress); FILL_INT_URB(mouse->urb,mouse->dev,pipe, mouse->buffer, usb_maxpacket(mouse->urb->dev, pipe, usb_pipeout(pipe)), mouse_irq,mouse,mouse->bInterval); ret=usb_submit_urb(mouse->urb); if (ret) { printk (KERN_WARNING "usb-mouse: usb_submit_urb(INT) failed (0x%x)\n", ret); return NULL; } mouse->suspended = 0; } printk("mouse probe2\n"); return mouse;}static void mouse_disconnect(struct usb_device *dev, void *priv){ struct mouse_state *mouse = priv; /* stop the usb interrupt transfer */ if (mouse->present) { usb_unlink_urb(mouse->urb); if (!force) wake_up(&mouse->wait); else mouse->suspended=1; } /* this might need work */ mouse->present = 0; printk("Mouse disconnected\n");}static struct usb_driver mouse_driver = { "mouse", mouse_probe, mouse_disconnect, { NULL, NULL }, &usb_mouse_fops, 16};int usb_mouse_init(void){ struct mouse_state *mouse = &static_mouse_state; mouse->present = mouse->active = mouse->suspended = 0; mouse->buffer=kmalloc(64,GFP_KERNEL); if (!mouse->buffer) return -ENOMEM; mouse->urb=usb_alloc_urb(0); if (!mouse->urb) printk(KERN_DEBUG MODSTR"URB allocation failed\n"); init_waitqueue_head(&mouse->wait); mouse->fasync = NULL; if (usb_register(&mouse_driver)<0) return -1; printk(KERN_INFO "USB HID boot protocol mouse driver registered.\n"); return 0;}void usb_mouse_cleanup(void){ struct mouse_state *mouse = &static_mouse_state; /* stop the usb interrupt transfer */ if (mouse->present) { usb_unlink_urb(mouse->urb); } kfree(mouse->urb); kfree(mouse->buffer); /* this, too, probably needs work */ usb_deregister(&mouse_driver);}#ifdef MODULEint init_module(void){ return usb_mouse_init();}void cleanup_module(void){ usb_mouse_cleanup();}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -