162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 1998-2001 Vojtech Pavlik 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Based on the work of: 662306a36Sopenharmony_ci * Steffen Schwenke 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * TurboGraFX parallel port interface driver for Linux. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/parport.h> 1562306a36Sopenharmony_ci#include <linux/input.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/init.h> 1862306a36Sopenharmony_ci#include <linux/mutex.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ciMODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); 2262306a36Sopenharmony_ciMODULE_DESCRIPTION("TurboGraFX parallel port interface driver"); 2362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define TGFX_MAX_PORTS 3 2662306a36Sopenharmony_ci#define TGFX_MAX_DEVICES 7 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct tgfx_config { 2962306a36Sopenharmony_ci int args[TGFX_MAX_DEVICES + 1]; 3062306a36Sopenharmony_ci unsigned int nargs; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic struct tgfx_config tgfx_cfg[TGFX_MAX_PORTS]; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cimodule_param_array_named(map, tgfx_cfg[0].args, int, &tgfx_cfg[0].nargs, 0); 3662306a36Sopenharmony_ciMODULE_PARM_DESC(map, "Describes first set of devices (<parport#>,<js1>,<js2>,..<js7>"); 3762306a36Sopenharmony_cimodule_param_array_named(map2, tgfx_cfg[1].args, int, &tgfx_cfg[1].nargs, 0); 3862306a36Sopenharmony_ciMODULE_PARM_DESC(map2, "Describes second set of devices"); 3962306a36Sopenharmony_cimodule_param_array_named(map3, tgfx_cfg[2].args, int, &tgfx_cfg[2].nargs, 0); 4062306a36Sopenharmony_ciMODULE_PARM_DESC(map3, "Describes third set of devices"); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define TGFX_REFRESH_TIME HZ/100 /* 10 ms */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define TGFX_TRIGGER 0x08 4562306a36Sopenharmony_ci#define TGFX_UP 0x10 4662306a36Sopenharmony_ci#define TGFX_DOWN 0x20 4762306a36Sopenharmony_ci#define TGFX_LEFT 0x40 4862306a36Sopenharmony_ci#define TGFX_RIGHT 0x80 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define TGFX_THUMB 0x02 5162306a36Sopenharmony_ci#define TGFX_THUMB2 0x04 5262306a36Sopenharmony_ci#define TGFX_TOP 0x01 5362306a36Sopenharmony_ci#define TGFX_TOP2 0x08 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int tgfx_buttons[] = { BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2 }; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic struct tgfx { 5862306a36Sopenharmony_ci struct pardevice *pd; 5962306a36Sopenharmony_ci struct timer_list timer; 6062306a36Sopenharmony_ci struct input_dev *dev[TGFX_MAX_DEVICES]; 6162306a36Sopenharmony_ci char name[TGFX_MAX_DEVICES][64]; 6262306a36Sopenharmony_ci char phys[TGFX_MAX_DEVICES][32]; 6362306a36Sopenharmony_ci int sticks; 6462306a36Sopenharmony_ci int used; 6562306a36Sopenharmony_ci int parportno; 6662306a36Sopenharmony_ci struct mutex sem; 6762306a36Sopenharmony_ci} *tgfx_base[TGFX_MAX_PORTS]; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * tgfx_timer() reads and analyzes TurboGraFX joystick data. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void tgfx_timer(struct timer_list *t) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct tgfx *tgfx = from_timer(tgfx, t, timer); 7662306a36Sopenharmony_ci struct input_dev *dev; 7762306a36Sopenharmony_ci int data1, data2, i; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci for (i = 0; i < 7; i++) 8062306a36Sopenharmony_ci if (tgfx->sticks & (1 << i)) { 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci dev = tgfx->dev[i]; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci parport_write_data(tgfx->pd->port, ~(1 << i)); 8562306a36Sopenharmony_ci data1 = parport_read_status(tgfx->pd->port) ^ 0x7f; 8662306a36Sopenharmony_ci data2 = parport_read_control(tgfx->pd->port) ^ 0x04; /* CAVEAT parport */ 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci input_report_abs(dev, ABS_X, !!(data1 & TGFX_RIGHT) - !!(data1 & TGFX_LEFT)); 8962306a36Sopenharmony_ci input_report_abs(dev, ABS_Y, !!(data1 & TGFX_DOWN ) - !!(data1 & TGFX_UP )); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci input_report_key(dev, BTN_TRIGGER, (data1 & TGFX_TRIGGER)); 9262306a36Sopenharmony_ci input_report_key(dev, BTN_THUMB, (data2 & TGFX_THUMB )); 9362306a36Sopenharmony_ci input_report_key(dev, BTN_THUMB2, (data2 & TGFX_THUMB2 )); 9462306a36Sopenharmony_ci input_report_key(dev, BTN_TOP, (data2 & TGFX_TOP )); 9562306a36Sopenharmony_ci input_report_key(dev, BTN_TOP2, (data2 & TGFX_TOP2 )); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci input_sync(dev); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int tgfx_open(struct input_dev *dev) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct tgfx *tgfx = input_get_drvdata(dev); 10662306a36Sopenharmony_ci int err; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci err = mutex_lock_interruptible(&tgfx->sem); 10962306a36Sopenharmony_ci if (err) 11062306a36Sopenharmony_ci return err; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (!tgfx->used++) { 11362306a36Sopenharmony_ci parport_claim(tgfx->pd); 11462306a36Sopenharmony_ci parport_write_control(tgfx->pd->port, 0x04); 11562306a36Sopenharmony_ci mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME); 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci mutex_unlock(&tgfx->sem); 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void tgfx_close(struct input_dev *dev) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct tgfx *tgfx = input_get_drvdata(dev); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci mutex_lock(&tgfx->sem); 12762306a36Sopenharmony_ci if (!--tgfx->used) { 12862306a36Sopenharmony_ci del_timer_sync(&tgfx->timer); 12962306a36Sopenharmony_ci parport_write_control(tgfx->pd->port, 0x00); 13062306a36Sopenharmony_ci parport_release(tgfx->pd); 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci mutex_unlock(&tgfx->sem); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* 13862306a36Sopenharmony_ci * tgfx_probe() probes for tg gamepads. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void tgfx_attach(struct parport *pp) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct tgfx *tgfx; 14462306a36Sopenharmony_ci struct input_dev *input_dev; 14562306a36Sopenharmony_ci struct pardevice *pd; 14662306a36Sopenharmony_ci int i, j, port_idx; 14762306a36Sopenharmony_ci int *n_buttons, n_devs; 14862306a36Sopenharmony_ci struct pardev_cb tgfx_parport_cb; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci for (port_idx = 0; port_idx < TGFX_MAX_PORTS; port_idx++) { 15162306a36Sopenharmony_ci if (tgfx_cfg[port_idx].nargs == 0 || 15262306a36Sopenharmony_ci tgfx_cfg[port_idx].args[0] < 0) 15362306a36Sopenharmony_ci continue; 15462306a36Sopenharmony_ci if (tgfx_cfg[port_idx].args[0] == pp->number) 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (port_idx == TGFX_MAX_PORTS) { 15962306a36Sopenharmony_ci pr_debug("Not using parport%d.\n", pp->number); 16062306a36Sopenharmony_ci return; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci n_buttons = tgfx_cfg[port_idx].args + 1; 16362306a36Sopenharmony_ci n_devs = tgfx_cfg[port_idx].nargs - 1; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci memset(&tgfx_parport_cb, 0, sizeof(tgfx_parport_cb)); 16662306a36Sopenharmony_ci tgfx_parport_cb.flags = PARPORT_FLAG_EXCL; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci pd = parport_register_dev_model(pp, "turbografx", &tgfx_parport_cb, 16962306a36Sopenharmony_ci port_idx); 17062306a36Sopenharmony_ci if (!pd) { 17162306a36Sopenharmony_ci pr_err("parport busy already - lp.o loaded?\n"); 17262306a36Sopenharmony_ci return; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci tgfx = kzalloc(sizeof(struct tgfx), GFP_KERNEL); 17662306a36Sopenharmony_ci if (!tgfx) { 17762306a36Sopenharmony_ci printk(KERN_ERR "turbografx.c: Not enough memory\n"); 17862306a36Sopenharmony_ci goto err_unreg_pardev; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci mutex_init(&tgfx->sem); 18262306a36Sopenharmony_ci tgfx->pd = pd; 18362306a36Sopenharmony_ci tgfx->parportno = pp->number; 18462306a36Sopenharmony_ci timer_setup(&tgfx->timer, tgfx_timer, 0); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci for (i = 0; i < n_devs; i++) { 18762306a36Sopenharmony_ci if (n_buttons[i] < 1) 18862306a36Sopenharmony_ci continue; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (n_buttons[i] > ARRAY_SIZE(tgfx_buttons)) { 19162306a36Sopenharmony_ci printk(KERN_ERR "turbografx.c: Invalid number of buttons %d\n", n_buttons[i]); 19262306a36Sopenharmony_ci goto err_unreg_devs; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci tgfx->dev[i] = input_dev = input_allocate_device(); 19662306a36Sopenharmony_ci if (!input_dev) { 19762306a36Sopenharmony_ci printk(KERN_ERR "turbografx.c: Not enough memory for input device\n"); 19862306a36Sopenharmony_ci goto err_unreg_devs; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci tgfx->sticks |= (1 << i); 20262306a36Sopenharmony_ci snprintf(tgfx->name[i], sizeof(tgfx->name[i]), 20362306a36Sopenharmony_ci "TurboGraFX %d-button Multisystem joystick", n_buttons[i]); 20462306a36Sopenharmony_ci snprintf(tgfx->phys[i], sizeof(tgfx->phys[i]), 20562306a36Sopenharmony_ci "%s/input%d", tgfx->pd->port->name, i); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci input_dev->name = tgfx->name[i]; 20862306a36Sopenharmony_ci input_dev->phys = tgfx->phys[i]; 20962306a36Sopenharmony_ci input_dev->id.bustype = BUS_PARPORT; 21062306a36Sopenharmony_ci input_dev->id.vendor = 0x0003; 21162306a36Sopenharmony_ci input_dev->id.product = n_buttons[i]; 21262306a36Sopenharmony_ci input_dev->id.version = 0x0100; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci input_set_drvdata(input_dev, tgfx); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci input_dev->open = tgfx_open; 21762306a36Sopenharmony_ci input_dev->close = tgfx_close; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 22062306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, -1, 1, 0, 0); 22162306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, -1, 1, 0, 0); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci for (j = 0; j < n_buttons[i]; j++) 22462306a36Sopenharmony_ci set_bit(tgfx_buttons[j], input_dev->keybit); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (input_register_device(tgfx->dev[i])) 22762306a36Sopenharmony_ci goto err_free_dev; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (!tgfx->sticks) { 23162306a36Sopenharmony_ci printk(KERN_ERR "turbografx.c: No valid devices specified\n"); 23262306a36Sopenharmony_ci goto err_free_tgfx; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci tgfx_base[port_idx] = tgfx; 23662306a36Sopenharmony_ci return; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci err_free_dev: 23962306a36Sopenharmony_ci input_free_device(tgfx->dev[i]); 24062306a36Sopenharmony_ci err_unreg_devs: 24162306a36Sopenharmony_ci while (--i >= 0) 24262306a36Sopenharmony_ci if (tgfx->dev[i]) 24362306a36Sopenharmony_ci input_unregister_device(tgfx->dev[i]); 24462306a36Sopenharmony_ci err_free_tgfx: 24562306a36Sopenharmony_ci kfree(tgfx); 24662306a36Sopenharmony_ci err_unreg_pardev: 24762306a36Sopenharmony_ci parport_unregister_device(pd); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void tgfx_detach(struct parport *port) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci int i; 25362306a36Sopenharmony_ci struct tgfx *tgfx; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci for (i = 0; i < TGFX_MAX_PORTS; i++) { 25662306a36Sopenharmony_ci if (tgfx_base[i] && tgfx_base[i]->parportno == port->number) 25762306a36Sopenharmony_ci break; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (i == TGFX_MAX_PORTS) 26162306a36Sopenharmony_ci return; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci tgfx = tgfx_base[i]; 26462306a36Sopenharmony_ci tgfx_base[i] = NULL; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci for (i = 0; i < TGFX_MAX_DEVICES; i++) 26762306a36Sopenharmony_ci if (tgfx->dev[i]) 26862306a36Sopenharmony_ci input_unregister_device(tgfx->dev[i]); 26962306a36Sopenharmony_ci parport_unregister_device(tgfx->pd); 27062306a36Sopenharmony_ci kfree(tgfx); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic struct parport_driver tgfx_parport_driver = { 27462306a36Sopenharmony_ci .name = "turbografx", 27562306a36Sopenharmony_ci .match_port = tgfx_attach, 27662306a36Sopenharmony_ci .detach = tgfx_detach, 27762306a36Sopenharmony_ci .devmodel = true, 27862306a36Sopenharmony_ci}; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int __init tgfx_init(void) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci int i; 28362306a36Sopenharmony_ci int have_dev = 0; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci for (i = 0; i < TGFX_MAX_PORTS; i++) { 28662306a36Sopenharmony_ci if (tgfx_cfg[i].nargs == 0 || tgfx_cfg[i].args[0] < 0) 28762306a36Sopenharmony_ci continue; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (tgfx_cfg[i].nargs < 2) { 29062306a36Sopenharmony_ci printk(KERN_ERR "turbografx.c: at least one joystick must be specified\n"); 29162306a36Sopenharmony_ci return -EINVAL; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci have_dev = 1; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (!have_dev) 29862306a36Sopenharmony_ci return -ENODEV; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return parport_register_driver(&tgfx_parport_driver); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic void __exit tgfx_exit(void) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci parport_unregister_driver(&tgfx_parport_driver); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cimodule_init(tgfx_init); 30962306a36Sopenharmony_cimodule_exit(tgfx_exit); 310