xref: /minix3/minix/servers/input/input.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1*433d6423SLionel Sambuc /* Keyboard/mouse input server. */
2*433d6423SLionel Sambuc #include <minix/drivers.h>
3*433d6423SLionel Sambuc #include <minix/chardriver.h>
4*433d6423SLionel Sambuc #include <minix/ds.h>
5*433d6423SLionel Sambuc #include <sys/ioctl.h>
6*433d6423SLionel Sambuc #include <sys/kbdio.h>
7*433d6423SLionel Sambuc 
8*433d6423SLionel Sambuc #include "input.h"
9*433d6423SLionel Sambuc 
10*433d6423SLionel Sambuc #define INPUT_DEBUG 0
11*433d6423SLionel Sambuc 
12*433d6423SLionel Sambuc static int input_open(devminor_t, int, endpoint_t);
13*433d6423SLionel Sambuc static int input_close(devminor_t);
14*433d6423SLionel Sambuc static ssize_t input_read(devminor_t, u64_t, endpoint_t, cp_grant_id_t, size_t,
15*433d6423SLionel Sambuc 	int, cdev_id_t);
16*433d6423SLionel Sambuc static int input_ioctl(devminor_t, unsigned long, endpoint_t, cp_grant_id_t,
17*433d6423SLionel Sambuc 	int, endpoint_t, cdev_id_t);
18*433d6423SLionel Sambuc static int input_cancel(devminor_t, endpoint_t, cdev_id_t);
19*433d6423SLionel Sambuc static int input_select(devminor_t, unsigned int, endpoint_t);
20*433d6423SLionel Sambuc static void input_other(message *, int);
21*433d6423SLionel Sambuc 
22*433d6423SLionel Sambuc static struct input_dev devs[INPUT_DEV_MAX];
23*433d6423SLionel Sambuc 
24*433d6423SLionel Sambuc #define input_dev_active(dev)		((dev)->owner != NONE || \
25*433d6423SLionel Sambuc 					 (dev)->minor == KBDMUX_MINOR || \
26*433d6423SLionel Sambuc 					 (dev)->minor == MOUSEMUX_MINOR)
27*433d6423SLionel Sambuc #define input_dev_buf_empty(dev)	((dev)->count == 0)
28*433d6423SLionel Sambuc #define input_dev_buf_full(dev)		((dev)->count == EVENTBUF_SIZE)
29*433d6423SLionel Sambuc 
30*433d6423SLionel Sambuc /* Entry points to the input driver. */
31*433d6423SLionel Sambuc static struct chardriver input_tab = {
32*433d6423SLionel Sambuc 	.cdr_open	= input_open,
33*433d6423SLionel Sambuc 	.cdr_close	= input_close,
34*433d6423SLionel Sambuc 	.cdr_read	= input_read,
35*433d6423SLionel Sambuc 	.cdr_ioctl	= input_ioctl,
36*433d6423SLionel Sambuc 	.cdr_cancel	= input_cancel,
37*433d6423SLionel Sambuc 	.cdr_select	= input_select,
38*433d6423SLionel Sambuc 	.cdr_other	= input_other
39*433d6423SLionel Sambuc };
40*433d6423SLionel Sambuc 
41*433d6423SLionel Sambuc /*
42*433d6423SLionel Sambuc  * Map a minor number to an input device structure.
43*433d6423SLionel Sambuc  */
44*433d6423SLionel Sambuc static struct input_dev *
input_map(devminor_t minor)45*433d6423SLionel Sambuc input_map(devminor_t minor)
46*433d6423SLionel Sambuc {
47*433d6423SLionel Sambuc 	/*
48*433d6423SLionel Sambuc 	 * The minor device numbers were chosen not to be equal to the array
49*433d6423SLionel Sambuc 	 * slots, so that more keyboards can be added without breaking backward
50*433d6423SLionel Sambuc 	 * compatibility later.
51*433d6423SLionel Sambuc 	 */
52*433d6423SLionel Sambuc 	if (minor == KBDMUX_MINOR)
53*433d6423SLionel Sambuc 		return &devs[KBDMUX_DEV];
54*433d6423SLionel Sambuc 	else if (minor >= KBD0_MINOR && minor < KBD0_MINOR + KBD_MINORS)
55*433d6423SLionel Sambuc 		return &devs[FIRST_KBD_DEV + (minor - KBD0_MINOR)];
56*433d6423SLionel Sambuc 	else if (minor == MOUSEMUX_MINOR)
57*433d6423SLionel Sambuc 		return &devs[MOUSEMUX_DEV];
58*433d6423SLionel Sambuc 	else if (minor >= MOUSE0_MINOR && minor < MOUSE0_MINOR + MOUSE_MINORS)
59*433d6423SLionel Sambuc 		return &devs[FIRST_MOUSE_DEV + (minor - MOUSE0_MINOR)];
60*433d6423SLionel Sambuc 	else
61*433d6423SLionel Sambuc 		return NULL;
62*433d6423SLionel Sambuc }
63*433d6423SLionel Sambuc 
64*433d6423SLionel Sambuc /*
65*433d6423SLionel Sambuc  * Map an input device structure index to a minor number.
66*433d6423SLionel Sambuc  */
67*433d6423SLionel Sambuc static devminor_t
input_revmap(int id)68*433d6423SLionel Sambuc input_revmap(int id)
69*433d6423SLionel Sambuc {
70*433d6423SLionel Sambuc 	if (id == KBDMUX_DEV)
71*433d6423SLionel Sambuc 		return KBDMUX_MINOR;
72*433d6423SLionel Sambuc 	else if (id >= FIRST_KBD_DEV && id <= LAST_KBD_DEV)
73*433d6423SLionel Sambuc 		return KBD0_MINOR + (id - FIRST_KBD_DEV);
74*433d6423SLionel Sambuc 	else if (id == MOUSEMUX_DEV)
75*433d6423SLionel Sambuc 		return MOUSEMUX_MINOR;
76*433d6423SLionel Sambuc 	else if (id >= FIRST_MOUSE_DEV && id <= LAST_MOUSE_DEV)
77*433d6423SLionel Sambuc 		return MOUSE0_MINOR + (id - FIRST_MOUSE_DEV);
78*433d6423SLionel Sambuc 	else
79*433d6423SLionel Sambuc 		panic("reverse-mapping invalid ID %d", id);
80*433d6423SLionel Sambuc }
81*433d6423SLionel Sambuc 
82*433d6423SLionel Sambuc /*
83*433d6423SLionel Sambuc  * Open an input device.
84*433d6423SLionel Sambuc  */
85*433d6423SLionel Sambuc static int
input_open(devminor_t minor,int UNUSED (access),endpoint_t UNUSED (user_endpt))86*433d6423SLionel Sambuc input_open(devminor_t minor, int UNUSED(access), endpoint_t UNUSED(user_endpt))
87*433d6423SLionel Sambuc {
88*433d6423SLionel Sambuc 	struct input_dev *input_dev;
89*433d6423SLionel Sambuc 
90*433d6423SLionel Sambuc 	if ((input_dev = input_map(minor)) == NULL)
91*433d6423SLionel Sambuc 		return ENXIO;
92*433d6423SLionel Sambuc 
93*433d6423SLionel Sambuc 	if (!input_dev_active(input_dev))
94*433d6423SLionel Sambuc 		return ENXIO;
95*433d6423SLionel Sambuc 
96*433d6423SLionel Sambuc 	if (input_dev->opened)
97*433d6423SLionel Sambuc 		return EBUSY;
98*433d6423SLionel Sambuc 
99*433d6423SLionel Sambuc 	input_dev->opened = TRUE;
100*433d6423SLionel Sambuc 
101*433d6423SLionel Sambuc 	return OK;
102*433d6423SLionel Sambuc }
103*433d6423SLionel Sambuc 
104*433d6423SLionel Sambuc /*
105*433d6423SLionel Sambuc  * Close an input device.
106*433d6423SLionel Sambuc  */
107*433d6423SLionel Sambuc static int
input_close(devminor_t minor)108*433d6423SLionel Sambuc input_close(devminor_t minor)
109*433d6423SLionel Sambuc {
110*433d6423SLionel Sambuc 	struct input_dev *input_dev;
111*433d6423SLionel Sambuc 
112*433d6423SLionel Sambuc 	if ((input_dev = input_map(minor)) == NULL)
113*433d6423SLionel Sambuc 		return ENXIO;
114*433d6423SLionel Sambuc 
115*433d6423SLionel Sambuc 	if (!input_dev->opened) {
116*433d6423SLionel Sambuc 		printf("INPUT: closing already-closed device %d\n", minor);
117*433d6423SLionel Sambuc 		return EINVAL;
118*433d6423SLionel Sambuc 	}
119*433d6423SLionel Sambuc 
120*433d6423SLionel Sambuc 	input_dev->opened = FALSE;
121*433d6423SLionel Sambuc 	input_dev->tail = 0;
122*433d6423SLionel Sambuc 	input_dev->count = 0;
123*433d6423SLionel Sambuc 
124*433d6423SLionel Sambuc 	return OK;
125*433d6423SLionel Sambuc }
126*433d6423SLionel Sambuc 
127*433d6423SLionel Sambuc /*
128*433d6423SLionel Sambuc  * Copy input events to a reader.
129*433d6423SLionel Sambuc  */
130*433d6423SLionel Sambuc static ssize_t
input_copy_events(endpoint_t endpt,cp_grant_id_t grant,unsigned int event_count,struct input_dev * input_dev)131*433d6423SLionel Sambuc input_copy_events(endpoint_t endpt, cp_grant_id_t grant,
132*433d6423SLionel Sambuc 	unsigned int event_count, struct input_dev *input_dev)
133*433d6423SLionel Sambuc {
134*433d6423SLionel Sambuc 	int r, nbytes, wrap_left;
135*433d6423SLionel Sambuc 	size_t event_size = sizeof(*input_dev->eventbuf);
136*433d6423SLionel Sambuc 
137*433d6423SLionel Sambuc 	if (input_dev->count < event_count)
138*433d6423SLionel Sambuc 		panic("input_copy_events: not enough input is ready");
139*433d6423SLionel Sambuc 
140*433d6423SLionel Sambuc 	wrap_left = input_dev->tail + event_count - EVENTBUF_SIZE;
141*433d6423SLionel Sambuc 	nbytes = (wrap_left <= 0 ? event_count :
142*433d6423SLionel Sambuc 	    EVENTBUF_SIZE - input_dev->tail) * event_size;
143*433d6423SLionel Sambuc 
144*433d6423SLionel Sambuc 	if ((r = sys_safecopyto(endpt, grant, 0,
145*433d6423SLionel Sambuc 	    (vir_bytes)(input_dev->eventbuf + input_dev->tail), nbytes)) != OK)
146*433d6423SLionel Sambuc 		return r;
147*433d6423SLionel Sambuc 
148*433d6423SLionel Sambuc 	/* Copy possible remaining part if we wrap over. */
149*433d6423SLionel Sambuc 	if (wrap_left > 0 && (r = sys_safecopyto(endpt, grant, nbytes,
150*433d6423SLionel Sambuc 	    (vir_bytes) input_dev->eventbuf, wrap_left * event_size)) != OK)
151*433d6423SLionel Sambuc 		return r;
152*433d6423SLionel Sambuc 
153*433d6423SLionel Sambuc 	input_dev->tail = (input_dev->tail + event_count) % EVENTBUF_SIZE;
154*433d6423SLionel Sambuc 	input_dev->count -= event_count;
155*433d6423SLionel Sambuc 
156*433d6423SLionel Sambuc 	return event_size * event_count; /* bytes copied */
157*433d6423SLionel Sambuc }
158*433d6423SLionel Sambuc 
159*433d6423SLionel Sambuc /*
160*433d6423SLionel Sambuc  * Read from an input device.
161*433d6423SLionel Sambuc  */
162*433d6423SLionel Sambuc static ssize_t
input_read(devminor_t minor,u64_t UNUSED (position),endpoint_t endpt,cp_grant_id_t grant,size_t size,int flags,cdev_id_t id)163*433d6423SLionel Sambuc input_read(devminor_t minor, u64_t UNUSED(position), endpoint_t endpt,
164*433d6423SLionel Sambuc 	cp_grant_id_t grant, size_t size, int flags, cdev_id_t id)
165*433d6423SLionel Sambuc {
166*433d6423SLionel Sambuc 	unsigned int event_count;
167*433d6423SLionel Sambuc 	struct input_dev *input_dev;
168*433d6423SLionel Sambuc 
169*433d6423SLionel Sambuc 	if ((input_dev = input_map(minor)) == NULL)
170*433d6423SLionel Sambuc 		return ENXIO;
171*433d6423SLionel Sambuc 
172*433d6423SLionel Sambuc 	/* We cannot accept more than one pending read request at once. */
173*433d6423SLionel Sambuc 	if (!input_dev_active(input_dev) || input_dev->suspended)
174*433d6423SLionel Sambuc 		return EIO;
175*433d6423SLionel Sambuc 
176*433d6423SLionel Sambuc 	/* The caller's buffer must have room for at least one whole event. */
177*433d6423SLionel Sambuc 	event_count = size / sizeof(*input_dev->eventbuf);
178*433d6423SLionel Sambuc 	if (event_count == 0)
179*433d6423SLionel Sambuc 		return EIO;
180*433d6423SLionel Sambuc 
181*433d6423SLionel Sambuc 	/* No data available? Suspend the caller, unless we shouldn't block. */
182*433d6423SLionel Sambuc 	if (input_dev_buf_empty(input_dev)) {
183*433d6423SLionel Sambuc 		if (flags & CDEV_NONBLOCK)
184*433d6423SLionel Sambuc 			return EAGAIN;
185*433d6423SLionel Sambuc 
186*433d6423SLionel Sambuc 		input_dev->suspended = TRUE;
187*433d6423SLionel Sambuc 		input_dev->caller = endpt;
188*433d6423SLionel Sambuc 		input_dev->grant = grant;
189*433d6423SLionel Sambuc 		input_dev->req_id = id;
190*433d6423SLionel Sambuc 
191*433d6423SLionel Sambuc 		/* We should now wake up any selector, but that's lame.. */
192*433d6423SLionel Sambuc 		return EDONTREPLY;
193*433d6423SLionel Sambuc 	}
194*433d6423SLionel Sambuc 
195*433d6423SLionel Sambuc 	if (event_count > input_dev->count)
196*433d6423SLionel Sambuc 		event_count = input_dev->count;
197*433d6423SLionel Sambuc 
198*433d6423SLionel Sambuc 	return input_copy_events(endpt, grant, event_count, input_dev);
199*433d6423SLionel Sambuc }
200*433d6423SLionel Sambuc 
201*433d6423SLionel Sambuc /*
202*433d6423SLionel Sambuc  * Set keyboard LEDs on one or all keyboards.
203*433d6423SLionel Sambuc  */
204*433d6423SLionel Sambuc static void
input_set_leds(devminor_t minor,unsigned int mask)205*433d6423SLionel Sambuc input_set_leds(devminor_t minor, unsigned int mask)
206*433d6423SLionel Sambuc {
207*433d6423SLionel Sambuc 	struct input_dev *dev;
208*433d6423SLionel Sambuc 	message m;
209*433d6423SLionel Sambuc 	int i, r;
210*433d6423SLionel Sambuc 
211*433d6423SLionel Sambuc 	/* Prepare the request message */
212*433d6423SLionel Sambuc 	memset(&m, 0, sizeof(m));
213*433d6423SLionel Sambuc 
214*433d6423SLionel Sambuc 	m.m_type = INPUT_SETLEDS;
215*433d6423SLionel Sambuc 	m.m_input_linputdriver_setleds.led_mask = mask;
216*433d6423SLionel Sambuc 
217*433d6423SLionel Sambuc 	/*
218*433d6423SLionel Sambuc 	 * Send the request to all matching keyboard devices.  As side effect,
219*433d6423SLionel Sambuc 	 * this approach discards the request on mouse devices.
220*433d6423SLionel Sambuc 	 */
221*433d6423SLionel Sambuc 	for (i = FIRST_KBD_DEV; i <= LAST_KBD_DEV; i++) {
222*433d6423SLionel Sambuc 		dev = &devs[i];
223*433d6423SLionel Sambuc 
224*433d6423SLionel Sambuc 		if (minor != KBDMUX_MINOR && minor != dev->minor)
225*433d6423SLionel Sambuc 			continue;
226*433d6423SLionel Sambuc 
227*433d6423SLionel Sambuc 		/* Save the new state; the driver might (re)start later. */
228*433d6423SLionel Sambuc 		dev->leds = mask;
229*433d6423SLionel Sambuc 
230*433d6423SLionel Sambuc 		if (dev->owner != NONE) {
231*433d6423SLionel Sambuc 			if ((r = asynsend3(dev->owner, &m, AMF_NOREPLY)) != OK)
232*433d6423SLionel Sambuc 				printf("INPUT: asynsend to %u failed (%d)\n",
233*433d6423SLionel Sambuc 				    dev->owner, r);
234*433d6423SLionel Sambuc 		}
235*433d6423SLionel Sambuc 	}
236*433d6423SLionel Sambuc }
237*433d6423SLionel Sambuc 
238*433d6423SLionel Sambuc /*
239*433d6423SLionel Sambuc  * Process an IOCTL request.
240*433d6423SLionel Sambuc  */
241*433d6423SLionel Sambuc static int
input_ioctl(devminor_t minor,unsigned long request,endpoint_t endpt,cp_grant_id_t grant,int flags,endpoint_t user_endpt,cdev_id_t id)242*433d6423SLionel Sambuc input_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
243*433d6423SLionel Sambuc 	cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id)
244*433d6423SLionel Sambuc {
245*433d6423SLionel Sambuc 	struct input_dev *input_dev;
246*433d6423SLionel Sambuc 	kio_leds_t leds;
247*433d6423SLionel Sambuc 	unsigned int mask;
248*433d6423SLionel Sambuc 	int r;
249*433d6423SLionel Sambuc 
250*433d6423SLionel Sambuc 	if ((input_dev = input_map(minor)) == NULL)
251*433d6423SLionel Sambuc 		return ENXIO;
252*433d6423SLionel Sambuc 
253*433d6423SLionel Sambuc 	if (!input_dev_active(input_dev))
254*433d6423SLionel Sambuc 		return EIO;
255*433d6423SLionel Sambuc 
256*433d6423SLionel Sambuc 	switch (request) {
257*433d6423SLionel Sambuc 	case KIOCSLEDS:
258*433d6423SLionel Sambuc 		if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &leds,
259*433d6423SLionel Sambuc 		    sizeof(leds))) != OK)
260*433d6423SLionel Sambuc 			return r;
261*433d6423SLionel Sambuc 
262*433d6423SLionel Sambuc 		mask = 0;
263*433d6423SLionel Sambuc 		if (leds.kl_bits & KBD_LEDS_NUM)
264*433d6423SLionel Sambuc 			mask |= (1 << INPUT_LED_NUMLOCK);
265*433d6423SLionel Sambuc 		if (leds.kl_bits & KBD_LEDS_CAPS)
266*433d6423SLionel Sambuc 			mask |= (1 << INPUT_LED_CAPSLOCK);
267*433d6423SLionel Sambuc 		if (leds.kl_bits & KBD_LEDS_SCROLL)
268*433d6423SLionel Sambuc 			mask |= (1 << INPUT_LED_SCROLLLOCK);
269*433d6423SLionel Sambuc 
270*433d6423SLionel Sambuc 		input_set_leds(minor, mask);
271*433d6423SLionel Sambuc 
272*433d6423SLionel Sambuc 		return OK;
273*433d6423SLionel Sambuc 
274*433d6423SLionel Sambuc 	default:
275*433d6423SLionel Sambuc 		return ENOTTY;
276*433d6423SLionel Sambuc 	}
277*433d6423SLionel Sambuc }
278*433d6423SLionel Sambuc 
279*433d6423SLionel Sambuc /*
280*433d6423SLionel Sambuc  * Cancel a suspended read request.
281*433d6423SLionel Sambuc  */
282*433d6423SLionel Sambuc static int
input_cancel(devminor_t minor,endpoint_t endpt,cdev_id_t id)283*433d6423SLionel Sambuc input_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id)
284*433d6423SLionel Sambuc {
285*433d6423SLionel Sambuc 	struct input_dev *input_dev;
286*433d6423SLionel Sambuc 
287*433d6423SLionel Sambuc 	if ((input_dev = input_map(minor)) == NULL)
288*433d6423SLionel Sambuc 		return ENXIO;
289*433d6423SLionel Sambuc 
290*433d6423SLionel Sambuc 	if (input_dev->suspended && input_dev->caller == endpt &&
291*433d6423SLionel Sambuc 	    input_dev->req_id == id) {
292*433d6423SLionel Sambuc 		input_dev->suspended = FALSE;
293*433d6423SLionel Sambuc 
294*433d6423SLionel Sambuc 		return EINTR;
295*433d6423SLionel Sambuc 	}
296*433d6423SLionel Sambuc 
297*433d6423SLionel Sambuc 	return EDONTREPLY;
298*433d6423SLionel Sambuc }
299*433d6423SLionel Sambuc 
300*433d6423SLionel Sambuc /*
301*433d6423SLionel Sambuc  * Perform a select call on an input device.
302*433d6423SLionel Sambuc  */
303*433d6423SLionel Sambuc static int
input_select(devminor_t minor,unsigned int ops,endpoint_t endpt)304*433d6423SLionel Sambuc input_select(devminor_t minor, unsigned int ops, endpoint_t endpt)
305*433d6423SLionel Sambuc {
306*433d6423SLionel Sambuc 	struct input_dev *input_dev;
307*433d6423SLionel Sambuc 	int ready_ops;
308*433d6423SLionel Sambuc 
309*433d6423SLionel Sambuc 	if ((input_dev = input_map(minor)) == NULL)
310*433d6423SLionel Sambuc 		return ENXIO;
311*433d6423SLionel Sambuc 
312*433d6423SLionel Sambuc 	ready_ops = 0;
313*433d6423SLionel Sambuc 
314*433d6423SLionel Sambuc 	if (ops & CDEV_OP_RD) {
315*433d6423SLionel Sambuc 		if (!input_dev_active(input_dev) || input_dev->suspended)
316*433d6423SLionel Sambuc 			ready_ops |= CDEV_OP_RD;	/* immediate error */
317*433d6423SLionel Sambuc 		else if (!input_dev_buf_empty(input_dev))
318*433d6423SLionel Sambuc 			ready_ops |= CDEV_OP_RD;	/* data available */
319*433d6423SLionel Sambuc 		else if (ops & CDEV_NOTIFY)
320*433d6423SLionel Sambuc 			input_dev->selector = endpt;	/* report later */
321*433d6423SLionel Sambuc 	}
322*433d6423SLionel Sambuc 
323*433d6423SLionel Sambuc 	if (ops & CDEV_OP_WR) ready_ops |= CDEV_OP_WR;	/* immediate error */
324*433d6423SLionel Sambuc 
325*433d6423SLionel Sambuc 	return ready_ops;
326*433d6423SLionel Sambuc }
327*433d6423SLionel Sambuc 
328*433d6423SLionel Sambuc /*
329*433d6423SLionel Sambuc  * An input device receives an input event.  Enqueue it, and possibly unsuspend
330*433d6423SLionel Sambuc  * a read request or wake up a selector.
331*433d6423SLionel Sambuc  */
332*433d6423SLionel Sambuc static void
input_process(struct input_dev * input_dev,const message * m)333*433d6423SLionel Sambuc input_process(struct input_dev *input_dev, const message *m)
334*433d6423SLionel Sambuc {
335*433d6423SLionel Sambuc 	unsigned int next;
336*433d6423SLionel Sambuc 	int r;
337*433d6423SLionel Sambuc 
338*433d6423SLionel Sambuc 	if (input_dev_buf_full(input_dev)) {
339*433d6423SLionel Sambuc 		/* Overflow.  Overwrite the oldest event. */
340*433d6423SLionel Sambuc 		input_dev->tail = (input_dev->tail + 1) % EVENTBUF_SIZE;
341*433d6423SLionel Sambuc 		input_dev->count--;
342*433d6423SLionel Sambuc 
343*433d6423SLionel Sambuc #if INPUT_DEBUG
344*433d6423SLionel Sambuc 		printf("INPUT: overflow on device %u\n", input_dev - devs);
345*433d6423SLionel Sambuc #endif
346*433d6423SLionel Sambuc 	}
347*433d6423SLionel Sambuc 	next = (input_dev->tail + input_dev->count) % EVENTBUF_SIZE;
348*433d6423SLionel Sambuc 	input_dev->eventbuf[next].page = m->m_linputdriver_input_event.page;
349*433d6423SLionel Sambuc 	input_dev->eventbuf[next].code = m->m_linputdriver_input_event.code;
350*433d6423SLionel Sambuc 	input_dev->eventbuf[next].value = m->m_linputdriver_input_event.value;
351*433d6423SLionel Sambuc 	input_dev->eventbuf[next].flags = m->m_linputdriver_input_event.flags;
352*433d6423SLionel Sambuc 	input_dev->eventbuf[next].devid = m->m_linputdriver_input_event.id;
353*433d6423SLionel Sambuc 	input_dev->eventbuf[next].rsvd[0] = 0;
354*433d6423SLionel Sambuc 	input_dev->eventbuf[next].rsvd[1] = 0;
355*433d6423SLionel Sambuc 	input_dev->count++;
356*433d6423SLionel Sambuc 
357*433d6423SLionel Sambuc 	/*
358*433d6423SLionel Sambuc 	 * There is new input.  Revive a suspended reader if there was one.
359*433d6423SLionel Sambuc 	 * Otherwise see if we should reply to a select query.
360*433d6423SLionel Sambuc 	 */
361*433d6423SLionel Sambuc 	if (input_dev->suspended) {
362*433d6423SLionel Sambuc 		r = input_copy_events(input_dev->caller, input_dev->grant, 1,
363*433d6423SLionel Sambuc 		    input_dev);
364*433d6423SLionel Sambuc 		chardriver_reply_task(input_dev->caller, input_dev->req_id, r);
365*433d6423SLionel Sambuc 		input_dev->suspended = FALSE;
366*433d6423SLionel Sambuc 	} else if (input_dev->selector != NONE) {
367*433d6423SLionel Sambuc 		chardriver_reply_select(input_dev->selector, input_dev->minor,
368*433d6423SLionel Sambuc 		    CDEV_OP_RD);
369*433d6423SLionel Sambuc 		input_dev->selector = NONE;
370*433d6423SLionel Sambuc 	}
371*433d6423SLionel Sambuc }
372*433d6423SLionel Sambuc 
373*433d6423SLionel Sambuc /*
374*433d6423SLionel Sambuc  * An input event has arrived from a driver.
375*433d6423SLionel Sambuc  */
376*433d6423SLionel Sambuc static void
input_event(message * m)377*433d6423SLionel Sambuc input_event(message *m)
378*433d6423SLionel Sambuc {
379*433d6423SLionel Sambuc 	struct input_dev *input_dev, *mux_dev;
380*433d6423SLionel Sambuc 	int r, id;
381*433d6423SLionel Sambuc 
382*433d6423SLionel Sambuc 	/* Unlike minor numbers, device IDs are in fact array indices. */
383*433d6423SLionel Sambuc 	id = m->m_linputdriver_input_event.id;
384*433d6423SLionel Sambuc 	if (id < 0 || id >= INPUT_DEV_MAX)
385*433d6423SLionel Sambuc 		return;
386*433d6423SLionel Sambuc 
387*433d6423SLionel Sambuc 	/* The sender must owner the device. */
388*433d6423SLionel Sambuc 	input_dev = &devs[id];
389*433d6423SLionel Sambuc 	if (input_dev->owner != m->m_source)
390*433d6423SLionel Sambuc 		return;
391*433d6423SLionel Sambuc 
392*433d6423SLionel Sambuc 	/* Input events are also delivered to the respective multiplexer. */
393*433d6423SLionel Sambuc 	if (input_dev->minor >= KBD0_MINOR &&
394*433d6423SLionel Sambuc 	    input_dev->minor < KBD0_MINOR + KBD_MINORS)
395*433d6423SLionel Sambuc 		mux_dev = &devs[KBDMUX_DEV];
396*433d6423SLionel Sambuc 	else
397*433d6423SLionel Sambuc 		mux_dev = &devs[MOUSEMUX_DEV];
398*433d6423SLionel Sambuc 
399*433d6423SLionel Sambuc 	/*
400*433d6423SLionel Sambuc 	 * Try to deliver the event to the input device or otherwise the
401*433d6423SLionel Sambuc 	 * corresponding multiplexer.  If neither are opened, forward the event
402*433d6423SLionel Sambuc 	 * to TTY.
403*433d6423SLionel Sambuc 	 */
404*433d6423SLionel Sambuc 	if (input_dev->opened)
405*433d6423SLionel Sambuc 		input_process(input_dev, m);
406*433d6423SLionel Sambuc 	else if (mux_dev->opened)
407*433d6423SLionel Sambuc 		input_process(mux_dev, m);
408*433d6423SLionel Sambuc 	else {
409*433d6423SLionel Sambuc 		message fwd;
410*433d6423SLionel Sambuc 		mess_input_tty_event *tty_event = &(fwd.m_input_tty_event);
411*433d6423SLionel Sambuc 
412*433d6423SLionel Sambuc 		fwd.m_type = TTY_INPUT_EVENT;
413*433d6423SLionel Sambuc 		tty_event->id = m->m_linputdriver_input_event.id;
414*433d6423SLionel Sambuc 		tty_event->page = m->m_linputdriver_input_event.page;
415*433d6423SLionel Sambuc 		tty_event->code = m->m_linputdriver_input_event.code;
416*433d6423SLionel Sambuc 		tty_event->value = m->m_linputdriver_input_event.value;
417*433d6423SLionel Sambuc 		tty_event->flags = m->m_linputdriver_input_event.flags;
418*433d6423SLionel Sambuc 
419*433d6423SLionel Sambuc 		if ((r = ipc_send(TTY_PROC_NR, &fwd)) != OK)
420*433d6423SLionel Sambuc 			printf("INPUT: send to TTY failed (%d)\n", r);
421*433d6423SLionel Sambuc 	}
422*433d6423SLionel Sambuc }
423*433d6423SLionel Sambuc 
424*433d6423SLionel Sambuc /*
425*433d6423SLionel Sambuc  * Allocate a device structure for an input driver of the given type, and
426*433d6423SLionel Sambuc  * return its ID.  If the given label already owns a device ID of the right
427*433d6423SLionel Sambuc  * type, update that entry instead.  If no device ID could be allocated, return
428*433d6423SLionel Sambuc  * INVALID_INPUT_ID.
429*433d6423SLionel Sambuc  */
430*433d6423SLionel Sambuc static int
input_alloc_id(int mouse,endpoint_t owner,const char * label)431*433d6423SLionel Sambuc input_alloc_id(int mouse, endpoint_t owner, const char *label)
432*433d6423SLionel Sambuc {
433*433d6423SLionel Sambuc 	int n, id, start, end;
434*433d6423SLionel Sambuc 
435*433d6423SLionel Sambuc 	if (!mouse) {
436*433d6423SLionel Sambuc 		start = FIRST_KBD_DEV;
437*433d6423SLionel Sambuc 		end = LAST_KBD_DEV;
438*433d6423SLionel Sambuc 	} else {
439*433d6423SLionel Sambuc 		start = FIRST_MOUSE_DEV;
440*433d6423SLionel Sambuc 		end = LAST_MOUSE_DEV;
441*433d6423SLionel Sambuc 	}
442*433d6423SLionel Sambuc 
443*433d6423SLionel Sambuc 	id = INVALID_INPUT_ID;
444*433d6423SLionel Sambuc 	for (n = start; n <= end; n++) {
445*433d6423SLionel Sambuc 		if (devs[n].owner != NONE) {
446*433d6423SLionel Sambuc 			if (!strcmp(devs[n].label, label)) {
447*433d6423SLionel Sambuc 				devs[n].owner = owner;
448*433d6423SLionel Sambuc 				return n;
449*433d6423SLionel Sambuc 			}
450*433d6423SLionel Sambuc 		/* Do not allocate the ID of a disconnected but open device. */
451*433d6423SLionel Sambuc 		} else if (!devs[n].opened && id == INVALID_INPUT_ID) {
452*433d6423SLionel Sambuc 			id = n;
453*433d6423SLionel Sambuc 		}
454*433d6423SLionel Sambuc 	}
455*433d6423SLionel Sambuc 
456*433d6423SLionel Sambuc 	if (id != INVALID_INPUT_ID) {
457*433d6423SLionel Sambuc 		devs[id].owner = owner;
458*433d6423SLionel Sambuc 		strlcpy(devs[id].label, label, sizeof(devs[id].label));
459*433d6423SLionel Sambuc 
460*433d6423SLionel Sambuc #if INPUT_DEBUG
461*433d6423SLionel Sambuc 		printf("INPUT: connected device %u to %u (%s)\n", id,
462*433d6423SLionel Sambuc 		    owner, label);
463*433d6423SLionel Sambuc #endif
464*433d6423SLionel Sambuc 	} else {
465*433d6423SLionel Sambuc 		printf("INPUT: out of %s slots for new driver %d\n",
466*433d6423SLionel Sambuc 		    mouse ? "mouse" : "keyboard", owner);
467*433d6423SLionel Sambuc 	}
468*433d6423SLionel Sambuc 
469*433d6423SLionel Sambuc 	return id;
470*433d6423SLionel Sambuc }
471*433d6423SLionel Sambuc 
472*433d6423SLionel Sambuc /*
473*433d6423SLionel Sambuc  * Register keyboard and/or a mouse devices for a driver.
474*433d6423SLionel Sambuc  */
475*433d6423SLionel Sambuc static void
input_connect(endpoint_t owner,char * labelp,int typemask)476*433d6423SLionel Sambuc input_connect(endpoint_t owner, char *labelp, int typemask)
477*433d6423SLionel Sambuc {
478*433d6423SLionel Sambuc 	message m;
479*433d6423SLionel Sambuc 	char label[DS_MAX_KEYLEN];
480*433d6423SLionel Sambuc 	int r, kbd_id, mouse_id;
481*433d6423SLionel Sambuc 
482*433d6423SLionel Sambuc #if INPUT_DEBUG
483*433d6423SLionel Sambuc 	printf("INPUT: connect request from %u (%s) for mask %x\n", owner,
484*433d6423SLionel Sambuc 	    labelp, typemask);
485*433d6423SLionel Sambuc #endif
486*433d6423SLionel Sambuc 
487*433d6423SLionel Sambuc 	/* Check the driver's label. */
488*433d6423SLionel Sambuc 	if ((r = ds_retrieve_label_name(label, owner)) != OK) {
489*433d6423SLionel Sambuc 		printf("INPUT: unable to get label for %u: %d\n", owner, r);
490*433d6423SLionel Sambuc 		return;
491*433d6423SLionel Sambuc 	}
492*433d6423SLionel Sambuc 	if (strcmp(label, labelp)) {
493*433d6423SLionel Sambuc 		printf("INPUT: ignoring driver %s label %s\n", label, labelp);
494*433d6423SLionel Sambuc 		return;
495*433d6423SLionel Sambuc 	}
496*433d6423SLionel Sambuc 
497*433d6423SLionel Sambuc 	kbd_id = INVALID_INPUT_ID;
498*433d6423SLionel Sambuc 	mouse_id = INVALID_INPUT_ID;
499*433d6423SLionel Sambuc 
500*433d6423SLionel Sambuc 	/*
501*433d6423SLionel Sambuc 	 * We ignore allocation failures here, thus possibly sending invalid
502*433d6423SLionel Sambuc 	 * IDs to the driver even for either or both the devices types it
503*433d6423SLionel Sambuc 	 * requested.  As a result, the driver will not send us input for these
504*433d6423SLionel Sambuc 	 * device types, possibly effectively disabling the driver altogether.
505*433d6423SLionel Sambuc 	 * Theoretically we could still admit events to the multiplexers for
506*433d6423SLionel Sambuc 	 * such drivers, but that would lead to unexpected behavior with
507*433d6423SLionel Sambuc 	 * respect to keyboard LEDs, for example.
508*433d6423SLionel Sambuc 	 */
509*433d6423SLionel Sambuc 	if (typemask & INPUT_DEV_KBD)
510*433d6423SLionel Sambuc 		kbd_id = input_alloc_id(FALSE /*mouse*/, owner, label);
511*433d6423SLionel Sambuc 	if (typemask & INPUT_DEV_MOUSE)
512*433d6423SLionel Sambuc 		mouse_id = input_alloc_id(TRUE /*mouse*/, owner, label);
513*433d6423SLionel Sambuc 
514*433d6423SLionel Sambuc 	memset(&m, 0, sizeof(m));
515*433d6423SLionel Sambuc 
516*433d6423SLionel Sambuc 	m.m_type = INPUT_CONF;
517*433d6423SLionel Sambuc 	m.m_input_linputdriver_input_conf.kbd_id = kbd_id;
518*433d6423SLionel Sambuc 	m.m_input_linputdriver_input_conf.mouse_id = mouse_id;
519*433d6423SLionel Sambuc 	m.m_input_linputdriver_input_conf.rsvd1_id = INVALID_INPUT_ID;	/* reserved (joystick?) */
520*433d6423SLionel Sambuc 	m.m_input_linputdriver_input_conf.rsvd2_id = INVALID_INPUT_ID;	/* reserved for future use */
521*433d6423SLionel Sambuc 
522*433d6423SLionel Sambuc 	if ((r = asynsend3(owner, &m, AMF_NOREPLY)) != OK)
523*433d6423SLionel Sambuc 		printf("INPUT: asynsend to %u failed (%d)\n", owner, r);
524*433d6423SLionel Sambuc 
525*433d6423SLionel Sambuc 	/* If a keyboard was registered, also set its initial LED state. */
526*433d6423SLionel Sambuc 	if (kbd_id != INVALID_INPUT_ID)
527*433d6423SLionel Sambuc 		input_set_leds(devs[kbd_id].minor, devs[kbd_id].leds);
528*433d6423SLionel Sambuc }
529*433d6423SLionel Sambuc 
530*433d6423SLionel Sambuc /*
531*433d6423SLionel Sambuc  * Disconnect a device.
532*433d6423SLionel Sambuc  */
533*433d6423SLionel Sambuc static void
input_disconnect(struct input_dev * input_dev)534*433d6423SLionel Sambuc input_disconnect(struct input_dev *input_dev)
535*433d6423SLionel Sambuc {
536*433d6423SLionel Sambuc #if INPUT_DEBUG
537*433d6423SLionel Sambuc 	printf("INPUT: disconnected device %u\n", input_dev - devs);
538*433d6423SLionel Sambuc #endif
539*433d6423SLionel Sambuc 
540*433d6423SLionel Sambuc 	if (input_dev->suspended) {
541*433d6423SLionel Sambuc 		chardriver_reply_task(input_dev->caller, input_dev->req_id,
542*433d6423SLionel Sambuc 		    EIO);
543*433d6423SLionel Sambuc 		input_dev->suspended = FALSE;
544*433d6423SLionel Sambuc 	}
545*433d6423SLionel Sambuc 
546*433d6423SLionel Sambuc 	if (input_dev->selector != NONE) {
547*433d6423SLionel Sambuc 		chardriver_reply_select(input_dev->selector, input_dev->minor,
548*433d6423SLionel Sambuc 		    CDEV_OP_RD);
549*433d6423SLionel Sambuc 		input_dev->selector = NONE;
550*433d6423SLionel Sambuc 	}
551*433d6423SLionel Sambuc 
552*433d6423SLionel Sambuc 	input_dev->owner = NONE;
553*433d6423SLionel Sambuc }
554*433d6423SLionel Sambuc 
555*433d6423SLionel Sambuc /*
556*433d6423SLionel Sambuc  * Check for driver status changes in the data store.
557*433d6423SLionel Sambuc  */
558*433d6423SLionel Sambuc static void
input_check(void)559*433d6423SLionel Sambuc input_check(void)
560*433d6423SLionel Sambuc {
561*433d6423SLionel Sambuc 	char key[DS_MAX_KEYLEN], *label;
562*433d6423SLionel Sambuc 	const char *driver_prefix = "drv.inp.";
563*433d6423SLionel Sambuc 	u32_t value;
564*433d6423SLionel Sambuc 	size_t len;
565*433d6423SLionel Sambuc 	int i, r, type;
566*433d6423SLionel Sambuc 	endpoint_t owner;
567*433d6423SLionel Sambuc 
568*433d6423SLionel Sambuc 	len = strlen(driver_prefix);
569*433d6423SLionel Sambuc 
570*433d6423SLionel Sambuc 	/* Check for new (input driver) entries. */
571*433d6423SLionel Sambuc 	while (ds_check(key, &type, &owner) == OK) {
572*433d6423SLionel Sambuc 		if ((r = ds_retrieve_u32(key, &value)) != OK) {
573*433d6423SLionel Sambuc 			printf("INPUT: ds_retrieve_u32 failed (%d)\n", r);
574*433d6423SLionel Sambuc 			continue;
575*433d6423SLionel Sambuc 		}
576*433d6423SLionel Sambuc 
577*433d6423SLionel Sambuc 		/* Only check for input driver registration events. */
578*433d6423SLionel Sambuc 		if (strncmp(key, driver_prefix, len))
579*433d6423SLionel Sambuc 			continue;
580*433d6423SLionel Sambuc 
581*433d6423SLionel Sambuc 		/* The prefix is followed by the driver's own label. */
582*433d6423SLionel Sambuc 		label = &key[len];
583*433d6423SLionel Sambuc 
584*433d6423SLionel Sambuc 		input_connect(owner, label, value);
585*433d6423SLionel Sambuc 	}
586*433d6423SLionel Sambuc 
587*433d6423SLionel Sambuc 	/* Check for removed (label) entries. */
588*433d6423SLionel Sambuc 	for (i = 0; i < INPUT_DEV_MAX; i++) {
589*433d6423SLionel Sambuc 		/* This also skips the multiplexers. */
590*433d6423SLionel Sambuc 		if (devs[i].owner == NONE)
591*433d6423SLionel Sambuc 			continue;
592*433d6423SLionel Sambuc 
593*433d6423SLionel Sambuc 		r = ds_retrieve_label_endpt(devs[i].label, &owner);
594*433d6423SLionel Sambuc 
595*433d6423SLionel Sambuc 		if (r == OK)
596*433d6423SLionel Sambuc 			devs[i].owner = owner;	/* not really necessary */
597*433d6423SLionel Sambuc 		else if (r == ESRCH)
598*433d6423SLionel Sambuc 			input_disconnect(&devs[i]);
599*433d6423SLionel Sambuc 		else
600*433d6423SLionel Sambuc 			printf("INPUT: ds_retrieve_label_endpt failed (%d)\n",
601*433d6423SLionel Sambuc 			    r);
602*433d6423SLionel Sambuc 	}
603*433d6423SLionel Sambuc }
604*433d6423SLionel Sambuc 
605*433d6423SLionel Sambuc /*
606*433d6423SLionel Sambuc  * Process messages not part of the character driver protocol.
607*433d6423SLionel Sambuc  */
608*433d6423SLionel Sambuc static void
input_other(message * m,int ipc_status)609*433d6423SLionel Sambuc input_other(message *m, int ipc_status)
610*433d6423SLionel Sambuc {
611*433d6423SLionel Sambuc 	if (is_ipc_notify(ipc_status)) {
612*433d6423SLionel Sambuc 		switch (m->m_source) {
613*433d6423SLionel Sambuc 		case DS_PROC_NR:
614*433d6423SLionel Sambuc 			input_check();
615*433d6423SLionel Sambuc 			break;
616*433d6423SLionel Sambuc 		default:
617*433d6423SLionel Sambuc 			printf("INPUT: unexpected notify from %d\n",
618*433d6423SLionel Sambuc 			    m->m_source);
619*433d6423SLionel Sambuc 		}
620*433d6423SLionel Sambuc 		return;
621*433d6423SLionel Sambuc 	}
622*433d6423SLionel Sambuc 
623*433d6423SLionel Sambuc 	/* An input event from a registered driver. */
624*433d6423SLionel Sambuc 	switch (m->m_type) {
625*433d6423SLionel Sambuc 	case INPUT_EVENT:
626*433d6423SLionel Sambuc 		input_event(m);
627*433d6423SLionel Sambuc 
628*433d6423SLionel Sambuc 		break;
629*433d6423SLionel Sambuc 
630*433d6423SLionel Sambuc 	case INPUT_SETLEDS:
631*433d6423SLionel Sambuc 		if (m->m_source == TTY_PROC_NR) {
632*433d6423SLionel Sambuc 			input_set_leds(KBDMUX_MINOR, m->m_input_linputdriver_setleds.led_mask);
633*433d6423SLionel Sambuc 
634*433d6423SLionel Sambuc 			break;
635*433d6423SLionel Sambuc 		}
636*433d6423SLionel Sambuc 		/* FALLTHROUGH */
637*433d6423SLionel Sambuc 	default:
638*433d6423SLionel Sambuc 		printf("INPUT: unexpected message %d from %d\n",
639*433d6423SLionel Sambuc 		    m->m_type, m->m_source);
640*433d6423SLionel Sambuc 	}
641*433d6423SLionel Sambuc }
642*433d6423SLionel Sambuc 
643*433d6423SLionel Sambuc /*
644*433d6423SLionel Sambuc  * Initialize the input server.
645*433d6423SLionel Sambuc  */
646*433d6423SLionel Sambuc static int
input_init(int UNUSED (type),sef_init_info_t * UNUSED (info))647*433d6423SLionel Sambuc input_init(int UNUSED(type), sef_init_info_t *UNUSED(info))
648*433d6423SLionel Sambuc {
649*433d6423SLionel Sambuc 	message m;
650*433d6423SLionel Sambuc 	int i, r;
651*433d6423SLionel Sambuc 
652*433d6423SLionel Sambuc 	/* Initialize input device structures. */
653*433d6423SLionel Sambuc 	for (i = 0; i < INPUT_DEV_MAX; i++) {
654*433d6423SLionel Sambuc 		devs[i].minor = input_revmap(i);
655*433d6423SLionel Sambuc 		devs[i].owner = NONE;
656*433d6423SLionel Sambuc 		devs[i].tail = 0;
657*433d6423SLionel Sambuc 		devs[i].count = 0;
658*433d6423SLionel Sambuc 		devs[i].opened = FALSE;
659*433d6423SLionel Sambuc 		devs[i].suspended = FALSE;
660*433d6423SLionel Sambuc 		devs[i].selector = NONE;
661*433d6423SLionel Sambuc 		devs[i].leds = 0;
662*433d6423SLionel Sambuc 	}
663*433d6423SLionel Sambuc 
664*433d6423SLionel Sambuc 	/* Subscribe to driver registration events for input drivers. */
665*433d6423SLionel Sambuc 	if ((r = ds_subscribe("drv\\.inp\\..*", DSF_INITIAL)) != OK)
666*433d6423SLionel Sambuc 		panic("INPUT: can't subscribe to driver events (%d)", r);
667*433d6423SLionel Sambuc 
668*433d6423SLionel Sambuc 	/* Announce our presence to VFS. */
669*433d6423SLionel Sambuc 	chardriver_announce();
670*433d6423SLionel Sambuc 
671*433d6423SLionel Sambuc 	/* Announce our presence to TTY. */
672*433d6423SLionel Sambuc 	memset(&m, 0, sizeof(m));
673*433d6423SLionel Sambuc 
674*433d6423SLionel Sambuc 	m.m_type = TTY_INPUT_UP;
675*433d6423SLionel Sambuc 
676*433d6423SLionel Sambuc 	if ((r = ipc_send(TTY_PROC_NR, &m)) != OK)
677*433d6423SLionel Sambuc 		printf("INPUT: send to TTY failed (%d)\n", r);
678*433d6423SLionel Sambuc 
679*433d6423SLionel Sambuc 	return OK;
680*433d6423SLionel Sambuc }
681*433d6423SLionel Sambuc 
682*433d6423SLionel Sambuc /*
683*433d6423SLionel Sambuc  * Set callbacks and invoke SEF startup.
684*433d6423SLionel Sambuc  */
685*433d6423SLionel Sambuc static void
input_startup(void)686*433d6423SLionel Sambuc input_startup(void)
687*433d6423SLionel Sambuc {
688*433d6423SLionel Sambuc 	sef_setcb_init_fresh(input_init);
689*433d6423SLionel Sambuc 
690*433d6423SLionel Sambuc 	sef_startup();
691*433d6423SLionel Sambuc }
692*433d6423SLionel Sambuc 
693*433d6423SLionel Sambuc /*
694*433d6423SLionel Sambuc  * Main program of the input server.
695*433d6423SLionel Sambuc  */
696*433d6423SLionel Sambuc int
main(void)697*433d6423SLionel Sambuc main(void)
698*433d6423SLionel Sambuc {
699*433d6423SLionel Sambuc 	input_startup();
700*433d6423SLionel Sambuc 
701*433d6423SLionel Sambuc 	chardriver_task(&input_tab);
702*433d6423SLionel Sambuc 
703*433d6423SLionel Sambuc 	return 0;
704*433d6423SLionel Sambuc }
705