1/*
2 * Copyright 2005-2007 Haiku, Inc.
3 * Distributed under the terms of the MIT License.
4 *
5 * PS/2 bus manager
6 *
7 * Authors (in chronological order):
8 *		Marcus Overhagen (marcus@overhagen.de)
9 */
10
11
12#include "ps2_service.h"
13
14#include "packet_buffer.h"
15
16
17#define DEBUG_PUBLISHING
18#ifdef DEBUG_PUBLISHING
19#	include <stdlib.h>
20#	include <debug.h>
21#endif
22
23
24//#define TRACE_PS2_SERVICE
25#ifdef TRACE_PS2_SERVICE
26#	define TRACE(x...) dprintf(x)
27#else
28#	define TRACE(x...)
29#endif
30
31
32typedef struct {
33	uint32		id;
34	ps2_dev *	dev;
35} ps2_service_cmd;
36
37enum {
38	PS2_SERVICE_NOTIFY_DEVICE_ADDED = 1,
39	PS2_SERVICE_NOTIFY_DEVICE_REPUBLISH,
40	PS2_SERVICE_NOTIFY_DEVICE_REMOVED,
41};
42
43
44static sem_id			sServiceSem;
45static thread_id		sServiceThread;
46static volatile bool	sServiceTerminate;
47static packet_buffer *  sServiceCmdBuffer;
48
49
50void
51ps2_service_notify_device_added(ps2_dev *dev)
52{
53	ps2_service_cmd cmd;
54
55	TRACE("ps2: ps2_service_notify_device_added %s\n", dev->name);
56
57	cmd.id = PS2_SERVICE_NOTIFY_DEVICE_ADDED;
58	cmd.dev = dev;
59
60	packet_buffer_write(sServiceCmdBuffer, (const uint8 *)&cmd, sizeof(cmd));
61	release_sem_etc(sServiceSem, 1, B_DO_NOT_RESCHEDULE);
62
63	TRACE("ps2: ps2_service_notify_device_added done\n");
64}
65
66
67void
68ps2_service_notify_device_republish(ps2_dev *dev)
69{
70	ps2_service_cmd cmd;
71
72	TRACE("ps2: ps2_service_notify_device_republish %s\n", dev->name);
73
74	cmd.id = PS2_SERVICE_NOTIFY_DEVICE_REPUBLISH;
75	cmd.dev = dev;
76
77	packet_buffer_write(sServiceCmdBuffer, (const uint8 *)&cmd, sizeof(cmd));
78	release_sem_etc(sServiceSem, 1, B_DO_NOT_RESCHEDULE);
79
80	TRACE("ps2: ps2_service_notify_device_republish done\n");
81}
82
83
84void
85ps2_service_notify_device_removed(ps2_dev *dev)
86{
87	ps2_service_cmd cmd;
88
89	TRACE("ps2: ps2_service_notify_device_removed %s\n", dev->name);
90
91	cmd.id = PS2_SERVICE_NOTIFY_DEVICE_REMOVED;
92	cmd.dev = dev;
93
94	packet_buffer_write(sServiceCmdBuffer, (const uint8 *)&cmd, sizeof(cmd));
95	release_sem_etc(sServiceSem, 1, B_DO_NOT_RESCHEDULE);
96
97	TRACE("ps2: ps2_service_notify_device_removed done\n");
98}
99
100
101static int32
102ps2_service_thread(void *arg)
103{
104	TRACE("ps2: ps2_service_thread started\n");
105
106	for (;;) {
107		status_t status;
108		status = acquire_sem_etc(sServiceSem, 1, B_CAN_INTERRUPT, 0);
109		if (sServiceTerminate)
110			break;
111		if (status == B_OK) {
112
113			// process service commands
114			ps2_service_cmd cmd;
115			packet_buffer_read(sServiceCmdBuffer, (uint8 *)&cmd, sizeof(cmd));
116			switch (cmd.id) {
117				case PS2_SERVICE_NOTIFY_DEVICE_ADDED:
118					TRACE("ps2: PS2_SERVICE_NOTIFY_DEVICE_ADDED %s\n", cmd.dev->name);
119					ps2_dev_publish(cmd.dev);
120					break;
121
122				case PS2_SERVICE_NOTIFY_DEVICE_REPUBLISH:
123					TRACE("ps2: PS2_SERVICE_NOTIFY_DEVICE_REPUBLISH %s\n", cmd.dev->name);
124					ps2_dev_unpublish(cmd.dev);
125					snooze(1500000);
126					ps2_dev_publish(cmd.dev);
127					break;
128
129				case PS2_SERVICE_NOTIFY_DEVICE_REMOVED:
130					TRACE("ps2: PS2_SERVICE_NOTIFY_DEVICE_REMOVED %s\n", cmd.dev->name);
131					ps2_dev_unpublish(cmd.dev);
132					break;
133
134				default:
135					TRACE("ps2: PS2_SERVICE: unknown id %" B_PRIu32 "\n", cmd.id);
136					break;
137			}
138		} else {
139			INFO("ps2: ps2_service_thread: Error, status 0x%08" B_PRIx32 ", "
140				"terminating\n", status);
141			break;
142		}
143	}
144	return 0;
145}
146
147
148#ifdef DEBUG_PUBLISHING
149static int
150ps2_republish(int argc, char **argv)
151{
152	int dev = 4;
153	if (argc == 2)
154		dev = strtoul(argv[1], NULL, 0);
155	if (dev < 0 || dev > 4)
156		dev = 4;
157	ps2_service_notify_device_republish(&ps2_device[dev]);
158	return 0;
159}
160#endif
161
162
163status_t
164ps2_service_init(void)
165{
166	TRACE("ps2: ps2_service_init\n");
167	sServiceCmdBuffer = create_packet_buffer(sizeof(ps2_service_cmd) * 50);
168	if (sServiceCmdBuffer == NULL)
169		goto err1;
170	sServiceSem = create_sem(0, "ps2 service");
171	if (sServiceSem < B_OK)
172		goto err2;
173	sServiceThread = spawn_kernel_thread(ps2_service_thread, "ps2 service", 20, NULL);
174	if (sServiceThread < B_OK)
175		goto err3;
176	sServiceTerminate = false;
177	resume_thread(sServiceThread);
178
179#ifdef DEBUG_PUBLISHING
180	add_debugger_command("ps2republish", &ps2_republish, "republish a ps2 device (0-3 mouse, 4 keyb (default))");
181#endif
182
183	TRACE("ps2: ps2_service_init done\n");
184	return B_OK;
185
186err3:
187	delete_sem(sServiceSem);
188err2:
189	delete_packet_buffer(sServiceCmdBuffer);
190err1:
191	TRACE("ps2: ps2_service_init failed\n");
192	return B_ERROR;
193}
194
195
196void
197ps2_service_exit(void)
198{
199	TRACE("ps2: ps2_service_exit enter\n");
200	sServiceTerminate = true;
201	release_sem(sServiceSem);
202	wait_for_thread(sServiceThread, NULL);
203	delete_sem(sServiceSem);
204	delete_packet_buffer(sServiceCmdBuffer);
205
206#ifdef DEBUG_PUBLISHING
207	remove_debugger_command("ps2republish", &ps2_republish);
208#endif
209	TRACE("ps2: ps2_service_exit done\n");
210}
211