adb_bus.c revision 256281
1219820Sjeff/*-
2219820Sjeff * Copyright (C) 2008 Nathan Whitehorn
3219820Sjeff * All rights reserved.
4219820Sjeff *
5219820Sjeff * Redistribution and use in source and binary forms, with or without
6219820Sjeff * modification, are permitted provided that the following conditions
7219820Sjeff * are met:
8219820Sjeff * 1. Redistributions of source code must retain the above copyright
9219820Sjeff *    notice, this list of conditions and the following disclaimer.
10219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright
11219820Sjeff *    notice, this list of conditions and the following disclaimer in the
12219820Sjeff *    documentation and/or other materials provided with the distribution.
13219820Sjeff *
14219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15219820Sjeff * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16219820Sjeff * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17219820Sjeff * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18219820Sjeff * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19219820Sjeff * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20219820Sjeff * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21219820Sjeff * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22219820Sjeff * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23219820Sjeff * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24219820Sjeff *
25219820Sjeff * $FreeBSD: stable/10/sys/dev/adb/adb_bus.c 199888 2009-11-28 17:48:25Z nwhitehorn $
26219820Sjeff */
27219820Sjeff
28219820Sjeff#include <sys/cdefs.h>
29219820Sjeff#include <sys/param.h>
30219820Sjeff#include <sys/systm.h>
31219820Sjeff#include <sys/module.h>
32219820Sjeff#include <sys/bus.h>
33219820Sjeff#include <sys/conf.h>
34219820Sjeff#include <sys/kernel.h>
35219820Sjeff
36219820Sjeff#include <machine/bus.h>
37219820Sjeff
38219820Sjeff#include <vm/vm.h>
39219820Sjeff#include <vm/pmap.h>
40219820Sjeff
41219820Sjeff#include "adb.h"
42219820Sjeff#include "adbvar.h"
43219820Sjeff
44219820Sjeffstatic int adb_bus_probe(device_t dev);
45219820Sjeffstatic int adb_bus_attach(device_t dev);
46219820Sjeffstatic int adb_bus_detach(device_t dev);
47219820Sjeffstatic void adb_bus_enumerate(void *xdev);
48219820Sjeffstatic void adb_probe_nomatch(device_t dev, device_t child);
49219820Sjeffstatic int adb_print_child(device_t dev, device_t child);
50219820Sjeff
51219820Sjeffstatic 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);
52219820Sjeff
53219820Sjeffstatic char *adb_device_string[] = {
54219820Sjeff	"HOST", "dongle", "keyboard", "mouse", "tablet", "modem", "RESERVED", "misc"
55219820Sjeff};
56219820Sjeff
57219820Sjeffstatic device_method_t adb_bus_methods[] = {
58219820Sjeff	/* Device interface */
59219820Sjeff	DEVMETHOD(device_probe,		adb_bus_probe),
60219820Sjeff	DEVMETHOD(device_attach,	adb_bus_attach),
61219820Sjeff	DEVMETHOD(device_detach,        adb_bus_detach),
62219820Sjeff        DEVMETHOD(device_shutdown,      bus_generic_shutdown),
63219820Sjeff        DEVMETHOD(device_suspend,       bus_generic_suspend),
64219820Sjeff        DEVMETHOD(device_resume,        bus_generic_resume),
65219820Sjeff
66219820Sjeff	/* Bus Interface */
67219820Sjeff        DEVMETHOD(bus_probe_nomatch,    adb_probe_nomatch),
68219820Sjeff        DEVMETHOD(bus_print_child,	adb_print_child),
69219820Sjeff
70219820Sjeff	{ 0, 0 },
71219820Sjeff};
72219820Sjeff
73219820Sjeffdriver_t adb_driver = {
74219820Sjeff	"adb",
75219820Sjeff	adb_bus_methods,
76219820Sjeff	sizeof(struct adb_softc),
77219820Sjeff};
78219820Sjeff
79219820Sjeffdevclass_t adb_devclass;
80219820Sjeff
81219820Sjeffstatic int
82219820Sjeffadb_bus_probe(device_t dev)
83219820Sjeff{
84219820Sjeff	device_set_desc(dev, "Apple Desktop Bus");
85219820Sjeff	return (0);
86219820Sjeff}
87219820Sjeff
88219820Sjeffstatic int
89219820Sjeffadb_bus_attach(device_t dev)
90219820Sjeff{
91219820Sjeff	struct adb_softc *sc = device_get_softc(dev);
92219820Sjeff	sc->enum_hook.ich_func = adb_bus_enumerate;
93219820Sjeff	sc->enum_hook.ich_arg = dev;
94219820Sjeff
95219820Sjeff	/*
96219820Sjeff	 * We should wait until interrupts are enabled to try to probe
97219820Sjeff	 * the bus. Enumerating the ADB involves receiving packets,
98219820Sjeff	 * which works best with interrupts enabled.
99219820Sjeff	 */
100219820Sjeff
101219820Sjeff	if (config_intrhook_establish(&sc->enum_hook) != 0)
102219820Sjeff		return (ENOMEM);
103219820Sjeff
104219820Sjeff	return (0);
105219820Sjeff}
106219820Sjeff
107219820Sjeffstatic void
108219820Sjeffadb_bus_enumerate(void *xdev)
109219820Sjeff{
110	device_t dev = (device_t)xdev;
111
112	struct adb_softc *sc = device_get_softc(dev);
113	uint8_t i, next_free;
114	uint16_t r3;
115
116	sc->sc_dev = dev;
117	sc->parent = device_get_parent(dev);
118
119	sc->packet_reply = 0;
120	sc->autopoll_mask = 0;
121	sc->sync_packet = 0xffff;
122
123	/* Initialize devinfo */
124	for (i = 0; i < 16; i++) {
125		sc->devinfo[i].address = i;
126		sc->devinfo[i].default_address = 0;
127	}
128
129	/* Reset ADB bus */
130	adb_send_raw_packet_sync(dev,0,ADB_COMMAND_BUS_RESET,0,0,NULL,NULL);
131	DELAY(1500);
132
133	/* Enumerate bus */
134	next_free = 8;
135
136	for (i = 1; i <= 7; i++) {
137	    int8_t first_relocated = -1;
138	    int reply = 0;
139
140	    do {
141		reply = adb_send_raw_packet_sync(dev,i,
142			    ADB_COMMAND_TALK,3,0,NULL,NULL);
143
144		if (reply) {
145			/* If we got a response, relocate to next_free */
146			r3 = sc->devinfo[i].register3;
147			r3 &= 0xf000;
148			r3 |= ((uint16_t)(next_free) & 0x000f) << 8;
149			r3 |= 0x00fe;
150
151			adb_send_raw_packet_sync(dev,i, ADB_COMMAND_LISTEN,3,
152			    sizeof(uint16_t),(u_char *)(&r3),NULL);
153
154			adb_send_raw_packet_sync(dev,next_free,
155			    ADB_COMMAND_TALK,3,0,NULL,NULL);
156
157			sc->devinfo[next_free].default_address = i;
158			if (first_relocated < 0)
159				first_relocated = next_free;
160
161			next_free++;
162		} else if (first_relocated > 0) {
163			/* Collisions removed, relocate first device back */
164
165			r3 = sc->devinfo[i].register3;
166			r3 &= 0xf000;
167			r3 |= ((uint16_t)(i) & 0x000f) << 8;
168
169			adb_send_raw_packet_sync(dev,first_relocated,
170			    ADB_COMMAND_LISTEN,3,
171			    sizeof(uint16_t),(u_char *)(&r3),NULL);
172			adb_send_raw_packet_sync(dev,i,
173			    ADB_COMMAND_TALK,3,0,NULL,NULL);
174
175			sc->devinfo[i].default_address = i;
176			sc->devinfo[(int)(first_relocated)].default_address = 0;
177			break;
178		}
179	    } while (reply);
180	}
181
182	for (i = 0; i < 16; i++) {
183		if (sc->devinfo[i].default_address) {
184			sc->children[i] = device_add_child(dev, NULL, -1);
185			device_set_ivars(sc->children[i], &sc->devinfo[i]);
186		}
187	}
188
189	bus_generic_attach(dev);
190
191	config_intrhook_disestablish(&sc->enum_hook);
192}
193
194static int adb_bus_detach(device_t dev)
195{
196	return (bus_generic_detach(dev));
197}
198
199
200static void
201adb_probe_nomatch(device_t dev, device_t child)
202{
203	struct adb_devinfo *dinfo;
204
205	if (bootverbose) {
206		dinfo = device_get_ivars(child);
207
208		device_printf(dev,"ADB %s at device %d (no driver attached)\n",
209		    adb_device_string[dinfo->default_address],dinfo->address);
210	}
211}
212
213u_int
214adb_receive_raw_packet(device_t dev, u_char status, u_char command, int len,
215    u_char *data)
216{
217	struct adb_softc *sc = device_get_softc(dev);
218	u_char addr = command >> 4;
219
220	if (len > 0 && (command & 0x0f) == ((ADB_COMMAND_TALK << 2) | 3)) {
221		memcpy(&sc->devinfo[addr].register3,data,2);
222		sc->devinfo[addr].handler_id = data[1];
223	}
224
225	if (sc->sync_packet == command)  {
226		memcpy(sc->syncreg,data,(len > 8) ? 8 : len);
227		atomic_store_rel_int(&sc->packet_reply,len + 1);
228		wakeup(sc);
229	}
230
231	if (sc->children[addr] != NULL) {
232		ADB_RECEIVE_PACKET(sc->children[addr],status,
233			(command & 0x0f) >> 2,command & 0x03,len,data);
234	}
235
236	return (0);
237}
238
239static int
240adb_print_child(device_t dev, device_t child)
241{
242	struct adb_devinfo *dinfo;
243	int retval = 0;
244
245	dinfo = device_get_ivars(child);
246
247	retval += bus_print_child_header(dev,child);
248	printf(" at device %d",dinfo->address);
249	retval += bus_print_child_footer(dev, child);
250
251	return (retval);
252}
253
254u_int
255adb_send_packet(device_t dev, u_char command, u_char reg, int len, u_char *data)
256{
257	u_char command_byte = 0;
258	struct adb_devinfo *dinfo;
259	struct adb_softc *sc;
260
261	sc = device_get_softc(device_get_parent(dev));
262	dinfo = device_get_ivars(dev);
263
264	command_byte |= dinfo->address << 4;
265	command_byte |= command << 2;
266	command_byte |= reg;
267
268	ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte, len, data, 1);
269
270	return (0);
271}
272
273u_int
274adb_set_autopoll(device_t dev, u_char enable)
275{
276	struct adb_devinfo *dinfo;
277	struct adb_softc *sc;
278	uint16_t mod = 0;
279
280	sc = device_get_softc(device_get_parent(dev));
281	dinfo = device_get_ivars(dev);
282
283	mod = enable << dinfo->address;
284	if (enable) {
285		sc->autopoll_mask |= mod;
286	} else {
287		mod = ~mod;
288		sc->autopoll_mask &= mod;
289	}
290
291	ADB_HB_SET_AUTOPOLL_MASK(sc->parent,sc->autopoll_mask);
292
293	return (0);
294}
295
296uint8_t
297adb_get_device_type(device_t dev)
298{
299	struct adb_devinfo *dinfo;
300
301	dinfo = device_get_ivars(dev);
302	return (dinfo->default_address);
303}
304
305uint8_t
306adb_get_device_handler(device_t dev)
307{
308	struct adb_devinfo *dinfo;
309
310	dinfo = device_get_ivars(dev);
311	return (dinfo->handler_id);
312}
313
314static int
315adb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command,
316    uint8_t reg, int len, u_char *data, u_char *reply)
317{
318	u_char command_byte = 0;
319	struct adb_softc *sc;
320	int result = -1;
321	int i = 1;
322
323	sc = device_get_softc(dev);
324
325	command_byte |= to << 4;
326	command_byte |= command << 2;
327	command_byte |= reg;
328
329	/* Wait if someone else has a synchronous request pending */
330	while (!atomic_cmpset_int(&sc->sync_packet, 0xffff, command_byte))
331		tsleep(sc, 0, "ADB sync", hz/10);
332
333	sc->packet_reply = 0;
334	sc->sync_packet = command_byte;
335
336	ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte, len, data, 1);
337
338	while (!atomic_fetchadd_int(&sc->packet_reply,0)) {
339		/*
340		 * Maybe the command got lost? Try resending and polling the
341		 * controller.
342		 */
343		if (i % 40 == 0)
344			ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte,
345			    len, data, 1);
346
347		tsleep(sc, 0, "ADB sync", hz/10);
348		i++;
349	}
350
351	result = sc->packet_reply - 1;
352
353	if (reply != NULL && result > 0)
354		memcpy(reply,sc->syncreg,result);
355
356	/* Clear packet sync */
357	sc->packet_reply = 0;
358
359	/*
360	 * We can't match a value beyond 8 bits, so set sync_packet to
361	 * 0xffff to avoid collisions.
362	 */
363	atomic_set_int(&sc->sync_packet, 0xffff);
364
365	return (result);
366}
367
368uint8_t
369adb_set_device_handler(device_t dev, uint8_t newhandler)
370{
371	struct adb_softc *sc;
372	struct adb_devinfo *dinfo;
373	uint16_t newr3;
374
375	dinfo = device_get_ivars(dev);
376	sc = device_get_softc(device_get_parent(dev));
377
378	newr3 = dinfo->register3 & 0xff00;
379	newr3 |= (uint16_t)(newhandler);
380
381	adb_send_raw_packet_sync(sc->sc_dev,dinfo->address, ADB_COMMAND_LISTEN,
382	    3, sizeof(uint16_t), (u_char *)(&newr3), NULL);
383	adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
384	    ADB_COMMAND_TALK, 3, 0, NULL, NULL);
385
386	return (dinfo->handler_id);
387}
388
389size_t
390adb_read_register(device_t dev, u_char reg, void *data)
391{
392	struct adb_softc *sc;
393	struct adb_devinfo *dinfo;
394	size_t result;
395
396	dinfo = device_get_ivars(dev);
397	sc = device_get_softc(device_get_parent(dev));
398
399	result = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
400	           ADB_COMMAND_TALK, reg, 0, NULL, data);
401
402	return (result);
403}
404
405size_t
406adb_write_register(device_t dev, u_char reg, size_t len, void *data)
407{
408	struct adb_softc *sc;
409	struct adb_devinfo *dinfo;
410	size_t result;
411
412	dinfo = device_get_ivars(dev);
413	sc = device_get_softc(device_get_parent(dev));
414
415	result = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
416		   ADB_COMMAND_LISTEN, reg, len, (u_char *)data, NULL);
417
418	result = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
419	           ADB_COMMAND_TALK, reg, 0, NULL, NULL);
420
421	return (result);
422}
423