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

📄 sidewinder.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  Copyright (c) 1998-2005 Vojtech Pavlik *//* * Microsoft SideWinder joystick family driver for Linux *//* * 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, Simunkova 1594, Prague 8, 182 00 Czech Republic */#include <linux/delay.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/input.h>#include <linux/gameport.h>#include <linux/jiffies.h>#define DRIVER_DESC	"Microsoft SideWinder joystick family driver"MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");/* * These are really magic values. Changing them can make a problem go away, * as well as break everything. */#undef SW_DEBUG#undef SW_DEBUG_DATA#define SW_START	600	/* The time we wait for the first bit [600 us] */#define SW_STROBE	60	/* Max time per bit [60 us] */#define SW_TIMEOUT	6	/* Wait for everything to settle [6 ms] */#define SW_KICK		45	/* Wait after A0 fall till kick [45 us] */#define SW_END		8	/* Number of bits before end of packet to kick */#define SW_FAIL		16	/* Number of packet read errors to fail and reinitialize */#define SW_BAD		2	/* Number of packet read errors to switch off 3d Pro optimization */#define SW_OK		64	/* Number of packet read successes to switch optimization back on */#define SW_LENGTH	512	/* Max number of bits in a packet */#ifdef SW_DEBUG#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg)#else#define dbg(format, arg...) do {} while (0)#endif/* * SideWinder joystick types ... */#define SW_ID_3DP	0#define SW_ID_GP	1#define SW_ID_PP	2#define SW_ID_FFP	3#define SW_ID_FSP	4#define SW_ID_FFW	5/* * Names, buttons, axes ... */static char *sw_name[] = {	"3D Pro", "GamePad", "Precision Pro", "Force Feedback Pro", "FreeStyle Pro",				"Force Feedback Wheel" };static char sw_abs[][7] = {	{ ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },	{ ABS_X, ABS_Y },	{ ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },	{ ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },	{ ABS_X, ABS_Y,         ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },	{ ABS_RX, ABS_RUDDER,   ABS_THROTTLE }};static char sw_bit[][7] = {	{ 10, 10,  9, 10,  1,  1 },	{  1,  1                 },	{ 10, 10,  6,  7,  1,  1 },	{ 10, 10,  6,  7,  1,  1 },	{ 10, 10,  6,  1,  1     },	{ 10,  7,  7,  1,  1     }};static short sw_btn[][12] = {	{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_MODE },	{ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE },	{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT },	{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT },	{ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT },	{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4 }};static struct {	int x;	int y;} sw_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};struct sw {	struct gameport *gameport;	struct input_dev *dev[4];	char name[64];	char phys[4][32];	int length;	int type;	int bits;	int number;	int fail;	int ok;	int reads;	int bads;};/* * sw_read_packet() is a function which reads either a data packet, or an * identification packet from a SideWinder joystick. The protocol is very, * very, very braindamaged. Microsoft patented it in US patent #5628686. */static int sw_read_packet(struct gameport *gameport, unsigned char *buf, int length, int id){	unsigned long flags;	int timeout, bitout, sched, i, kick, start, strobe;	unsigned char pending, u, v;	i = -id;						/* Don't care about data, only want ID */	timeout = id ? gameport_time(gameport, SW_TIMEOUT * 1000) : 0; /* Set up global timeout for ID packet */	kick = id ? gameport_time(gameport, SW_KICK) : 0;	/* Set up kick timeout for ID packet */	start = gameport_time(gameport, SW_START);	strobe = gameport_time(gameport, SW_STROBE);	bitout = start;	pending = 0;	sched = 0;        local_irq_save(flags);					/* Quiet, please */	gameport_trigger(gameport);				/* Trigger */	v = gameport_read(gameport);	do {		bitout--;		u = v;		v = gameport_read(gameport);	} while (!(~v & u & 0x10) && (bitout > 0));		/* Wait for first falling edge on clock */	if (bitout > 0)		bitout = strobe;				/* Extend time if not timed out */	while ((timeout > 0 || bitout > 0) && (i < length)) {		timeout--;		bitout--;					/* Decrement timers */		sched--;		u = v;		v = gameport_read(gameport);		if ((~u & v & 0x10) && (bitout > 0)) {		/* Rising edge on clock - data bit */			if (i >= 0)				/* Want this data */				buf[i] = v >> 5;		/* Store it */			i++;					/* Advance index */			bitout = strobe;			/* Extend timeout for next bit */		}		if (kick && (~v & u & 0x01)) {			/* Falling edge on axis 0 */			sched = kick;				/* Schedule second trigger */			kick = 0;				/* Don't schedule next time on falling edge */			pending = 1;				/* Mark schedule */		}		if (pending && sched < 0 && (i > -SW_END)) {	/* Second trigger time */			gameport_trigger(gameport);		/* Trigger */			bitout = start;				/* Long bit timeout */			pending = 0;				/* Unmark schedule */			timeout = 0;				/* Switch from global to bit timeouts */		}	}	local_irq_restore(flags);					/* Done - relax */#ifdef SW_DEBUG_DATA	{		int j;		printk(KERN_DEBUG "sidewinder.c: Read %d triplets. [", i);		for (j = 0; j < i; j++) printk("%d", buf[j]);		printk("]\n");	}#endif	return i;}/* * sw_get_bits() and GB() compose bits from the triplet buffer into a __u64. * Parameter 'pos' is bit number inside packet where to start at, 'num' is number * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits * is number of bits per triplet. */#define GB(pos,num) sw_get_bits(buf, pos, num, sw->bits)static __u64 sw_get_bits(unsigned char *buf, int pos, int num, char bits){	__u64 data = 0;	int tri = pos % bits;						/* Start position */	int i   = pos / bits;	int bit = 0;	while (num--) {		data |= (__u64)((buf[i] >> tri++) & 1) << bit++;	/* Transfer bit */		if (tri == bits) {			i++;						/* Next triplet */			tri = 0;		}	}	return data;}/* * sw_init_digital() initializes a SideWinder 3D Pro joystick * into digital mode. */static void sw_init_digital(struct gameport *gameport){	int seq[] = { 140, 140+725, 140+300, 0 };	unsigned long flags;	int i, t;        local_irq_save(flags);	i = 0;        do {                gameport_trigger(gameport);			/* Trigger */		t = gameport_time(gameport, SW_TIMEOUT * 1000);		while ((gameport_read(gameport) & 1) && t) t--;	/* Wait for axis to fall back to 0 */                udelay(seq[i]);					/* Delay magic time */        } while (seq[++i]);	gameport_trigger(gameport);				/* Last trigger */	local_irq_restore(flags);}/* * sw_parity() computes parity of __u64 */static int sw_parity(__u64 t){	int x = t ^ (t >> 32);	x ^= x >> 16;	x ^= x >> 8;	x ^= x >> 4;	x ^= x >> 2;	x ^= x >> 1;	return x & 1;}/* * sw_ccheck() checks synchronization bits and computes checksum of nibbles. */static int sw_check(__u64 t){	unsigned char sum = 0;	if ((t & 0x8080808080808080ULL) ^ 0x80)			/* Sync */		return -1;	while (t) {						/* Sum */		sum += t & 0xf;		t >>= 4;	}	return sum & 0xf;}/* * sw_parse() analyzes SideWinder joystick data, and writes the results into * the axes and buttons arrays. */static int sw_parse(unsigned char *buf, struct sw *sw){	int hat, i, j;	struct input_dev *dev;	switch (sw->type) {		case SW_ID_3DP:			if (sw_check(GB(0,64)) || (hat = (GB(6,1) << 3) | GB(60,3)) > 8)				return -1;			dev = sw->dev[0];			input_report_abs(dev, ABS_X,        (GB( 3,3) << 7) | GB(16,7));			input_report_abs(dev, ABS_Y,        (GB( 0,3) << 7) | GB(24,7));			input_report_abs(dev, ABS_RZ,       (GB(35,2) << 7) | GB(40,7));			input_report_abs(dev, ABS_THROTTLE, (GB(32,3) << 7) | GB(48,7));			input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x);			input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y);			for (j = 0; j < 7; j++)				input_report_key(dev, sw_btn[SW_ID_3DP][j], !GB(j+8,1));			input_report_key(dev, BTN_BASE4, !GB(38,1));			input_report_key(dev, BTN_BASE5, !GB(37,1));			input_sync(dev);			return 0;		case SW_ID_GP:			for (i = 0; i < sw->number; i ++) {				if (sw_parity(GB(i*15,15)))					return -1;				input_report_abs(sw->dev[i], ABS_X, GB(i*15+3,1) - GB(i*15+2,1));				input_report_abs(sw->dev[i], ABS_Y, GB(i*15+0,1) - GB(i*15+1,1));				for (j = 0; j < 10; j++)					input_report_key(sw->dev[i], sw_btn[SW_ID_GP][j], !GB(i*15+j+4,1));				input_sync(sw->dev[i]);			}			return 0;		case SW_ID_PP:		case SW_ID_FFP:			if (!sw_parity(GB(0,48)) || (hat = GB(42,4)) > 8)				return -1;			dev = sw->dev[0];			input_report_abs(dev, ABS_X,        GB( 9,10));			input_report_abs(dev, ABS_Y,        GB(19,10));			input_report_abs(dev, ABS_RZ,       GB(36, 6));			input_report_abs(dev, ABS_THROTTLE, GB(29, 7));			input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x);			input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y);			for (j = 0; j < 9; j++)				input_report_key(dev, sw_btn[SW_ID_PP][j], !GB(j,1));			input_sync(dev);			return 0;		case SW_ID_FSP:			if (!sw_parity(GB(0,43)) || (hat = GB(28,4)) > 8)				return -1;			dev = sw->dev[0];			input_report_abs(dev, ABS_X,        GB( 0,10));			input_report_abs(dev, ABS_Y,        GB(16,10));			input_report_abs(dev, ABS_THROTTLE, GB(32, 6));			input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x);			input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y);			for (j = 0; j < 6; j++)				input_report_key(dev, sw_btn[SW_ID_FSP][j], !GB(j+10,1));			input_report_key(dev, BTN_TR,     !GB(26,1));			input_report_key(dev, BTN_START,  !GB(27,1));			input_report_key(dev, BTN_MODE,   !GB(38,1));			input_report_key(dev, BTN_SELECT, !GB(39,1));			input_sync(dev);			return 0;		case SW_ID_FFW:			if (!sw_parity(GB(0,33)))				return -1;			dev = sw->dev[0];			input_report_abs(dev, ABS_RX,       GB( 0,10));			input_report_abs(dev, ABS_RUDDER,   GB(10, 6));			input_report_abs(dev, ABS_THROTTLE, GB(16, 6));			for (j = 0; j < 8; j++)				input_report_key(dev, sw_btn[SW_ID_FFW][j], !GB(j+22,1));			input_sync(dev);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -