Linux Audio

Check our new training course

Linux kernel drivers training

May 6-19, 2025
Register
Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 *  Copyright (c) 1998-2001 Vojtech Pavlik
  4 */
  5
  6/*
  7 * PDPI Lightning 4 gamecard driver for Linux.
  8 */
  9
 10#include <asm/io.h>
 11#include <linux/delay.h>
 12#include <linux/errno.h>
 13#include <linux/ioport.h>
 14#include <linux/kernel.h>
 15#include <linux/module.h>
 16#include <linux/init.h>
 17#include <linux/gameport.h>
 18
 19#define L4_PORT			0x201
 20#define L4_SELECT_ANALOG	0xa4
 21#define L4_SELECT_DIGITAL	0xa5
 22#define L4_SELECT_SECONDARY	0xa6
 23#define L4_CMD_ID		0x80
 24#define L4_CMD_GETCAL		0x92
 25#define L4_CMD_SETCAL		0x93
 26#define L4_ID			0x04
 27#define L4_BUSY			0x01
 28#define L4_TIMEOUT		80	/* 80 us */
 29
 30MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 31MODULE_DESCRIPTION("PDPI Lightning 4 gamecard driver");
 32MODULE_LICENSE("GPL");
 33
 34struct l4 {
 35	struct gameport *gameport;
 36	unsigned char port;
 37};
 38
 39static struct l4 l4_ports[8];
 40
 41/*
 42 * l4_wait_ready() waits for the L4 to become ready.
 43 */
 44
 45static int l4_wait_ready(void)
 46{
 47	unsigned int t = L4_TIMEOUT;
 48
 49	while ((inb(L4_PORT) & L4_BUSY) && t > 0) t--;
 50	return -(t <= 0);
 51}
 52
 53/*
 54 * l4_cooked_read() reads data from the Lightning 4.
 55 */
 56
 57static int l4_cooked_read(struct gameport *gameport, int *axes, int *buttons)
 58{
 59	struct l4 *l4 = gameport->port_data;
 60	unsigned char status;
 61	int i, result = -1;
 62
 63	outb(L4_SELECT_ANALOG, L4_PORT);
 64	outb(L4_SELECT_DIGITAL + (l4->port >> 2), L4_PORT);
 65
 66	if (inb(L4_PORT) & L4_BUSY) goto fail;
 67	outb(l4->port & 3, L4_PORT);
 68
 69	if (l4_wait_ready()) goto fail;
 70	status = inb(L4_PORT);
 71
 72	for (i = 0; i < 4; i++)
 73		if (status & (1 << i)) {
 74			if (l4_wait_ready()) goto fail;
 75			axes[i] = inb(L4_PORT);
 76			if (axes[i] > 252) axes[i] = -1;
 77		}
 78
 79	if (status & 0x10) {
 80		if (l4_wait_ready()) goto fail;
 81		*buttons = inb(L4_PORT) & 0x0f;
 82	}
 83
 84	result = 0;
 85
 86fail:	outb(L4_SELECT_ANALOG, L4_PORT);
 87	return result;
 88}
 89
 90static int l4_open(struct gameport *gameport, int mode)
 91{
 92	struct l4 *l4 = gameport->port_data;
 93
 94        if (l4->port != 0 && mode != GAMEPORT_MODE_COOKED)
 95		return -1;
 96	outb(L4_SELECT_ANALOG, L4_PORT);
 97	return 0;
 98}
 99
100/*
101 * l4_getcal() reads the L4 with calibration values.
102 */
103
104static int l4_getcal(int port, int *cal)
105{
106	int i, result = -1;
107
108	outb(L4_SELECT_ANALOG, L4_PORT);
109	outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
110	if (inb(L4_PORT) & L4_BUSY)
111		goto out;
112
113	outb(L4_CMD_GETCAL, L4_PORT);
114	if (l4_wait_ready())
115		goto out;
116
117	if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))
118		goto out;
119
120	if (l4_wait_ready())
121		goto out;
122        outb(port & 3, L4_PORT);
123
124	for (i = 0; i < 4; i++) {
125		if (l4_wait_ready())
126			goto out;
127		cal[i] = inb(L4_PORT);
128	}
129
130	result = 0;
131
132out:	outb(L4_SELECT_ANALOG, L4_PORT);
133	return result;
134}
135
136/*
137 * l4_setcal() programs the L4 with calibration values.
138 */
139
140static int l4_setcal(int port, int *cal)
141{
142	int i, result = -1;
143
144	outb(L4_SELECT_ANALOG, L4_PORT);
145	outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
146	if (inb(L4_PORT) & L4_BUSY)
147		goto out;
148
149	outb(L4_CMD_SETCAL, L4_PORT);
150	if (l4_wait_ready())
151		goto out;
152
153	if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))
154		goto out;
155
156	if (l4_wait_ready())
157		goto out;
158        outb(port & 3, L4_PORT);
159
160	for (i = 0; i < 4; i++) {
161		if (l4_wait_ready())
162			goto out;
163		outb(cal[i], L4_PORT);
164	}
165
166	result = 0;
167
168out:	outb(L4_SELECT_ANALOG, L4_PORT);
169	return result;
170}
171
172/*
173 * l4_calibrate() calibrates the L4 for the attached device, so
174 * that the device's resistance fits into the L4's 8-bit range.
175 */
176
177static int l4_calibrate(struct gameport *gameport, int *axes, int *max)
178{
179	int i, t;
180	int cal[4];
181	struct l4 *l4 = gameport->port_data;
182
183	if (l4_getcal(l4->port, cal))
184		return -1;
185
186	for (i = 0; i < 4; i++) {
187		t = (max[i] * cal[i]) / 200;
188		t = (t < 1) ? 1 : ((t > 255) ? 255 : t);
189		axes[i] = (axes[i] < 0) ? -1 : (axes[i] * cal[i]) / t;
190		axes[i] = (axes[i] > 252) ? 252 : axes[i];
191		cal[i] = t;
192	}
193
194	if (l4_setcal(l4->port, cal))
195		return -1;
196
197	return 0;
198}
199
200static int __init l4_create_ports(int card_no)
201{
202	struct l4 *l4;
203	struct gameport *port;
204	int i, idx;
205
206	for (i = 0; i < 4; i++) {
207
208		idx = card_no * 4 + i;
209		l4 = &l4_ports[idx];
210
211		if (!(l4->gameport = port = gameport_allocate_port())) {
212			printk(KERN_ERR "lightning: Memory allocation failed\n");
213			while (--i >= 0) {
214				gameport_free_port(l4->gameport);
215				l4->gameport = NULL;
216			}
217			return -ENOMEM;
218		}
219		l4->port = idx;
220
221		port->port_data = l4;
222		port->open = l4_open;
223		port->cooked_read = l4_cooked_read;
224		port->calibrate = l4_calibrate;
225
226		gameport_set_name(port, "PDPI Lightning 4");
227		gameport_set_phys(port, "isa%04x/gameport%d", L4_PORT, idx);
228
229		if (idx == 0)
230			port->io = L4_PORT;
231	}
232
233	return 0;
234}
235
236static int __init l4_add_card(int card_no)
237{
238	int cal[4] = { 255, 255, 255, 255 };
239	int i, rev, result;
240	struct l4 *l4;
241
242	outb(L4_SELECT_ANALOG, L4_PORT);
243	outb(L4_SELECT_DIGITAL + card_no, L4_PORT);
244
245	if (inb(L4_PORT) & L4_BUSY)
246		return -1;
247	outb(L4_CMD_ID, L4_PORT);
248
249	if (l4_wait_ready())
250		return -1;
251
252	if (inb(L4_PORT) != L4_SELECT_DIGITAL + card_no)
253		return -1;
254
255	if (l4_wait_ready())
256		return -1;
257	if (inb(L4_PORT) != L4_ID)
258		return -1;
259
260	if (l4_wait_ready())
261		return -1;
262	rev = inb(L4_PORT);
263
264	if (!rev)
265		return -1;
266
267	result = l4_create_ports(card_no);
268	if (result)
269		return result;
270
271	printk(KERN_INFO "gameport: PDPI Lightning 4 %s card v%d.%d at %#x\n",
272		card_no ? "secondary" : "primary", rev >> 4, rev, L4_PORT);
273
274	for (i = 0; i < 4; i++) {
275		l4 = &l4_ports[card_no * 4 + i];
276
277		if (rev > 0x28)		/* on 2.9+ the setcal command works correctly */
278			l4_setcal(l4->port, cal);
279		gameport_register_port(l4->gameport);
280	}
281
282	return 0;
283}
284
285static int __init l4_init(void)
286{
287	int i, cards = 0;
288
289	if (!request_region(L4_PORT, 1, "lightning"))
290		return -EBUSY;
291
292	for (i = 0; i < 2; i++)
293		if (l4_add_card(i) == 0)
294			cards++;
295
296	outb(L4_SELECT_ANALOG, L4_PORT);
297
298	if (!cards) {
299		release_region(L4_PORT, 1);
300		return -ENODEV;
301	}
302
303	return 0;
304}
305
306static void __exit l4_exit(void)
307{
308	int i;
309	int cal[4] = { 59, 59, 59, 59 };
310
311	for (i = 0; i < 8; i++)
312		if (l4_ports[i].gameport) {
313			l4_setcal(l4_ports[i].port, cal);
314			gameport_unregister_port(l4_ports[i].gameport);
315		}
316
317	outb(L4_SELECT_ANALOG, L4_PORT);
318	release_region(L4_PORT, 1);
319}
320
321module_init(l4_init);
322module_exit(l4_exit);
v6.8
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 *  Copyright (c) 1998-2001 Vojtech Pavlik
  4 */
  5
  6/*
  7 * PDPI Lightning 4 gamecard driver for Linux.
  8 */
  9
 10#include <asm/io.h>
 11#include <linux/delay.h>
 12#include <linux/errno.h>
 13#include <linux/ioport.h>
 14#include <linux/kernel.h>
 15#include <linux/module.h>
 16#include <linux/init.h>
 17#include <linux/gameport.h>
 18
 19#define L4_PORT			0x201
 20#define L4_SELECT_ANALOG	0xa4
 21#define L4_SELECT_DIGITAL	0xa5
 22#define L4_SELECT_SECONDARY	0xa6
 23#define L4_CMD_ID		0x80
 24#define L4_CMD_GETCAL		0x92
 25#define L4_CMD_SETCAL		0x93
 26#define L4_ID			0x04
 27#define L4_BUSY			0x01
 28#define L4_TIMEOUT		80	/* 80 us */
 29
 30MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 31MODULE_DESCRIPTION("PDPI Lightning 4 gamecard driver");
 32MODULE_LICENSE("GPL");
 33
 34struct l4 {
 35	struct gameport *gameport;
 36	unsigned char port;
 37};
 38
 39static struct l4 l4_ports[8];
 40
 41/*
 42 * l4_wait_ready() waits for the L4 to become ready.
 43 */
 44
 45static int l4_wait_ready(void)
 46{
 47	unsigned int t = L4_TIMEOUT;
 48
 49	while ((inb(L4_PORT) & L4_BUSY) && t > 0) t--;
 50	return -(t <= 0);
 51}
 52
 53/*
 54 * l4_cooked_read() reads data from the Lightning 4.
 55 */
 56
 57static int l4_cooked_read(struct gameport *gameport, int *axes, int *buttons)
 58{
 59	struct l4 *l4 = gameport->port_data;
 60	unsigned char status;
 61	int i, result = -1;
 62
 63	outb(L4_SELECT_ANALOG, L4_PORT);
 64	outb(L4_SELECT_DIGITAL + (l4->port >> 2), L4_PORT);
 65
 66	if (inb(L4_PORT) & L4_BUSY) goto fail;
 67	outb(l4->port & 3, L4_PORT);
 68
 69	if (l4_wait_ready()) goto fail;
 70	status = inb(L4_PORT);
 71
 72	for (i = 0; i < 4; i++)
 73		if (status & (1 << i)) {
 74			if (l4_wait_ready()) goto fail;
 75			axes[i] = inb(L4_PORT);
 76			if (axes[i] > 252) axes[i] = -1;
 77		}
 78
 79	if (status & 0x10) {
 80		if (l4_wait_ready()) goto fail;
 81		*buttons = inb(L4_PORT) & 0x0f;
 82	}
 83
 84	result = 0;
 85
 86fail:	outb(L4_SELECT_ANALOG, L4_PORT);
 87	return result;
 88}
 89
 90static int l4_open(struct gameport *gameport, int mode)
 91{
 92	struct l4 *l4 = gameport->port_data;
 93
 94        if (l4->port != 0 && mode != GAMEPORT_MODE_COOKED)
 95		return -1;
 96	outb(L4_SELECT_ANALOG, L4_PORT);
 97	return 0;
 98}
 99
100/*
101 * l4_getcal() reads the L4 with calibration values.
102 */
103
104static int l4_getcal(int port, int *cal)
105{
106	int i, result = -1;
107
108	outb(L4_SELECT_ANALOG, L4_PORT);
109	outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
110	if (inb(L4_PORT) & L4_BUSY)
111		goto out;
112
113	outb(L4_CMD_GETCAL, L4_PORT);
114	if (l4_wait_ready())
115		goto out;
116
117	if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))
118		goto out;
119
120	if (l4_wait_ready())
121		goto out;
122        outb(port & 3, L4_PORT);
123
124	for (i = 0; i < 4; i++) {
125		if (l4_wait_ready())
126			goto out;
127		cal[i] = inb(L4_PORT);
128	}
129
130	result = 0;
131
132out:	outb(L4_SELECT_ANALOG, L4_PORT);
133	return result;
134}
135
136/*
137 * l4_setcal() programs the L4 with calibration values.
138 */
139
140static int l4_setcal(int port, int *cal)
141{
142	int i, result = -1;
143
144	outb(L4_SELECT_ANALOG, L4_PORT);
145	outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
146	if (inb(L4_PORT) & L4_BUSY)
147		goto out;
148
149	outb(L4_CMD_SETCAL, L4_PORT);
150	if (l4_wait_ready())
151		goto out;
152
153	if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))
154		goto out;
155
156	if (l4_wait_ready())
157		goto out;
158        outb(port & 3, L4_PORT);
159
160	for (i = 0; i < 4; i++) {
161		if (l4_wait_ready())
162			goto out;
163		outb(cal[i], L4_PORT);
164	}
165
166	result = 0;
167
168out:	outb(L4_SELECT_ANALOG, L4_PORT);
169	return result;
170}
171
172/*
173 * l4_calibrate() calibrates the L4 for the attached device, so
174 * that the device's resistance fits into the L4's 8-bit range.
175 */
176
177static int l4_calibrate(struct gameport *gameport, int *axes, int *max)
178{
179	int i, t;
180	int cal[4];
181	struct l4 *l4 = gameport->port_data;
182
183	if (l4_getcal(l4->port, cal))
184		return -1;
185
186	for (i = 0; i < 4; i++) {
187		t = (max[i] * cal[i]) / 200;
188		t = (t < 1) ? 1 : ((t > 255) ? 255 : t);
189		axes[i] = (axes[i] < 0) ? -1 : (axes[i] * cal[i]) / t;
190		axes[i] = (axes[i] > 252) ? 252 : axes[i];
191		cal[i] = t;
192	}
193
194	if (l4_setcal(l4->port, cal))
195		return -1;
196
197	return 0;
198}
199
200static int __init l4_create_ports(int card_no)
201{
202	struct l4 *l4;
203	struct gameport *port;
204	int i, idx;
205
206	for (i = 0; i < 4; i++) {
207
208		idx = card_no * 4 + i;
209		l4 = &l4_ports[idx];
210
211		if (!(l4->gameport = port = gameport_allocate_port())) {
212			printk(KERN_ERR "lightning: Memory allocation failed\n");
213			while (--i >= 0) {
214				gameport_free_port(l4->gameport);
215				l4->gameport = NULL;
216			}
217			return -ENOMEM;
218		}
219		l4->port = idx;
220
221		port->port_data = l4;
222		port->open = l4_open;
223		port->cooked_read = l4_cooked_read;
224		port->calibrate = l4_calibrate;
225
226		gameport_set_name(port, "PDPI Lightning 4");
227		gameport_set_phys(port, "isa%04x/gameport%d", L4_PORT, idx);
228
229		if (idx == 0)
230			port->io = L4_PORT;
231	}
232
233	return 0;
234}
235
236static int __init l4_add_card(int card_no)
237{
238	int cal[4] = { 255, 255, 255, 255 };
239	int i, rev, result;
240	struct l4 *l4;
241
242	outb(L4_SELECT_ANALOG, L4_PORT);
243	outb(L4_SELECT_DIGITAL + card_no, L4_PORT);
244
245	if (inb(L4_PORT) & L4_BUSY)
246		return -1;
247	outb(L4_CMD_ID, L4_PORT);
248
249	if (l4_wait_ready())
250		return -1;
251
252	if (inb(L4_PORT) != L4_SELECT_DIGITAL + card_no)
253		return -1;
254
255	if (l4_wait_ready())
256		return -1;
257	if (inb(L4_PORT) != L4_ID)
258		return -1;
259
260	if (l4_wait_ready())
261		return -1;
262	rev = inb(L4_PORT);
263
264	if (!rev)
265		return -1;
266
267	result = l4_create_ports(card_no);
268	if (result)
269		return result;
270
271	printk(KERN_INFO "gameport: PDPI Lightning 4 %s card v%d.%d at %#x\n",
272		card_no ? "secondary" : "primary", rev >> 4, rev, L4_PORT);
273
274	for (i = 0; i < 4; i++) {
275		l4 = &l4_ports[card_no * 4 + i];
276
277		if (rev > 0x28)		/* on 2.9+ the setcal command works correctly */
278			l4_setcal(l4->port, cal);
279		gameport_register_port(l4->gameport);
280	}
281
282	return 0;
283}
284
285static int __init l4_init(void)
286{
287	int i, cards = 0;
288
289	if (!request_region(L4_PORT, 1, "lightning"))
290		return -EBUSY;
291
292	for (i = 0; i < 2; i++)
293		if (l4_add_card(i) == 0)
294			cards++;
295
296	outb(L4_SELECT_ANALOG, L4_PORT);
297
298	if (!cards) {
299		release_region(L4_PORT, 1);
300		return -ENODEV;
301	}
302
303	return 0;
304}
305
306static void __exit l4_exit(void)
307{
308	int i;
309	int cal[4] = { 59, 59, 59, 59 };
310
311	for (i = 0; i < 8; i++)
312		if (l4_ports[i].gameport) {
313			l4_setcal(l4_ports[i].port, cal);
314			gameport_unregister_port(l4_ports[i].gameport);
315		}
316
317	outb(L4_SELECT_ANALOG, L4_PORT);
318	release_region(L4_PORT, 1);
319}
320
321module_init(l4_init);
322module_exit(l4_exit);