1048 lines · 24.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * NES, SNES, N64, MultiSystem, PSX gamepad driver for Linux * *  Copyright (c) 1999-2004	Vojtech Pavlik <vojtech@suse.cz> *  Copyright (c) 2004		Peter Nelson <rufus-kernel@hackish.org> * *  Based on the work of: *	Andree Borrmann		John Dahlstrom *	David Kuder		Nathan Hand *	Raphael Assenat */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h>#include <linux/delay.h>#include <linux/module.h>#include <linux/init.h>#include <linux/parport.h>#include <linux/input.h>#include <linux/mutex.h>#include <linux/slab.h> MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");MODULE_DESCRIPTION("NES, SNES, N64, MultiSystem, PSX gamepad driver");MODULE_LICENSE("GPL"); #define GC_MAX_PORTS		3#define GC_MAX_DEVICES		5 struct gc_config {	int args[GC_MAX_DEVICES + 1];	unsigned int nargs;}; static struct gc_config gc_cfg[GC_MAX_PORTS]; module_param_array_named(map, gc_cfg[0].args, int, &gc_cfg[0].nargs, 0);MODULE_PARM_DESC(map, "Describes first set of devices (<parport#>,<pad1>,<pad2>,..<pad5>)");module_param_array_named(map2, gc_cfg[1].args, int, &gc_cfg[1].nargs, 0);MODULE_PARM_DESC(map2, "Describes second set of devices");module_param_array_named(map3, gc_cfg[2].args, int, &gc_cfg[2].nargs, 0);MODULE_PARM_DESC(map3, "Describes third set of devices"); /* see also gs_psx_delay parameter in PSX support section */ enum gc_type {	GC_NONE = 0,	GC_SNES,	GC_NES,	GC_NES4,	GC_MULTI,	GC_MULTI2,	GC_N64,	GC_PSX,	GC_DDR,	GC_SNESMOUSE,	GC_MAX}; #define GC_REFRESH_TIME	HZ/100 struct gc_pad {	struct input_dev *dev;	enum gc_type type;	char phys[32];}; struct gc {	struct pardevice *pd;	struct gc_pad pads[GC_MAX_DEVICES];	struct timer_list timer;	int pad_count[GC_MAX];	int used;	int parportno;	struct mutex mutex;}; struct gc_subdev {	unsigned int idx;}; static struct gc *gc_base[3]; static const int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 }; static const char *gc_names[] = {	NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick",	"Multisystem 2-button joystick", "N64 controller", "PSX controller",	"PSX DDR controller", "SNES mouse"}; /* * N64 support. */ static const unsigned char gc_n64_bytes[] = { 0, 1, 13, 15, 14, 12, 10, 11, 2, 3 };static const short gc_n64_btn[] = {	BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z,	BTN_TL, BTN_TR, BTN_TRIGGER, BTN_START}; #define GC_N64_LENGTH		32		/* N64 bit length, not including stop bit */#define GC_N64_STOP_LENGTH	5		/* Length of encoded stop bit */#define GC_N64_CMD_00		0x11111111UL#define GC_N64_CMD_01		0xd1111111UL#define GC_N64_CMD_03		0xdd111111UL#define GC_N64_CMD_1b		0xdd1dd111UL#define GC_N64_CMD_c0		0x111111ddUL#define GC_N64_CMD_80		0x1111111dUL#define GC_N64_STOP_BIT		0x1d		/* Encoded stop bit */#define GC_N64_REQUEST_DATA	GC_N64_CMD_01	/* the request data command */#define GC_N64_DELAY		133		/* delay between transmit request, and response ready (us) */#define GC_N64_DWS		3		/* delay between write segments (required for sound playback because of ISA DMA) */						/* GC_N64_DWS > 24 is known to fail */#define GC_N64_POWER_W		0xe2		/* power during write (transmit request) */#define GC_N64_POWER_R		0xfd		/* power during read */#define GC_N64_OUT		0x1d		/* output bits to the 4 pads */						/* Reading the main axes of any N64 pad is known to fail if the corresponding bit */						/* in GC_N64_OUT is pulled low on the output port (by any routine) for more */						/* than 123 us */#define GC_N64_CLOCK		0x02		/* clock bits for read */ /* * Used for rumble code. */ /* Send encoded command */static void gc_n64_send_command(struct gc *gc, unsigned long cmd,				unsigned char target){	struct parport *port = gc->pd->port;	int i; 	for (i = 0; i < GC_N64_LENGTH; i++) {		unsigned char data = (cmd >> i) & 1 ? target : 0;		parport_write_data(port, GC_N64_POWER_W | data);		udelay(GC_N64_DWS);	}} /* Send stop bit */static void gc_n64_send_stop_bit(struct gc *gc, unsigned char target){	struct parport *port = gc->pd->port;	int i; 	for (i = 0; i < GC_N64_STOP_LENGTH; i++) {		unsigned char data = (GC_N64_STOP_BIT >> i) & 1 ? target : 0;		parport_write_data(port, GC_N64_POWER_W | data);		udelay(GC_N64_DWS);	}} /* * gc_n64_read_packet() reads an N64 packet. * Each pad uses one bit per byte. So all pads connected to this port * are read in parallel. */ static void gc_n64_read_packet(struct gc *gc, unsigned char *data){	int i;	unsigned long flags; /* * Request the pad to transmit data */ 	local_irq_save(flags);	gc_n64_send_command(gc, GC_N64_REQUEST_DATA, GC_N64_OUT);	gc_n64_send_stop_bit(gc, GC_N64_OUT);	local_irq_restore(flags); /* * Wait for the pad response to be loaded into the 33-bit register * of the adapter. */ 	udelay(GC_N64_DELAY); /* * Grab data (ignoring the last bit, which is a stop bit) */ 	for (i = 0; i < GC_N64_LENGTH; i++) {		parport_write_data(gc->pd->port, GC_N64_POWER_R);		udelay(2);		data[i] = parport_read_status(gc->pd->port);		parport_write_data(gc->pd->port, GC_N64_POWER_R | GC_N64_CLOCK);	 } /* * We must wait 200 ms here for the controller to reinitialize before * the next read request. No worries as long as gc_read is polled less * frequently than this. */ } static void gc_n64_process_packet(struct gc *gc){	unsigned char data[GC_N64_LENGTH];	struct input_dev *dev;	int i, j, s;	signed char x, y; 	gc_n64_read_packet(gc, data); 	for (i = 0; i < GC_MAX_DEVICES; i++) { 		if (gc->pads[i].type != GC_N64)			continue; 		dev = gc->pads[i].dev;		s = gc_status_bit[i]; 		if (s & ~(data[8] | data[9])) { 			x = y = 0; 			for (j = 0; j < 8; j++) {				if (data[23 - j] & s)					x |= 1 << j;				if (data[31 - j] & s)					y |= 1 << j;			} 			input_report_abs(dev, ABS_X,  x);			input_report_abs(dev, ABS_Y, -y); 			input_report_abs(dev, ABS_HAT0X,					 !(s & data[6]) - !(s & data[7]));			input_report_abs(dev, ABS_HAT0Y,					 !(s & data[4]) - !(s & data[5])); 			for (j = 0; j < 10; j++)				input_report_key(dev, gc_n64_btn[j],						 s & data[gc_n64_bytes[j]]); 			input_sync(dev);		}	}} static int gc_n64_play_effect(struct input_dev *dev, void *data,			      struct ff_effect *effect){	int i;	unsigned long flags;	struct gc *gc = input_get_drvdata(dev);	struct gc_subdev *sdev = data;	unsigned char target = 1 << sdev->idx; /* select desired pin */ 	if (effect->type == FF_RUMBLE) {		struct ff_rumble_effect *rumble = &effect->u.rumble;		unsigned int cmd =			rumble->strong_magnitude || rumble->weak_magnitude ?			GC_N64_CMD_01 : GC_N64_CMD_00; 		local_irq_save(flags); 		/* Init Rumble - 0x03, 0x80, 0x01, (34)0x80 */		gc_n64_send_command(gc, GC_N64_CMD_03, target);		gc_n64_send_command(gc, GC_N64_CMD_80, target);		gc_n64_send_command(gc, GC_N64_CMD_01, target);		for (i = 0; i < 32; i++)			gc_n64_send_command(gc, GC_N64_CMD_80, target);		gc_n64_send_stop_bit(gc, target); 		udelay(GC_N64_DELAY); 		/* Now start or stop it - 0x03, 0xc0, 0zx1b, (32)0x01/0x00 */		gc_n64_send_command(gc, GC_N64_CMD_03, target);		gc_n64_send_command(gc, GC_N64_CMD_c0, target);		gc_n64_send_command(gc, GC_N64_CMD_1b, target);		for (i = 0; i < 32; i++)			gc_n64_send_command(gc, cmd, target);		gc_n64_send_stop_bit(gc, target); 		local_irq_restore(flags); 	} 	return 0;} static int gc_n64_init_ff(struct input_dev *dev, int i){	struct gc_subdev *sdev;	int err; 	sdev = kmalloc_obj(*sdev);	if (!sdev)		return -ENOMEM; 	sdev->idx = i; 	input_set_capability(dev, EV_FF, FF_RUMBLE); 	err = input_ff_create_memless(dev, sdev, gc_n64_play_effect);	if (err) {		kfree(sdev);		return err;	} 	return 0;} /* * NES/SNES support. */ #define GC_NES_DELAY		6	/* Delay between bits - 6us */#define GC_NES_LENGTH		8	/* The NES pads use 8 bits of data */#define GC_SNES_LENGTH		12	/* The SNES true length is 16, but the					   last 4 bits are unused */#define GC_SNESMOUSE_LENGTH	32	/* The SNES mouse uses 32 bits, the first					   16 bits are equivalent to a gamepad */ #define GC_NES_POWER	0xfc#define GC_NES_CLOCK	0x01#define GC_NES_LATCH	0x02 static const unsigned char gc_nes_bytes[] = { 0, 1, 2, 3 };static const unsigned char gc_snes_bytes[] = { 8, 0, 2, 3, 9, 1, 10, 11 };static const short gc_snes_btn[] = {	BTN_A, BTN_B, BTN_SELECT, BTN_START, BTN_X, BTN_Y, BTN_TL, BTN_TR}; /* * gc_nes_read_packet() reads a NES/SNES packet. * Each pad uses one bit per byte. So all pads connected to * this port are read in parallel. */ static void gc_nes_read_packet(struct gc *gc, int length, unsigned char *data){	int i; 	parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK | GC_NES_LATCH);	udelay(GC_NES_DELAY * 2);	parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK); 	for (i = 0; i < length; i++) {		udelay(GC_NES_DELAY);		parport_write_data(gc->pd->port, GC_NES_POWER);		data[i] = parport_read_status(gc->pd->port) ^ 0x7f;		udelay(GC_NES_DELAY);		parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK);	}} static void gc_nes_process_packet(struct gc *gc){	unsigned char data[GC_SNESMOUSE_LENGTH];	struct gc_pad *pad;	struct input_dev *dev;	int i, j, s, len;	char x_rel, y_rel; 	len = gc->pad_count[GC_SNESMOUSE] ? GC_SNESMOUSE_LENGTH :			(gc->pad_count[GC_SNES] ? GC_SNES_LENGTH : GC_NES_LENGTH); 	gc_nes_read_packet(gc, len, data); 	for (i = 0; i < GC_MAX_DEVICES; i++) { 		pad = &gc->pads[i];		dev = pad->dev;		s = gc_status_bit[i]; 		switch (pad->type) { 		case GC_NES: 			input_report_abs(dev, ABS_X, !(s & data[6]) - !(s & data[7]));			input_report_abs(dev, ABS_Y, !(s & data[4]) - !(s & data[5])); 			for (j = 0; j < 4; j++)				input_report_key(dev, gc_snes_btn[j],						 s & data[gc_nes_bytes[j]]);			input_sync(dev);			break; 		case GC_SNES: 			input_report_abs(dev, ABS_X, !(s & data[6]) - !(s & data[7]));			input_report_abs(dev, ABS_Y, !(s & data[4]) - !(s & data[5])); 			for (j = 0; j < 8; j++)				input_report_key(dev, gc_snes_btn[j],						 s & data[gc_snes_bytes[j]]);			input_sync(dev);			break; 		case GC_SNESMOUSE:			/*			 * The 4 unused bits from SNES controllers appear			 * to be ID bits so use them to make sure we are			 * dealing with a mouse.			 * gamepad is connected. This is important since			 * my SNES gamepad sends 1's for bits 16-31, which			 * cause the mouse pointer to quickly move to the			 * upper left corner of the screen.			 */			if (!(s & data[12]) && !(s & data[13]) &&			    !(s & data[14]) && (s & data[15])) {				input_report_key(dev, BTN_LEFT, s & data[9]);				input_report_key(dev, BTN_RIGHT, s & data[8]); 				x_rel = y_rel = 0;				for (j = 0; j < 7; j++) {					x_rel <<= 1;					if (data[25 + j] & s)						x_rel |= 1; 					y_rel <<= 1;					if (data[17 + j] & s)						y_rel |= 1;				} 				if (x_rel) {					if (data[24] & s)						x_rel = -x_rel;					input_report_rel(dev, REL_X, x_rel);				} 				if (y_rel) {					if (data[16] & s)						y_rel = -y_rel;					input_report_rel(dev, REL_Y, y_rel);				} 				input_sync(dev);			}			break; 		default:			break;		}	}} /* * Multisystem joystick support */ #define GC_MULTI_LENGTH		5	/* Multi system joystick packet length is 5 */#define GC_MULTI2_LENGTH	6	/* One more bit for one more button */ /* * gc_multi_read_packet() reads a Multisystem joystick packet. */ static void gc_multi_read_packet(struct gc *gc, int length, unsigned char *data){	int i; 	for (i = 0; i < length; i++) {		parport_write_data(gc->pd->port, ~(1 << i));		data[i] = parport_read_status(gc->pd->port) ^ 0x7f;	}} static void gc_multi_process_packet(struct gc *gc){	unsigned char data[GC_MULTI2_LENGTH];	int data_len = gc->pad_count[GC_MULTI2] ? GC_MULTI2_LENGTH : GC_MULTI_LENGTH;	struct gc_pad *pad;	struct input_dev *dev;	int i, s; 	gc_multi_read_packet(gc, data_len, data); 	for (i = 0; i < GC_MAX_DEVICES; i++) {		pad = &gc->pads[i];		dev = pad->dev;		s = gc_status_bit[i]; 		switch (pad->type) {		case GC_MULTI2:			input_report_key(dev, BTN_THUMB, s & data[5]);			fallthrough; 		case GC_MULTI:			input_report_abs(dev, ABS_X,					 !(s & data[2]) - !(s & data[3]));			input_report_abs(dev, ABS_Y,					 !(s & data[0]) - !(s & data[1]));			input_report_key(dev, BTN_TRIGGER, s & data[4]);			input_sync(dev);			break; 		default:			break;		}	}}

Lines 1–500 of 1048.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t