xref: /freebsd/sys/dev/adb/adb_bus.c (revision d62d10eb1229c9aa0b3cf1b18872df59c18155db)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (C) 2008 Nathan Whitehorn
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  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/module.h>
31 #include <sys/bus.h>
32 #include <sys/conf.h>
33 #include <sys/kernel.h>
34 
35 #include <machine/bus.h>
36 
37 #include <vm/vm.h>
38 #include <vm/pmap.h>
39 
40 #include "adb.h"
41 #include "adbvar.h"
42 
43 static int adb_bus_probe(device_t dev);
44 static int adb_bus_attach(device_t dev);
45 static void adb_bus_enumerate(void *xdev);
46 static void adb_probe_nomatch(device_t dev, device_t child);
47 static int adb_print_child(device_t dev, device_t child);
48 
49 static int adb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command, uint8_t reg, int len, u_char *data, u_char *reply);
50 
51 static char *adb_device_string[] = {
52 	"HOST", "dongle", "keyboard", "mouse", "tablet", "modem", "RESERVED", "misc"
53 };
54 
55 static device_method_t adb_bus_methods[] = {
56 	/* Device interface */
57 	DEVMETHOD(device_probe,		adb_bus_probe),
58 	DEVMETHOD(device_attach,	adb_bus_attach),
59 	DEVMETHOD(device_detach,        bus_generic_detach),
60         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
61         DEVMETHOD(device_suspend,       bus_generic_suspend),
62         DEVMETHOD(device_resume,        bus_generic_resume),
63 
64 	/* Bus Interface */
65         DEVMETHOD(bus_probe_nomatch,    adb_probe_nomatch),
66         DEVMETHOD(bus_print_child,	adb_print_child),
67 
68 	{ 0, 0 },
69 };
70 
71 driver_t adb_driver = {
72 	"adb",
73 	adb_bus_methods,
74 	sizeof(struct adb_softc),
75 };
76 
77 static int
adb_bus_probe(device_t dev)78 adb_bus_probe(device_t dev)
79 {
80 	device_set_desc(dev, "Apple Desktop Bus");
81 	return (0);
82 }
83 
84 static int
adb_bus_attach(device_t dev)85 adb_bus_attach(device_t dev)
86 {
87 	struct adb_softc *sc = device_get_softc(dev);
88 	sc->enum_hook.ich_func = adb_bus_enumerate;
89 	sc->enum_hook.ich_arg = dev;
90 
91 	/*
92 	 * We should wait until interrupts are enabled to try to probe
93 	 * the bus. Enumerating the ADB involves receiving packets,
94 	 * which works best with interrupts enabled.
95 	 */
96 
97 	if (config_intrhook_establish(&sc->enum_hook) != 0)
98 		return (ENOMEM);
99 
100 	return (0);
101 }
102 
103 static void
adb_bus_enumerate(void * xdev)104 adb_bus_enumerate(void *xdev)
105 {
106 	device_t dev = (device_t)xdev;
107 
108 	struct adb_softc *sc = device_get_softc(dev);
109 	uint8_t i, next_free;
110 	uint16_t r3;
111 
112 	sc->sc_dev = dev;
113 	sc->parent = device_get_parent(dev);
114 
115 	sc->packet_reply = 0;
116 	sc->autopoll_mask = 0;
117 	sc->sync_packet = 0xffff;
118 
119 	/* Initialize devinfo */
120 	for (i = 0; i < 16; i++) {
121 		sc->devinfo[i].address = i;
122 		sc->devinfo[i].default_address = 0;
123 	}
124 
125 	/* Reset ADB bus */
126 	adb_send_raw_packet_sync(dev,0,ADB_COMMAND_BUS_RESET,0,0,NULL,NULL);
127 	DELAY(1500);
128 
129 	/* Enumerate bus */
130 	next_free = 8;
131 
132 	for (i = 1; i <= 7; i++) {
133 	    int8_t first_relocated = -1;
134 	    int reply = 0;
135 
136 	    do {
137 		reply = adb_send_raw_packet_sync(dev,i,
138 			    ADB_COMMAND_TALK,3,0,NULL,NULL);
139 
140 		if (reply) {
141 			/* If we got a response, relocate to next_free */
142 			r3 = sc->devinfo[i].register3;
143 			r3 &= 0xf000;
144 			r3 |= ((uint16_t)(next_free) & 0x000f) << 8;
145 			r3 |= 0x00fe;
146 
147 			adb_send_raw_packet_sync(dev,i, ADB_COMMAND_LISTEN,3,
148 			    sizeof(uint16_t),(u_char *)(&r3),NULL);
149 
150 			adb_send_raw_packet_sync(dev,next_free,
151 			    ADB_COMMAND_TALK,3,0,NULL,NULL);
152 
153 			sc->devinfo[next_free].default_address = i;
154 			if (first_relocated < 0)
155 				first_relocated = next_free;
156 
157 			next_free++;
158 		} else if (first_relocated > 0) {
159 			/* Collisions removed, relocate first device back */
160 
161 			r3 = sc->devinfo[i].register3;
162 			r3 &= 0xf000;
163 			r3 |= ((uint16_t)(i) & 0x000f) << 8;
164 
165 			adb_send_raw_packet_sync(dev,first_relocated,
166 			    ADB_COMMAND_LISTEN,3,
167 			    sizeof(uint16_t),(u_char *)(&r3),NULL);
168 			adb_send_raw_packet_sync(dev,i,
169 			    ADB_COMMAND_TALK,3,0,NULL,NULL);
170 
171 			sc->devinfo[i].default_address = i;
172 			sc->devinfo[(int)(first_relocated)].default_address = 0;
173 			break;
174 		}
175 	    } while (reply);
176 	}
177 
178 	for (i = 0; i < 16; i++) {
179 		if (sc->devinfo[i].default_address) {
180 			sc->children[i] = device_add_child(dev, NULL, DEVICE_UNIT_ANY);
181 			device_set_ivars(sc->children[i], &sc->devinfo[i]);
182 		}
183 	}
184 
185 	bus_generic_attach(dev);
186 
187 	config_intrhook_disestablish(&sc->enum_hook);
188 }
189 
190 static void
adb_probe_nomatch(device_t dev,device_t child)191 adb_probe_nomatch(device_t dev, device_t child)
192 {
193 	struct adb_devinfo *dinfo;
194 
195 	if (bootverbose) {
196 		dinfo = device_get_ivars(child);
197 
198 		device_printf(dev,"ADB %s at device %d (no driver attached)\n",
199 		    adb_device_string[dinfo->default_address],dinfo->address);
200 	}
201 }
202 
203 u_int
adb_receive_raw_packet(device_t dev,u_char status,u_char command,int len,u_char * data)204 adb_receive_raw_packet(device_t dev, u_char status, u_char command, int len,
205     u_char *data)
206 {
207 	struct adb_softc *sc = device_get_softc(dev);
208 	u_char addr = command >> 4;
209 
210 	if (len > 0 && (command & 0x0f) == ((ADB_COMMAND_TALK << 2) | 3)) {
211 		memcpy(&sc->devinfo[addr].register3,data,2);
212 		sc->devinfo[addr].handler_id = data[1];
213 	}
214 
215 	if (sc->sync_packet == command)  {
216 		memcpy(sc->syncreg,data,(len > 8) ? 8 : len);
217 		atomic_store_rel_int(&sc->packet_reply,len + 1);
218 		wakeup(sc);
219 	}
220 
221 	if (sc->children[addr] != NULL) {
222 		ADB_RECEIVE_PACKET(sc->children[addr],status,
223 			(command & 0x0f) >> 2,command & 0x03,len,data);
224 	}
225 
226 	return (0);
227 }
228 
229 static int
adb_print_child(device_t dev,device_t child)230 adb_print_child(device_t dev, device_t child)
231 {
232 	struct adb_devinfo *dinfo;
233 	int retval = 0;
234 
235 	dinfo = device_get_ivars(child);
236 
237 	retval += bus_print_child_header(dev,child);
238 	printf(" at device %d",dinfo->address);
239 	retval += bus_print_child_footer(dev, child);
240 
241 	return (retval);
242 }
243 
244 u_int
adb_send_packet(device_t dev,u_char command,u_char reg,int len,u_char * data)245 adb_send_packet(device_t dev, u_char command, u_char reg, int len, u_char *data)
246 {
247 	u_char command_byte = 0;
248 	struct adb_devinfo *dinfo;
249 	struct adb_softc *sc;
250 
251 	sc = device_get_softc(device_get_parent(dev));
252 	dinfo = device_get_ivars(dev);
253 
254 	command_byte |= dinfo->address << 4;
255 	command_byte |= command << 2;
256 	command_byte |= reg;
257 
258 	ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte, len, data, 1);
259 
260 	return (0);
261 }
262 
263 u_int
adb_set_autopoll(device_t dev,u_char enable)264 adb_set_autopoll(device_t dev, u_char enable)
265 {
266 	struct adb_devinfo *dinfo;
267 	struct adb_softc *sc;
268 	uint16_t mod = 0;
269 
270 	sc = device_get_softc(device_get_parent(dev));
271 	dinfo = device_get_ivars(dev);
272 
273 	mod = enable << dinfo->address;
274 	if (enable) {
275 		sc->autopoll_mask |= mod;
276 	} else {
277 		mod = ~mod;
278 		sc->autopoll_mask &= mod;
279 	}
280 
281 	ADB_HB_SET_AUTOPOLL_MASK(sc->parent,sc->autopoll_mask);
282 
283 	return (0);
284 }
285 
286 uint8_t
adb_get_device_type(device_t dev)287 adb_get_device_type(device_t dev)
288 {
289 	struct adb_devinfo *dinfo;
290 
291 	dinfo = device_get_ivars(dev);
292 	return (dinfo->default_address);
293 }
294 
295 uint8_t
adb_get_device_handler(device_t dev)296 adb_get_device_handler(device_t dev)
297 {
298 	struct adb_devinfo *dinfo;
299 
300 	dinfo = device_get_ivars(dev);
301 	return (dinfo->handler_id);
302 }
303 
304 static int
adb_send_raw_packet_sync(device_t dev,uint8_t to,uint8_t command,uint8_t reg,int len,u_char * data,u_char * reply)305 adb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command,
306     uint8_t reg, int len, u_char *data, u_char *reply)
307 {
308 	u_char command_byte = 0;
309 	struct adb_softc *sc;
310 	int result = -1;
311 	int i = 1;
312 
313 	sc = device_get_softc(dev);
314 
315 	command_byte |= to << 4;
316 	command_byte |= command << 2;
317 	command_byte |= reg;
318 
319 	/* Wait if someone else has a synchronous request pending */
320 	while (!atomic_cmpset_int(&sc->sync_packet, 0xffff, command_byte))
321 		tsleep(sc, 0, "ADB sync", hz/10);
322 
323 	sc->packet_reply = 0;
324 	sc->sync_packet = command_byte;
325 
326 	ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte, len, data, 1);
327 
328 	while (!atomic_fetchadd_int(&sc->packet_reply,0)) {
329 		/*
330 		 * Maybe the command got lost? Try resending and polling the
331 		 * controller.
332 		 */
333 		if (i % 40 == 0)
334 			ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte,
335 			    len, data, 1);
336 
337 		tsleep(sc, 0, "ADB sync", hz/10);
338 		i++;
339 	}
340 
341 	result = sc->packet_reply - 1;
342 
343 	if (reply != NULL && result > 0)
344 		memcpy(reply,sc->syncreg,result);
345 
346 	/* Clear packet sync */
347 	sc->packet_reply = 0;
348 
349 	/*
350 	 * We can't match a value beyond 8 bits, so set sync_packet to
351 	 * 0xffff to avoid collisions.
352 	 */
353 	atomic_set_int(&sc->sync_packet, 0xffff);
354 
355 	return (result);
356 }
357 
358 uint8_t
adb_set_device_handler(device_t dev,uint8_t newhandler)359 adb_set_device_handler(device_t dev, uint8_t newhandler)
360 {
361 	struct adb_softc *sc;
362 	struct adb_devinfo *dinfo;
363 	uint16_t newr3;
364 
365 	dinfo = device_get_ivars(dev);
366 	sc = device_get_softc(device_get_parent(dev));
367 
368 	newr3 = dinfo->register3 & 0xff00;
369 	newr3 |= (uint16_t)(newhandler);
370 
371 	adb_send_raw_packet_sync(sc->sc_dev,dinfo->address, ADB_COMMAND_LISTEN,
372 	    3, sizeof(uint16_t), (u_char *)(&newr3), NULL);
373 	adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
374 	    ADB_COMMAND_TALK, 3, 0, NULL, NULL);
375 
376 	return (dinfo->handler_id);
377 }
378 
379 size_t
adb_read_register(device_t dev,u_char reg,void * data)380 adb_read_register(device_t dev, u_char reg, void *data)
381 {
382 	struct adb_softc *sc;
383 	struct adb_devinfo *dinfo;
384 	size_t result;
385 
386 	dinfo = device_get_ivars(dev);
387 	sc = device_get_softc(device_get_parent(dev));
388 
389 	result = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
390 	           ADB_COMMAND_TALK, reg, 0, NULL, data);
391 
392 	return (result);
393 }
394 
395 size_t
adb_write_register(device_t dev,u_char reg,size_t len,void * data)396 adb_write_register(device_t dev, u_char reg, size_t len, void *data)
397 {
398 	struct adb_softc *sc;
399 	struct adb_devinfo *dinfo;
400 	size_t result;
401 
402 	dinfo = device_get_ivars(dev);
403 	sc = device_get_softc(device_get_parent(dev));
404 
405 	result = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
406 		   ADB_COMMAND_LISTEN, reg, len, (u_char *)data, NULL);
407 
408 	result = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
409 	           ADB_COMMAND_TALK, reg, 0, NULL, NULL);
410 
411 	return (result);
412 }
413