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