⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 joystick.c

📁 powerpc内核mpc8241linux系统下char驱动程序
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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 + -