// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
* Based on the work of:
* Andree Borrmann Mats Sjövall
*/
/*
* Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver for Linux
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.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("Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver" );
MODULE_LICENSE("GPL" );
struct db9_config {
int args[2 ];
unsigned int nargs;
};
#define DB9_MAX_PORTS 3
static struct db9_config db9_cfg[DB9_MAX_PORTS];
module_param_array_named(dev, db9_cfg[0 ].args, int , &db9_cfg[0 ].nargs, 0 );
MODULE_PARM_DESC(dev, "Describes first attached device (<parport#>,<type>)" );
module_param_array_named(dev2, db9_cfg[1 ].args, int , &db9_cfg[1 ].nargs, 0 );
MODULE_PARM_DESC(dev2, "Describes second attached device (<parport#>,<type>)" );
module_param_array_named(dev3, db9_cfg[2 ].args, int , &db9_cfg[2 ].nargs, 0 );
MODULE_PARM_DESC(dev3, "Describes third attached device (<parport#>,<type>)" );
#define DB9_ARG_PARPORT 0
#define DB9_ARG_MODE 1
#define DB9_MULTI_STICK 0 x01
#define DB9_MULTI2_STICK 0 x02
#define DB9_GENESIS_PAD 0 x03
#define DB9_GENESIS5_PAD 0 x05
#define DB9_GENESIS6_PAD 0 x06
#define DB9_SATURN_PAD 0 x07
#define DB9_MULTI_0802 0 x08
#define DB9_MULTI_0802_2 0 x09
#define DB9_CD32_PAD 0 x0A
#define DB9_SATURN_DPP 0 x0B
#define DB9_SATURN_DPP_2 0 x0C
#define DB9_MAX_PAD 0 x0D
#define DB9_UP 0 x01
#define DB9_DOWN 0 x02
#define DB9_LEFT 0 x04
#define DB9_RIGHT 0 x08
#define DB9_FIRE1 0 x10
#define DB9_FIRE2 0 x20
#define DB9_FIRE3 0 x40
#define DB9_FIRE4 0 x80
#define DB9_NORMAL 0 x0a
#define DB9_NOSELECT 0 x08
#define DB9_GENESIS6_DELAY 14
#define DB9_REFRESH_TIME HZ/100
#define DB9_MAX_DEVICES 2
struct db9_mode_data {
const char *name;
const short *buttons;
int n_buttons;
int n_pads;
int n_axis;
int bidirectional;
int reverse;
};
struct db9 {
struct input_dev *dev[DB9_MAX_DEVICES];
struct timer_list timer;
struct pardevice *pd;
int mode;
int used;
int parportno;
struct mutex mutex;
char phys[DB9_MAX_DEVICES][32 ];
};
static struct db9 *db9_base[3 ];
static const short db9_multi_btn[] = { BTN_TRIGGER, BTN_THUMB };
static const short db9_genesis_btn[] = { BTN_START, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_MODE };
static const short db9_cd32_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START };
static const short db9_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_RZ, ABS_Z, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y };
static const struct db9_mode_data db9_modes[] = {
{ NULL, NULL, 0 , 0 , 0 , 0 , 0 },
{ "Multisystem joystick" , db9_multi_btn, 1 , 1 , 2 , 1 , 1 },
{ "Multisystem joystick (2 fire)" , db9_multi_btn, 2 , 1 , 2 , 1 , 1 },
{ "Genesis pad" , db9_genesis_btn, 4 , 1 , 2 , 1 , 1 },
{ NULL, NULL, 0 , 0 , 0 , 0 , 0 },
{ "Genesis 5 pad" , db9_genesis_btn, 6 , 1 , 2 , 1 , 1 },
{ "Genesis 6 pad" , db9_genesis_btn, 8 , 1 , 2 , 1 , 1 },
{ "Saturn pad" , db9_cd32_btn, 9 , 6 , 7 , 0 , 1 },
{ "Multisystem (0.8.0.2) joystick" , db9_multi_btn, 1 , 1 , 2 , 1 , 1 },
{ "Multisystem (0.8.0.2-dual) joystick" , db9_multi_btn, 1 , 2 , 2 , 1 , 1 },
{ "Amiga CD-32 pad" , db9_cd32_btn, 7 , 1 , 2 , 1 , 1 },
{ "Saturn dpp" , db9_cd32_btn, 9 , 6 , 7 , 0 , 0 },
{ "Saturn dpp dual" , db9_cd32_btn, 9 , 12 , 7 , 0 , 0 },
};
/*
* Saturn controllers
*/
#define DB9_SATURN_DELAY 300
static const int db9_saturn_byte[] = { 1 , 1 , 1 , 2 , 2 , 2 , 2 , 2 , 1 };
static const unsigned char db9_saturn_mask[] = { 0 x04, 0 x01, 0 x02, 0 x40, 0 x20, 0 x10, 0 x08, 0 x80, 0 x08 };
/*
* db9_saturn_write_sub() writes 2 bit data.
*/
static void db9_saturn_write_sub(struct parport *port, int type, unsigned char data, int powered, int pwr_sub)
{
unsigned char c;
switch (type) {
case 1 : /* DPP1 */
c = 0 x80 | 0 x30 | (powered ? 0 x08 : 0 ) | (pwr_sub ? 0 x04 : 0 ) | data;
parport_write_data(port, c);
break ;
case 2 : /* DPP2 */
c = 0 x40 | data << 4 | (powered ? 0 x08 : 0 ) | (pwr_sub ? 0 x04 : 0 ) | 0 x03;
parport_write_data(port, c);
break ;
case 0 : /* DB9 */
c = ((((data & 2 ) ? 2 : 0 ) | ((data & 1 ) ? 4 : 0 )) ^ 0 x02) | !powered;
parport_write_control(port, c);
break ;
}
}
/*
* gc_saturn_read_sub() reads 4 bit data.
*/
static unsigned char db9_saturn_read_sub(struct parport *port, int type)
{
unsigned char data;
if (type) {
/* DPP */
data = parport_read_status(port) ^ 0 x80;
return (data & 0 x80 ? 1 : 0 ) | (data & 0 x40 ? 2 : 0 )
| (data & 0 x20 ? 4 : 0 ) | (data & 0 x10 ? 8 : 0 );
} else {
/* DB9 */
data = parport_read_data(port) & 0 x0f;
return (data & 0 x8 ? 1 : 0 ) | (data & 0 x4 ? 2 : 0 )
| (data & 0 x2 ? 4 : 0 ) | (data & 0 x1 ? 8 : 0 );
}
}
/*
* db9_saturn_read_analog() sends clock and reads 8 bit data.
*/
static unsigned char db9_saturn_read_analog(struct parport *port, int type, int powered)
{
unsigned char data;
db9_saturn_write_sub(port, type, 0 , powered, 0 );
udelay(DB9_SATURN_DELAY);
data = db9_saturn_read_sub(port, type) << 4 ;
db9_saturn_write_sub(port, type, 2 , powered, 0 );
udelay(DB9_SATURN_DELAY);
data |= db9_saturn_read_sub(port, type);
return data;
}
/*
* db9_saturn_read_packet() reads whole saturn packet at connector
* and returns device identifier code.
*/
static unsigned char db9_saturn_read_packet(struct parport *port, unsigned char *data, int type, int powered)
{
int i, j;
unsigned char tmp;
db9_saturn_write_sub(port, type, 3 , powered, 0 );
data[0 ] = db9_saturn_read_sub(port, type);
switch (data[0 ] & 0 x0f) {
case 0 xf:
/* 1111 no pad */
return data[0 ] = 0 xff;
case 0 x4: case 0 x4 | 0 x8:
/* ?100 : digital controller */
db9_saturn_write_sub(port, type, 0 , powered, 1 );
data[2 ] = db9_saturn_read_sub(port, type) << 4 ;
db9_saturn_write_sub(port, type, 2 , powered, 1 );
data[1 ] = db9_saturn_read_sub(port, type) << 4 ;
db9_saturn_write_sub(port, type, 1 , powered, 1 );
data[1 ] |= db9_saturn_read_sub(port, type);
db9_saturn_write_sub(port, type, 3 , powered, 1 );
/* data[2] |= db9_saturn_read_sub(port, type); */
data[2 ] |= data[0 ];
return data[0 ] = 0 x02;
case 0 x1:
/* 0001 : analog controller or multitap */
db9_saturn_write_sub(port, type, 2 , powered, 0 );
udelay(DB9_SATURN_DELAY);
data[0 ] = db9_saturn_read_analog(port, type, powered);
if (data[0 ] != 0 x41) {
/* read analog controller */
for (i = 0 ; i < (data[0 ] & 0 x0f); i++)
data[i + 1 ] = db9_saturn_read_analog(port, type, powered);
db9_saturn_write_sub(port, type, 3 , powered, 0 );
return data[0 ];
} else {
/* read multitap */
if (db9_saturn_read_analog(port, type, powered) != 0 x60)
return data[0 ] = 0 xff;
for (i = 0 ; i < 60 ; i += 10 ) {
data[i] = db9_saturn_read_analog(port, type, powered);
if (data[i] != 0 xff)
/* read each pad */
for (j = 0 ; j < (data[i] & 0 x0f); j++)
data[i + j + 1 ] = db9_saturn_read_analog(port, type, powered);
}
db9_saturn_write_sub(port, type, 3 , powered, 0 );
return 0 x41;
}
case 0 x0:
/* 0000 : mouse */
db9_saturn_write_sub(port, type, 2 , powered, 0 );
udelay(DB9_SATURN_DELAY);
tmp = db9_saturn_read_analog(port, type, powered);
if (tmp == 0 xff) {
for (i = 0 ; i < 3 ; i++)
data[i + 1 ] = db9_saturn_read_analog(port, type, powered);
db9_saturn_write_sub(port, type, 3 , powered, 0 );
return data[0 ] = 0 xe3;
}
fallthrough;
default :
return data[0 ];
}
}
/*
* db9_saturn_report() analyzes packet and reports.
*/
static int db9_saturn_report(unsigned char id, unsigned char data[60 ], struct input_dev *devs[], int n, int max_pads)
{
struct input_dev *dev;
int tmp, i, j;
tmp = (id == 0 x41) ? 60 : 10 ;
for (j = 0 ; j < tmp && n < max_pads; j += 10 , n++) {
dev = devs[n];
switch (data[j]) {
case 0 x16: /* multi controller (analog 4 axis) */
input_report_abs(dev, db9_abs[5 ], data[j + 6 ]);
fallthrough;
case 0 x15: /* mission stick (analog 3 axis) */
input_report_abs(dev, db9_abs[3 ], data[j + 4 ]);
input_report_abs(dev, db9_abs[4 ], data[j + 5 ]);
fallthrough;
case 0 x13: /* racing controller (analog 1 axis) */
input_report_abs(dev, db9_abs[2 ], data[j + 3 ]);
fallthrough;
case 0 x34: /* saturn keyboard (udlr ZXC ASD QE Esc) */
case 0 x02: /* digital pad (digital 2 axis + buttons) */
input_report_abs(dev, db9_abs[0 ], !(data[j + 1 ] & 128 ) - !(data[j + 1 ] & 64 ));
input_report_abs(dev, db9_abs[1 ], !(data[j + 1 ] & 32 ) - !(data[j + 1 ] & 16 ));
for (i = 0 ; i < 9 ; i++)
input_report_key(dev, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
break ;
case 0 x19: /* mission stick x2 (analog 6 axis + buttons) */
input_report_abs(dev, db9_abs[0 ], !(data[j + 1 ] & 128 ) - !(data[j + 1 ] & 64 ));
input_report_abs(dev, db9_abs[1 ], !(data[j + 1 ] & 32 ) - !(data[j + 1 ] & 16 ));
for (i = 0 ; i < 9 ; i++)
input_report_key(dev, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
input_report_abs(dev, db9_abs[2 ], data[j + 3 ]);
input_report_abs(dev, db9_abs[3 ], data[j + 4 ]);
input_report_abs(dev, db9_abs[4 ], data[j + 5 ]);
/*
input_report_abs(dev, db9_abs[8], (data[j + 6] & 128 ? 0 : 1) - (data[j + 6] & 64 ? 0 : 1));
input_report_abs(dev, db9_abs[9], (data[j + 6] & 32 ? 0 : 1) - (data[j + 6] & 16 ? 0 : 1));
*/
input_report_abs(dev, db9_abs[6 ], data[j + 7 ]);
input_report_abs(dev, db9_abs[7 ], data[j + 8 ]);
input_report_abs(dev, db9_abs[5 ], data[j + 9 ]);
break ;
case 0 xd3: /* sankyo ff (analog 1 axis + stop btn) */
input_report_key(dev, BTN_A, data[j + 3 ] & 0 x80);
input_report_abs(dev, db9_abs[2 ], data[j + 3 ] & 0 x7f);
break ;
case 0 xe3: /* shuttle mouse (analog 2 axis + buttons. signed value) */
input_report_key(dev, BTN_START, data[j + 1 ] & 0 x08);
input_report_key(dev, BTN_A, data[j + 1 ] & 0 x04);
input_report_key(dev, BTN_C, data[j + 1 ] & 0 x02);
input_report_key(dev, BTN_B, data[j + 1 ] & 0 x01);
input_report_abs(dev, db9_abs[2 ], data[j + 2 ] ^ 0 x80);
input_report_abs(dev, db9_abs[3 ], (0 xff-(data[j + 3 ] ^ 0 x80))+1 ); /* */
break ;
case 0 xff:
default : /* no pad */
input_report_abs(dev, db9_abs[0 ], 0 );
input_report_abs(dev, db9_abs[1 ], 0 );
for (i = 0 ; i < 9 ; i++)
input_report_key(dev, db9_cd32_btn[i], 0 );
break ;
}
}
return n;
}
static int db9_saturn(int mode, struct parport *port, struct input_dev *devs[])
{
unsigned char id, data[60 ];
int type, n, max_pads;
int tmp, i;
switch (mode) {
case DB9_SATURN_PAD:
type = 0 ;
n = 1 ;
break ;
case DB9_SATURN_DPP:
type = 1 ;
n = 1 ;
break ;
case DB9_SATURN_DPP_2:
type = 1 ;
n = 2 ;
break ;
default :
return -1 ;
}
max_pads = min(db9_modes[mode].n_pads, DB9_MAX_DEVICES);
for (tmp = 0 , i = 0 ; i < n; i++) {
id = db9_saturn_read_packet(port, data, type + i, 1 );
tmp = db9_saturn_report(id, data, devs, tmp, max_pads);
}
return 0 ;
}
static void db9_timer(struct timer_list *t)
{
struct db9 *db9 = timer_container_of(db9, t, timer);
struct parport *port = db9->pd->port;
struct input_dev *dev = db9->dev[0 ];
struct input_dev *dev2 = db9->dev[1 ];
int data, i;
switch (db9->mode) {
case DB9_MULTI_0802_2:
data = parport_read_data(port) >> 3 ;
input_report_abs(dev2, ABS_X, (data & DB9_RIGHT ? 0 : 1 ) - (data & DB9_LEFT ? 0 : 1 ));
input_report_abs(dev2, ABS_Y, (data & DB9_DOWN ? 0 : 1 ) - (data & DB9_UP ? 0 : 1 ));
input_report_key(dev2, BTN_TRIGGER, ~data & DB9_FIRE1);
fallthrough;
case DB9_MULTI_0802:
data = parport_read_status(port) >> 3 ;
input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1 ) - (data & DB9_LEFT ? 0 : 1 ));
input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1 ) - (data & DB9_UP ? 0 : 1 ));
input_report_key(dev, BTN_TRIGGER, data & DB9_FIRE1);
break ;
case DB9_MULTI_STICK:
data = parport_read_data(port);
input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1 ) - (data & DB9_LEFT ? 0 : 1 ));
input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1 ) - (data & DB9_UP ? 0 : 1 ));
input_report_key(dev, BTN_TRIGGER, ~data & DB9_FIRE1);
break ;
case DB9_MULTI2_STICK:
data = parport_read_data(port);
input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1 ) - (data & DB9_LEFT ? 0 : 1 ));
input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1 ) - (data & DB9_UP ? 0 : 1 ));
input_report_key(dev, BTN_TRIGGER, ~data & DB9_FIRE1);
input_report_key(dev, BTN_THUMB, ~data & DB9_FIRE2);
break ;
case DB9_GENESIS_PAD:
parport_write_control(port, DB9_NOSELECT);
data = parport_read_data(port);
input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1 ) - (data & DB9_LEFT ? 0 : 1 ));
input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1 ) - (data & DB9_UP ? 0 : 1 ));
input_report_key(dev, BTN_B, ~data & DB9_FIRE1);
input_report_key(dev, BTN_C, ~data & DB9_FIRE2);
parport_write_control(port, DB9_NORMAL);
data = parport_read_data(port);
input_report_key(dev, BTN_A, ~data & DB9_FIRE1);
input_report_key(dev, BTN_START, ~data & DB9_FIRE2);
break ;
case DB9_GENESIS5_PAD:
parport_write_control(port, DB9_NOSELECT);
data = parport_read_data(port);
input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1 ) - (data & DB9_LEFT ? 0 : 1 ));
input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1 ) - (data & DB9_UP ? 0 : 1 ));
input_report_key(dev, BTN_B, ~data & DB9_FIRE1);
input_report_key(dev, BTN_C, ~data & DB9_FIRE2);
parport_write_control(port, DB9_NORMAL);
data = parport_read_data(port);
input_report_key(dev, BTN_A, ~data & DB9_FIRE1);
input_report_key(dev, BTN_X, ~data & DB9_FIRE2);
input_report_key(dev, BTN_Y, ~data & DB9_LEFT);
input_report_key(dev, BTN_START, ~data & DB9_RIGHT);
break ;
case DB9_GENESIS6_PAD:
parport_write_control(port, DB9_NOSELECT); /* 1 */
udelay(DB9_GENESIS6_DELAY);
data = parport_read_data(port);
input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1 ) - (data & DB9_LEFT ? 0 : 1 ));
input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1 ) - (data & DB9_UP ? 0 : 1 ));
input_report_key(dev, BTN_B, ~data & DB9_FIRE1);
input_report_key(dev, BTN_C, ~data & DB9_FIRE2);
parport_write_control(port, DB9_NORMAL);
udelay(DB9_GENESIS6_DELAY);
data = parport_read_data(port);
input_report_key(dev, BTN_A, ~data & DB9_FIRE1);
input_report_key(dev, BTN_START, ~data & DB9_FIRE2);
parport_write_control(port, DB9_NOSELECT); /* 2 */
udelay(DB9_GENESIS6_DELAY);
parport_write_control(port, DB9_NORMAL);
udelay(DB9_GENESIS6_DELAY);
parport_write_control(port, DB9_NOSELECT); /* 3 */
udelay(DB9_GENESIS6_DELAY);
data=parport_read_data(port);
input_report_key(dev, BTN_X, ~data & DB9_LEFT);
input_report_key(dev, BTN_Y, ~data & DB9_DOWN);
input_report_key(dev, BTN_Z, ~data & DB9_UP);
input_report_key(dev, BTN_MODE, ~data & DB9_RIGHT);
parport_write_control(port, DB9_NORMAL);
udelay(DB9_GENESIS6_DELAY);
parport_write_control(port, DB9_NOSELECT); /* 4 */
udelay(DB9_GENESIS6_DELAY);
parport_write_control(port, DB9_NORMAL);
break ;
case DB9_SATURN_PAD:
case DB9_SATURN_DPP:
case DB9_SATURN_DPP_2:
db9_saturn(db9->mode, port, db9->dev);
break ;
case DB9_CD32_PAD:
data = parport_read_data(port);
input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1 ) - (data & DB9_LEFT ? 0 : 1 ));
input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1 ) - (data & DB9_UP ? 0 : 1 ));
parport_write_control(port, 0 x0a);
for (i = 0 ; i < 7 ; i++) {
data = parport_read_data(port);
parport_write_control(port, 0 x02);
parport_write_control(port, 0 x0a);
input_report_key(dev, db9_cd32_btn[i], ~data & DB9_FIRE2);
}
parport_write_control(port, 0 x00);
break ;
}
input_sync(dev);
mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME);
}
static int db9_open(struct input_dev *dev)
{
struct db9 *db9 = input_get_drvdata(dev);
struct parport *port = db9->pd->port;
scoped_guard(mutex_intr, &db9->mutex) {
if (!db9->used++) {
parport_claim(db9->pd);
parport_write_data(port, 0 xff);
if (db9_modes[db9->mode].reverse) {
parport_data_reverse(port);
parport_write_control(port, DB9_NORMAL);
}
mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME);
}
return 0 ;
}
return -EINTR;
}
static void db9_close(struct input_dev *dev)
{
struct db9 *db9 = input_get_drvdata(dev);
struct parport *port = db9->pd->port;
guard(mutex)(&db9->mutex);
if (!--db9->used) {
timer_delete_sync(&db9->timer);
parport_write_control(port, 0 x00);
parport_data_forward(port);
parport_release(db9->pd);
}
}
static void db9_attach(struct parport *pp)
{
struct db9 *db9;
const struct db9_mode_data *db9_mode;
struct pardevice *pd;
struct input_dev *input_dev;
int i, j, port_idx;
int mode;
struct pardev_cb db9_parport_cb;
for (port_idx = 0 ; port_idx < DB9_MAX_PORTS; port_idx++) {
if (db9_cfg[port_idx].nargs == 0 ||
db9_cfg[port_idx].args[DB9_ARG_PARPORT] < 0 )
continue ;
if (db9_cfg[port_idx].args[DB9_ARG_PARPORT] == pp->number)
break ;
}
if (port_idx == DB9_MAX_PORTS) {
pr_debug("Not using parport%d.\n" , pp->number);
return ;
}
mode = db9_cfg[port_idx].args[DB9_ARG_MODE];
if (mode < 1 || mode >= DB9_MAX_PAD || !db9_modes[mode].n_buttons) {
printk(KERN_ERR "db9.c: Bad device type %d\n" , mode);
return ;
}
db9_mode = &db9_modes[mode];
if (db9_mode->bidirectional && !(pp->modes & PARPORT_MODE_TRISTATE)) {
printk(KERN_ERR "db9.c: specified parport is not bidirectional\n" );
return ;
}
memset(&db9_parport_cb, 0 , sizeof (db9_parport_cb));
db9_parport_cb.flags = PARPORT_FLAG_EXCL;
pd = parport_register_dev_model(pp, "db9" , &db9_parport_cb, port_idx);
if (!pd) {
printk(KERN_ERR "db9.c: parport busy already - lp.o loaded?\n" );
return ;
}
db9 = kzalloc(sizeof (*db9), GFP_KERNEL);
if (!db9)
goto err_unreg_pardev;
mutex_init(&db9->mutex);
db9->pd = pd;
db9->mode = mode;
db9->parportno = pp->number;
timer_setup(&db9->timer, db9_timer, 0 );
for (i = 0 ; i < (min(db9_mode->n_pads, DB9_MAX_DEVICES)); i++) {
db9->dev[i] = input_dev = input_allocate_device();
if (!input_dev) {
printk(KERN_ERR "db9.c: Not enough memory for input device\n" );
goto err_unreg_devs;
}
snprintf(db9->phys[i], sizeof (db9->phys[i]),
"%s/input%d" , db9->pd->port->name, i);
input_dev->name = db9_mode->name;
input_dev->phys = db9->phys[i];
input_dev->id.bustype = BUS_PARPORT;
input_dev->id.vendor = 0 x0002;
input_dev->id.product = mode;
input_dev->id.version = 0 x0100;
input_set_drvdata(input_dev, db9);
input_dev->open = db9_open;
input_dev->close = db9_close;
input_dev->evbit[0 ] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
for (j = 0 ; j < db9_mode->n_buttons; j++)
set_bit(db9_mode->buttons[j], input_dev->keybit);
for (j = 0 ; j < db9_mode->n_axis; j++) {
if (j < 2 )
input_set_abs_params(input_dev, db9_abs[j], -1 , 1 , 0 , 0 );
else
input_set_abs_params(input_dev, db9_abs[j], 1 , 255 , 0 , 0 );
}
if (input_register_device(input_dev))
goto err_free_dev;
}
db9_base[port_idx] = db9;
return ;
err_free_dev:
input_free_device(db9->dev[i]);
err_unreg_devs:
while (--i >= 0 )
input_unregister_device(db9->dev[i]);
kfree(db9);
err_unreg_pardev:
parport_unregister_device(pd);
}
static void db9_detach(struct parport *port)
{
int i;
struct db9 *db9;
for (i = 0 ; i < DB9_MAX_PORTS; i++) {
if (db9_base[i] && db9_base[i]->parportno == port->number)
break ;
}
if (i == DB9_MAX_PORTS)
return ;
db9 = db9_base[i];
db9_base[i] = NULL;
for (i = 0 ; i < min(db9_modes[db9->mode].n_pads, DB9_MAX_DEVICES); i++)
input_unregister_device(db9->dev[i]);
parport_unregister_device(db9->pd);
kfree(db9);
}
static struct parport_driver db9_parport_driver = {
.name = "db9" ,
.match_port = db9_attach,
.detach = db9_detach,
};
static int __init db9_init(void )
{
int i;
int have_dev = 0 ;
for (i = 0 ; i < DB9_MAX_PORTS; i++) {
if (db9_cfg[i].nargs == 0 || db9_cfg[i].args[DB9_ARG_PARPORT] < 0 )
continue ;
if (db9_cfg[i].nargs < 2 ) {
printk(KERN_ERR "db9.c: Device type must be specified.\n" );
return -EINVAL;
}
have_dev = 1 ;
}
if (!have_dev)
return -ENODEV;
return parport_register_driver(&db9_parport_driver);
}
static void __exit db9_exit(void )
{
parport_unregister_driver(&db9_parport_driver);
}
module_init(db9_init);
module_exit(db9_exit);
Messung V0.5 in Prozent C=95 H=92 G=93
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-08)
¤
*© Formatika GbR, Deutschland