joy.c revision 330897
1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1995 Jean-Marc Zucconi
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer
12 *    in this position and unchanged.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: stable/11/sys/dev/joy/joy.c 330897 2018-03-14 03:19:51Z eadler $");
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/conf.h>
38#include <sys/uio.h>
39#include <sys/kernel.h>
40#include <sys/module.h>
41#include <sys/bus.h>
42#include <machine/bus.h>
43#include <machine/resource.h>
44#include <sys/rman.h>
45#include <sys/time.h>
46#include <sys/joystick.h>
47#include <dev/joy/joyvar.h>
48
49/* The game port can manage 4 buttons and 4 variable resistors (usually 2
50 * joysticks, each with 2 buttons and 2 pots.) via the port at address 0x201.
51 * Getting the state of the buttons is done by reading the game port:
52 * buttons 1-4 correspond to bits 4-7 and resistors 1-4 (X1, Y1, X2, Y2)
53 * to bits 0-3.
54 * if button 1 (resp 2, 3, 4) is pressed, the bit 4 (resp 5, 6, 7) is set to 0
55 * to get the value of a resistor, write the value 0xff at port and
56 * wait until the corresponding bit returns to 0.
57 */
58
59#define joypart(d) (dev2unit(d)&1)
60#ifndef JOY_TIMEOUT
61#define JOY_TIMEOUT   2000 /* 2 milliseconds */
62#endif
63
64static	d_open_t	joyopen;
65static	d_close_t	joyclose;
66static	d_read_t	joyread;
67static	d_ioctl_t	joyioctl;
68
69static struct cdevsw joy_cdevsw = {
70	.d_version =	D_VERSION,
71	.d_flags =	D_NEEDGIANT,
72	.d_open =	joyopen,
73	.d_close =	joyclose,
74	.d_read =	joyread,
75	.d_ioctl =	joyioctl,
76	.d_name =	"joy",
77};
78
79devclass_t joy_devclass;
80
81int
82joy_probe(device_t dev)
83{
84#ifdef WANT_JOYSTICK_CONNECTED
85#ifdef notyet
86	outb(dev->id_iobase, 0xff);
87	DELAY(10000); /*  10 ms delay */
88	return (inb(dev->id_iobase) & 0x0f) != 0x0f;
89#else
90	return (0);
91#endif
92#else
93	return (0);
94#endif
95}
96
97int
98joy_attach(device_t dev)
99{
100	int	unit = device_get_unit(dev);
101	struct joy_softc *joy = device_get_softc(dev);
102
103	joy->rid = 0;
104	joy->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &joy->rid,
105	    RF_ACTIVE|RF_SHAREABLE);
106	if (joy->res == NULL)
107		return ENXIO;
108	joy->bt = rman_get_bustag(joy->res);
109	joy->port = rman_get_bushandle(joy->res);
110	joy->timeout[0] = joy->timeout[1] = 0;
111	joy->d = make_dev(&joy_cdevsw, unit, 0, 0, 0600, "joy%d", unit);
112	joy->d->si_drv1 = joy;
113	return (0);
114}
115
116int
117joy_detach(device_t dev)
118{
119	struct joy_softc *joy = device_get_softc(dev);
120
121	if (joy->res != NULL)
122		bus_release_resource(dev, SYS_RES_IOPORT, joy->rid, joy->res);
123	if (joy->d)
124		destroy_dev(joy->d);
125	return (0);
126}
127
128
129static int
130joyopen(struct cdev *dev, int flags, int fmt, struct thread *td)
131{
132	int i = joypart (dev);
133	struct joy_softc *joy = dev->si_drv1;
134
135	if (joy->timeout[i])
136		return (EBUSY);
137	joy->x_off[i] = joy->y_off[i] = 0;
138	joy->timeout[i] = JOY_TIMEOUT;
139	return (0);
140}
141
142static int
143joyclose(struct cdev *dev, int flags, int fmt, struct thread *td)
144{
145	int i = joypart (dev);
146	struct joy_softc *joy = dev->si_drv1;
147
148	joy->timeout[i] = 0;
149	return (0);
150}
151
152static int
153joyread(struct cdev *dev, struct uio *uio, int flag)
154{
155	struct joy_softc *joy = dev->si_drv1;
156	bus_space_handle_t port = joy->port;
157	bus_space_tag_t bt = joy->bt;
158	struct timespec t, start, end;
159	int state = 0;
160	struct timespec x, y;
161	struct joystick c;
162#ifndef __i386__
163	int s;
164
165	s = splhigh();
166#else
167	disable_intr ();
168#endif
169	nanotime(&t);
170	end.tv_sec = 0;
171	end.tv_nsec = joy->timeout[joypart(dev)] * 1000;
172	timespecadd(&end, &t);
173	for (; timespeccmp(&t, &end, <) && (bus_space_read_1(bt, port, 0) & 0x0f); nanotime(&t))
174		;	/* nothing */
175	bus_space_write_1 (bt, port, 0, 0xff);
176	nanotime(&start);
177	end.tv_sec = 0;
178	end.tv_nsec = joy->timeout[joypart(dev)] * 1000;
179	timespecadd(&end, &start);
180	t = start;
181	timespecclear(&x);
182	timespecclear(&y);
183	while (timespeccmp(&t, &end, <)) {
184		state = bus_space_read_1 (bt, port, 0);
185		if (joypart(dev) == 1)
186			state >>= 2;
187		nanotime(&t);
188		if (!timespecisset(&x) && !(state & 0x01))
189			x = t;
190		if (!timespecisset(&y) && !(state & 0x02))
191			y = t;
192		if (timespecisset(&x) && timespecisset(&y))
193			break;
194	}
195#ifndef __i386__
196	splx(s);
197#else
198	enable_intr ();
199#endif
200	if (timespecisset(&x)) {
201		timespecsub(&x, &start);
202		c.x = joy->x_off[joypart(dev)] + x.tv_nsec / 1000;
203	} else
204		c.x = 0x80000000;
205	if (timespecisset(&y)) {
206		timespecsub(&y, &start);
207		c.y = joy->y_off[joypart(dev)] + y.tv_nsec / 1000;
208	} else
209		c.y = 0x80000000;
210	state >>= 4;
211	c.b1 = ~state & 1;
212	c.b2 = ~(state >> 1) & 1;
213	return (uiomove((caddr_t)&c, sizeof(struct joystick), uio));
214}
215
216static int
217joyioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
218{
219	struct joy_softc *joy = dev->si_drv1;
220	int i = joypart (dev);
221	int x;
222
223	switch (cmd) {
224	case JOY_SETTIMEOUT:
225		x = *(int *) data;
226		if (x < 1 || x > 10000) /* 10ms maximum! */
227			return EINVAL;
228		joy->timeout[i] = x;
229		break;
230	case JOY_GETTIMEOUT:
231		*(int *) data = joy->timeout[i];
232		break;
233	case JOY_SET_X_OFFSET:
234		joy->x_off[i] = *(int *) data;
235		break;
236	case JOY_SET_Y_OFFSET:
237		joy->y_off[i] = *(int *) data;
238		break;
239	case JOY_GET_X_OFFSET:
240		*(int *) data = joy->x_off[i];
241		break;
242	case JOY_GET_Y_OFFSET:
243		*(int *) data = joy->y_off[i];
244		break;
245	default:
246		return (ENOTTY);
247	}
248	return (0);
249}
250