📄 joystick.c
字号:
/* * joystick.c Version 1.2 * * Copyright (c) 1996-1998 Vojtech Pavlik *//* * This is the main joystick driver for Linux. It doesn't support any * devices directly, rather is lets you use sub-modules to do that job. See * Documentation/joystick.txt for more info. *//* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */#include <asm/io.h>#include <asm/system.h>#include <asm/segment.h>#include <linux/config.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/joystick.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/malloc.h>#include <linux/mm.h>#include <linux/module.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)#include <asm/spinlock.h>#include <linux/poll.h>#endif/* * Configurable parameters. */#define JS_REFRESH_TIME HZ/50 /* Time between two reads of joysticks (20ms) *//* * Buffer macros. */#define ROT(A,B,C) ((((A)<(C))&&(((B)>(A))&&((B)<(C))))||(((A)>(C))&&(((B)>(A))||((B)<(C)))))#define GOF(X) (((X)==JS_BUFF_SIZE-1)?0:(X)+1)#define GOB(X) ((X)?(X)-1:JS_BUFF_SIZE-1)#define DIFF(X,Y) ((X)>(Y)?(X)-(Y):(Y)-(X))/* * Global variables. */static struct JS_DATA_SAVE_TYPE js_comp_glue;static struct js_port *js_port = NULL;static struct js_dev *js_dev = NULL;static struct timer_list js_timer;spinlock_t js_lock = SPIN_LOCK_UNLOCKED;static int js_use_count = 0;/* * Exported variables. */unsigned int js_time_speed = 0;js_time_func js_get_time;js_delta_func js_delta;unsigned int js_time_speed_a = 0;js_time_func js_get_time_a;js_delta_func js_delta_a;/* * Module info. */MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");MODULE_SUPPORTED_DEVICE("js");/* * js_get_time_*() are different functions to get current time. * js_delta_*() are functions to compute time difference. */#ifdef __i386__static unsigned int js_get_time_rdtsc(void){ unsigned int x; __asm__ __volatile__ ( "rdtsc" : "=A" (x) ); return x;}static unsigned int js_get_time_pit(void){ unsigned long flags; unsigned int x; __save_flags(flags); __cli(); outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; __restore_flags(flags); return x;}static int js_delta_pit(unsigned int x, unsigned int y){ return y - x + ( y < x ? 1193180L / HZ : 0 );}static unsigned int js_get_time_counter(void){ static int time_counter = 0; return time_counter++;}#else#ifdef __alpha__static unsigned int js_get_time_rpcc(void){ unsigned int x; __asm__ __volatile__ ( "rpcc %0" : "=r" (x) ); return x;}#else#ifndef MODULE#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)static unsigned int js_get_time_system(void){ static struct timeval js_tv; get_fast_time(&js_tv); return js_tv.tv_sec * 1000000L + js_tv.tv_usec;}#endif#endif#endif#endifstatic int js_delta_normal(unsigned int x, unsigned int y){ return x - y;}/* * js_calibrate_time() calibrates a given timer. */static int __init js_calibrate_time(js_time_func get_time, js_delta_func delta){ unsigned int t1, t2, t3; unsigned long flags; __save_flags(flags); __cli(); t1 = get_time(); udelay(1000); t2 = get_time(); t3 = get_time(); __restore_flags(flags); return delta(t2, t1) - delta(t3, t2);}/* * js_calibrate_time_counter() calibrates the counter timer, which can't * be calibrated using the above function. */#ifdef __i386__static int __init js_calibrate_time_counter(void){ unsigned int i, j, t1, t2, t3; j = jiffies; do { inb(0x201); t1 = js_get_time_counter(); } while (j == jiffies); j = jiffies; do { inb(0x201); t2 = js_get_time_counter(); } while (j == jiffies); j = (t2 - t1) * HZ / 1000; t1 = js_get_time_pit(); for (i = 0; i < 1000; i++) { inb(0x201); js_get_time_counter(); } t2 = js_get_time_pit(); t3 = js_get_time_pit(); i = 1193180L / (js_delta_pit(t2, t1) - js_delta_pit(t3, t2)); if (DIFF(i,j) > 5) printk(KERN_WARNING "js: Counter timer calibration unsure," " pass1 (0.%d MHz) and pass2 (0.%d MHz) differ.\n", j, i); return (i + j) >> 1;}#endif/* * js_setup_time chooses the best available timers * on the system and calibrates them. */static int __init js_setup_time(void){ int t; char *name, *name_a; name = ""; name_a = ""; js_time_speed = 0; js_time_speed_a = 0;#ifdef __i386__ t = js_calibrate_time(js_get_time_pit, js_delta_pit); if (DIFF(t, 1193) > 5) printk(KERN_WARNING "js: Measured PIT speed is %d.%03d MHz, but should be 1.193 MHz.\n" KERN_WARNING "js: This is probably caused by wrong BogoMIPS value. It is: %ld, should be: %ld.\n", t / 1000, t % 1000, loops_per_sec / 500000, loops_per_sec / (t * 500000 / 1193)); if (JS_HAS_RDTSC && (t = js_calibrate_time(js_get_time_rdtsc, js_delta_normal)) > 0) { js_time_speed_a = t; js_get_time_a = js_get_time_rdtsc; js_delta_a = js_delta_normal; js_time_speed = t; js_get_time = js_get_time_rdtsc; js_delta = js_delta_normal; name = "RDTSC"; } else { js_time_speed_a = t; js_get_time_a = js_get_time_pit; js_delta_a = js_delta_pit; name_a = "PIT"; t = js_calibrate_time_counter(); js_time_speed = t; js_get_time = js_get_time_counter; js_delta = js_delta_normal; name = "counter"; }#else#ifdef __alpha__ t = js_calibrate_time(js_get_time_rpcc, js_delta_normal); js_time_speed_a = t; js_get_time_a = js_get_time_rpcc; js_delta_a = js_delta_normal; js_time_speed = t; js_get_time = js_get_time_rpcc; js_delta = js_delta_normal; name = "RPCC";#else#ifndef MODULE#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) t = js_calibrate_time(js_get_time_system, js_delta_normal); js_time_speed_a = t; js_get_time_a = js_get_time_system; js_delta_a = js_delta_normal; js_time_speed = t; js_get_time = js_get_time_system; js_delta = js_delta_normal; name = "system";#endif#endif#endif#endif printk(KERN_INFO "js: Version %d.%d.%d ", JS_VERSION >> 16 & 0xff, JS_VERSION >> 8 & 0xff, JS_VERSION & 0xff); if (js_time_speed_a <= 0 || js_time_speed <= 0) { printk("\n"); return -1; } printk("using "); if (js_time_speed > 10000) { t = js_time_speed / 1000 + (js_time_speed % 1000 >= 500); printk("%d MHz ", t); } else { t = js_time_speed / 10 + (js_time_speed % 10 >= 5); printk("%d.%02d MHz ", t / 100, t % 100); } if (js_get_time_a != js_get_time) { t = js_time_speed_a / 10 + (js_time_speed_a % 10 >= 5); printk("%s timer and %d.%02d MHz %s timer.\n", name, t / 100, t % 100, name_a); } else { printk("%s timer.\n", name); } return 0;}/* * js_correct() performs correction of raw joystick data. */static int js_correct(int value, struct js_corr *corr){ switch (corr->type) { case JS_CORR_NONE: break; case JS_CORR_BROKEN: value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : ((corr->coef[3] * (value - corr->coef[1])) >> 14)) : ((corr->coef[2] * (value - corr->coef[0])) >> 14); break; default: return 0; } if (value < -32767) return -32767; if (value > 32767) return 32767; return value;}/* * js_button() returns value of button number i. */static inline int js_button(int *buttons, int i){ return (buttons[i >> 5] >> (i & 0x1f)) & 1;}/* * js_add_event() adds an event to the buffer. This requires additional * queue post-processing done by js_sync_buff. */static void js_add_event(struct js_dev *jd, __u32 time, __u8 type, __u8 number, __s16 value){ jd->buff[jd->ahead].time = time; jd->buff[jd->ahead].type = type; jd->buff[jd->ahead].number = number; jd->buff[jd->ahead].value = value; if (++jd->ahead == JS_BUFF_SIZE) jd->ahead = 0;}/* * js_flush_data() does the same as js_process_data, except for that it doesn't * generate any events - it just copies the data from new to cur. */static void js_flush_data(struct js_dev *jd){ int i; for (i = 0; i < ((jd->num_buttons - 1) >> 5) + 1; i++) jd->cur.buttons[i] = jd->new.buttons[i]; for (i = 0; i < jd->num_axes; i++) jd->cur.axes[i] = jd->new.axes[i];}/* * js_process_data() finds changes in button states and axis positions and adds * them as events to the buffer. */static void js_process_data(struct js_dev *jd){ int i, t; for (i = 0; i < jd->num_buttons; i++) if ((t = js_button(jd->new.buttons, i)) != js_button(jd->cur.buttons, i)) { js_add_event(jd, jiffies, JS_EVENT_BUTTON, i, t); jd->cur.buttons[i >> 5] ^= (1 << (i & 0x1f)); } for (i = 0; i < jd->num_axes; i++) { t = js_correct(jd->new.axes[i], &jd->corr[i]); if (((jd->corr[i].prec == -1) && t) || ((DIFF(jd->new.axes[i], jd->cur.axes[i]) > jd->corr[i].prec) && (t != js_correct(jd->cur.axes[i], &jd->corr[i])))) { js_add_event(jd, jiffies, JS_EVENT_AXIS, i, t); jd->cur.axes[i] = jd->new.axes[i]; } }}/* * js_sync_buff() checks for all overflows caused by recent additions to the buffer. * These happen only if some process is reading the data too slowly. It * wakes up any process waiting for data. */static void js_sync_buff(struct js_dev *jd){ struct js_list *curl = jd->list; if (jd->bhead != jd->ahead) { if(ROT(jd->bhead, jd->tail, jd->ahead) || (jd->tail == jd->bhead)) { while (curl) { if (ROT(jd->bhead, curl->tail, jd->ahead) || (curl->tail == jd->bhead)) { curl->tail = jd->ahead; curl->startup = 0; } curl = curl->next; } jd->tail = jd->ahead; } jd->bhead = jd->ahead; wake_up_interruptible(&jd->wait); }}/* * js_do_timer() acts as an interrupt replacement. It reads the data * from all ports and then generates events for all devices. */static void js_do_timer(unsigned long data){ struct js_port *curp = js_port; struct js_dev *curd = js_dev; unsigned long flags; while (curp != NULL) { curp->read(curp->info, curp->axes, curp->buttons); curp = curp->next; } spin_lock_irqsave(&js_lock, flags); while (curd != NULL) { if (data) { js_process_data(curd); js_sync_buff(curd); } else { js_flush_data(curd); } curd = curd->next; } spin_unlock_irqrestore(&js_lock, flags); js_timer.expires = jiffies + JS_REFRESH_TIME; add_timer(&js_timer);}/* * js_read() copies one or more entries from jsd[].buff to user * space. */#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos)#elsestatic int js_read(struct inode *inode, struct file *file, char *buf, int count)#endif{ struct wait_queue wait = { current, NULL }; struct js_event *buff = (void *) buf; struct js_list *curl; struct js_dev *jd; unsigned long blocks = count / sizeof(struct js_event); int written = 0; int new_tail, orig_tail; int retval = 0; unsigned long flags; curl = file->private_data; jd = curl->dev; orig_tail = curl->tail;/* * Check user data. */ if (!blocks) return -EINVAL;/* * Lock it. */ spin_lock_irqsave(&js_lock, flags);/* * Handle (non)blocking i/o. */ if (count != sizeof(struct JS_DATA_TYPE)) { if (GOF(curl->tail) == jd->bhead && curl->startup == jd->num_axes + jd->num_buttons) { add_wait_queue(&jd->wait, &wait); current->state = TASK_INTERRUPTIBLE; while (GOF(curl->tail) == jd->bhead) { if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; break; } if (signal_pending(current)) { retval = -ERESTARTSYS; break; } spin_unlock_irqrestore(&js_lock, flags); schedule(); spin_lock_irqsave(&js_lock, flags); } current->state = TASK_RUNNING; remove_wait_queue(&jd->wait, &wait); } if (retval) { spin_unlock_irqrestore(&js_lock, flags); return retval; }/* * Initial state. */ while (curl->startup < jd->num_axes + jd->num_buttons && written < blocks && !retval) { struct js_event tmpevent; if (curl->startup < jd->num_buttons) { tmpevent.type = JS_EVENT_BUTTON | JS_EVENT_INIT; tmpevent.value = js_button(jd->cur.buttons, curl->startup); tmpevent.number = curl->startup; } else { tmpevent.type = JS_EVENT_AXIS | JS_EVENT_INIT; tmpevent.value = js_correct(jd->cur.axes[curl->startup - jd->num_buttons], &jd->corr[curl->startup - jd->num_buttons]); tmpevent.number = curl->startup - jd->num_buttons; } tmpevent.time = jiffies * (1000/HZ);#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) if (copy_to_user(&buff[written], &tmpevent, sizeof(struct js_event))) retval = -EFAULT;#else if (!(retval = verify_area(VERIFY_WRITE, &buff[written], sizeof(struct js_event)))) memcpy_tofs(&buff[written], &tmpevent, sizeof(struct js_event));#endif curl->startup++; written++; }/* * Buffer data. */ while ((jd->bhead != (new_tail = GOF(curl->tail))) && (written < blocks) && !retval) {#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) if (copy_to_user(&buff[written], &jd->buff[new_tail], sizeof(struct js_event))) retval = -EFAULT; if (put_user((__u32)(jd->buff[new_tail].time * (1000/HZ)), &buff[written].time)) retval = -EFAULT;#else if (!(retval = verify_area(VERIFY_WRITE, &buff[written], sizeof(struct js_event)))) { memcpy_tofs(&buff[written], &jd->buff[new_tail], sizeof(struct js_event)); put_user((__u32)(jd->buff[new_tail].time * (1000/HZ)), &buff[written].time); }#endif curl->tail = new_tail; written++; } } else/* * Handle version 0.x compatibility. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -