1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (c) 2001 Vojtech Pavlik
5 * Based on the work of:
10 * InterAct digital gamepad/joystick driver for Linux
16 #include <linux/kernel.h>
17 #include <linux/slab.h>
18 #include <linux/module.h>
19 #include <linux/delay.h>
20 #include <linux/gameport.h>
21 #include <linux/input.h>
22 #include <linux/jiffies.h>
24 #define DRIVER_DESC "InterAct digital joystick driver"
26 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
27 MODULE_DESCRIPTION(DRIVER_DESC
);
28 MODULE_LICENSE("GPL");
30 #define INTERACT_MAX_START 600 /* 400 us */
31 #define INTERACT_MAX_STROBE 60 /* 40 us */
32 #define INTERACT_MAX_LENGTH 32 /* 32 bits */
34 #define INTERACT_TYPE_HHFX 0 /* HammerHead/FX */
35 #define INTERACT_TYPE_PP8D 1 /* ProPad 8 */
38 struct gameport
*gameport
;
39 struct input_dev
*dev
;
47 static short interact_abs_hhfx
[] =
48 { ABS_RX
, ABS_RY
, ABS_X
, ABS_Y
, ABS_HAT0X
, ABS_HAT0Y
, -1 };
49 static short interact_abs_pp8d
[] =
52 static short interact_btn_hhfx
[] =
53 { BTN_TR
, BTN_X
, BTN_Y
, BTN_Z
, BTN_A
, BTN_B
, BTN_C
, BTN_TL
, BTN_TL2
, BTN_TR2
, BTN_MODE
, BTN_SELECT
, -1 };
54 static short interact_btn_pp8d
[] =
55 { BTN_C
, BTN_TL
, BTN_TR
, BTN_A
, BTN_B
, BTN_Y
, BTN_Z
, BTN_X
, -1 };
57 struct interact_type
{
66 static struct interact_type interact_type
[] = {
67 { 0x6202, interact_abs_hhfx
, interact_btn_hhfx
, "InterAct HammerHead/FX", 32, 4 },
68 { 0x53f8, interact_abs_pp8d
, interact_btn_pp8d
, "InterAct ProPad 8 Digital", 16, 0 },
72 * interact_read_packet() reads and InterAct joystick data.
75 static int interact_read_packet(struct gameport
*gameport
, int length
, u32
*data
)
83 data
[0] = data
[1] = data
[2] = 0;
84 t
= gameport_time(gameport
, INTERACT_MAX_START
);
85 s
= gameport_time(gameport
, INTERACT_MAX_STROBE
);
87 local_irq_save(flags
);
88 gameport_trigger(gameport
);
89 v
= gameport_read(gameport
);
91 while (t
> 0 && i
< length
) {
93 u
= v
; v
= gameport_read(gameport
);
95 data
[0] = (data
[0] << 1) | ((v
>> 4) & 1);
96 data
[1] = (data
[1] << 1) | ((v
>> 5) & 1);
97 data
[2] = (data
[2] << 1) | ((v
>> 7) & 1);
103 local_irq_restore(flags
);
109 * interact_poll() reads and analyzes InterAct joystick data.
112 static void interact_poll(struct gameport
*gameport
)
114 struct interact
*interact
= gameport_get_drvdata(gameport
);
115 struct input_dev
*dev
= interact
->dev
;
121 if (interact_read_packet(interact
->gameport
, interact
->length
, data
) < interact
->length
) {
125 for (i
= 0; i
< 3; i
++)
126 data
[i
] <<= INTERACT_MAX_LENGTH
- interact
->length
;
128 switch (interact
->type
) {
130 case INTERACT_TYPE_HHFX
:
132 for (i
= 0; i
< 4; i
++)
133 input_report_abs(dev
, interact_abs_hhfx
[i
], (data
[i
& 1] >> ((i
>> 1) << 3)) & 0xff);
135 for (i
= 0; i
< 2; i
++)
136 input_report_abs(dev
, ABS_HAT0Y
- i
,
137 ((data
[1] >> ((i
<< 1) + 17)) & 1) - ((data
[1] >> ((i
<< 1) + 16)) & 1));
139 for (i
= 0; i
< 8; i
++)
140 input_report_key(dev
, interact_btn_hhfx
[i
], (data
[0] >> (i
+ 16)) & 1);
142 for (i
= 0; i
< 4; i
++)
143 input_report_key(dev
, interact_btn_hhfx
[i
+ 8], (data
[1] >> (i
+ 20)) & 1);
147 case INTERACT_TYPE_PP8D
:
149 for (i
= 0; i
< 2; i
++)
150 input_report_abs(dev
, interact_abs_pp8d
[i
],
151 ((data
[0] >> ((i
<< 1) + 20)) & 1) - ((data
[0] >> ((i
<< 1) + 21)) & 1));
153 for (i
= 0; i
< 8; i
++)
154 input_report_key(dev
, interact_btn_pp8d
[i
], (data
[1] >> (i
+ 16)) & 1);
164 * interact_open() is a callback from the input open routine.
167 static int interact_open(struct input_dev
*dev
)
169 struct interact
*interact
= input_get_drvdata(dev
);
171 gameport_start_polling(interact
->gameport
);
176 * interact_close() is a callback from the input close routine.
179 static void interact_close(struct input_dev
*dev
)
181 struct interact
*interact
= input_get_drvdata(dev
);
183 gameport_stop_polling(interact
->gameport
);
187 * interact_connect() probes for InterAct joysticks.
190 static int interact_connect(struct gameport
*gameport
, struct gameport_driver
*drv
)
192 struct interact
*interact
;
193 struct input_dev
*input_dev
;
198 interact
= kzalloc(sizeof(struct interact
), GFP_KERNEL
);
199 input_dev
= input_allocate_device();
200 if (!interact
|| !input_dev
) {
205 interact
->gameport
= gameport
;
206 interact
->dev
= input_dev
;
208 gameport_set_drvdata(gameport
, interact
);
210 err
= gameport_open(gameport
, drv
, GAMEPORT_MODE_RAW
);
214 i
= interact_read_packet(gameport
, INTERACT_MAX_LENGTH
* 2, data
);
216 if (i
!= 32 || (data
[0] >> 24) != 0x0c || (data
[1] >> 24) != 0x02) {
221 for (i
= 0; interact_type
[i
].length
; i
++)
222 if (interact_type
[i
].id
== (data
[2] >> 16))
225 if (!interact_type
[i
].length
) {
226 printk(KERN_WARNING
"interact.c: Unknown joystick on %s. [len %d d0 %08x d1 %08x i2 %08x]\n",
227 gameport
->phys
, i
, data
[0], data
[1], data
[2]);
232 gameport_set_poll_handler(gameport
, interact_poll
);
233 gameport_set_poll_interval(gameport
, 20);
235 snprintf(interact
->phys
, sizeof(interact
->phys
), "%s/input0", gameport
->phys
);
238 interact
->length
= interact_type
[i
].length
;
240 input_dev
->name
= interact_type
[i
].name
;
241 input_dev
->phys
= interact
->phys
;
242 input_dev
->id
.bustype
= BUS_GAMEPORT
;
243 input_dev
->id
.vendor
= GAMEPORT_ID_VENDOR_INTERACT
;
244 input_dev
->id
.product
= interact_type
[i
].id
;
245 input_dev
->id
.version
= 0x0100;
246 input_dev
->dev
.parent
= &gameport
->dev
;
248 input_set_drvdata(input_dev
, interact
);
250 input_dev
->open
= interact_open
;
251 input_dev
->close
= interact_close
;
253 input_dev
->evbit
[0] = BIT_MASK(EV_KEY
) | BIT_MASK(EV_ABS
);
255 for (i
= 0; (t
= interact_type
[interact
->type
].abs
[i
]) >= 0; i
++) {
256 if (i
< interact_type
[interact
->type
].b8
)
257 input_set_abs_params(input_dev
, t
, 0, 255, 0, 0);
259 input_set_abs_params(input_dev
, t
, -1, 1, 0, 0);
262 for (i
= 0; (t
= interact_type
[interact
->type
].btn
[i
]) >= 0; i
++)
263 __set_bit(t
, input_dev
->keybit
);
265 err
= input_register_device(interact
->dev
);
271 fail2
: gameport_close(gameport
);
272 fail1
: gameport_set_drvdata(gameport
, NULL
);
273 input_free_device(input_dev
);
278 static void interact_disconnect(struct gameport
*gameport
)
280 struct interact
*interact
= gameport_get_drvdata(gameport
);
282 input_unregister_device(interact
->dev
);
283 gameport_close(gameport
);
284 gameport_set_drvdata(gameport
, NULL
);
288 static struct gameport_driver interact_drv
= {
292 .description
= DRIVER_DESC
,
293 .connect
= interact_connect
,
294 .disconnect
= interact_disconnect
,
297 module_gameport_driver(interact_drv
);