summaryrefslogtreecommitdiffstats
path: root/qemu/roms/openbios/drivers/adb_bus.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/openbios/drivers/adb_bus.c')
-rw-r--r--qemu/roms/openbios/drivers/adb_bus.c257
1 files changed, 257 insertions, 0 deletions
diff --git a/qemu/roms/openbios/drivers/adb_bus.c b/qemu/roms/openbios/drivers/adb_bus.c
new file mode 100644
index 000000000..d67d1f1a4
--- /dev/null
+++ b/qemu/roms/openbios/drivers/adb_bus.c
@@ -0,0 +1,257 @@
+/*
+ *
+ * Open Hack'Ware BIOS ADB bus support, ported to OpenBIOS
+ *
+ * Copyright (c) 2005 Jocelyn Mayer
+ * Copyright (c) 2005 Stefan Reinauer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License V2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "libc/vsprintf.h"
+
+#include "adb_bus.h"
+#include "adb_kbd.h"
+#include "adb_mouse.h"
+
+DECLARE_UNNAMED_NODE( adb, INSTALL_OPEN, sizeof(int));
+
+static void
+adb_initialize (int *idx)
+{
+ phandle_t ph=get_cur_dev();
+
+ push_str("adb");
+ fword("device-type");
+
+ set_property(ph, "compatible", "adb", 4);
+ set_int_property(ph, "#address-cells", 1);
+ set_int_property(ph, "#size-cells", 0);
+}
+
+static void
+adb_open(int *idx)
+{
+ RET(-1);
+}
+
+static void
+adb_close(int *idx)
+{
+}
+
+NODE_METHODS( adb ) = {
+ { NULL, adb_initialize },
+ { "open", adb_open },
+ { "close", adb_close },
+};
+
+adb_bus_t *adb_bus_new (void *host,
+ int (*req)(void *host, const uint8_t *snd_buf,
+ int len, uint8_t *rcv_buf))
+{
+ adb_bus_t *new;
+
+ new = malloc(sizeof(adb_bus_t));
+ if (new == NULL)
+ return NULL;
+ new->host = host;
+ new->req = req;
+
+ return new;
+}
+
+/* Check and relocate all ADB devices as suggested in
+ * ADB_manager Apple documentation
+ */
+
+int adb_bus_init (char *path, adb_bus_t *bus)
+{
+ char buf[64];
+ uint8_t buffer[ADB_BUF_SIZE];
+ uint8_t adb_addresses[16] =
+ { 8, 9, 10, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, 0, };
+ adb_dev_t tmp_device, **cur;
+ int address;
+ int reloc = 0, next_free = 7;
+ int keep;
+
+ snprintf(buf, sizeof(buf), "%s/adb", path);
+ REGISTER_NAMED_NODE( adb, buf);
+ /* Reset the bus */
+ // ADB_DPRINTF("\n");
+ adb_reset(bus);
+ cur = &bus->devices;
+ memset(&tmp_device, 0, sizeof(adb_dev_t));
+ tmp_device.bus = bus;
+ for (address = 1; address < 8 && adb_addresses[reloc] > 0;) {
+ if (address == ADB_RES) {
+ /* Reserved */
+ address++;
+ continue;
+ }
+ //ADB_DPRINTF("Check device on ADB address %d\n", address);
+ tmp_device.addr = address;
+ switch (adb_reg_get(&tmp_device, 3, buffer)) {
+ case 0:
+ //ADB_DPRINTF("No device on ADB address %d\n", address);
+ /* Register this address as free */
+ if (adb_addresses[next_free] != 0)
+ adb_addresses[next_free++] = address;
+ /* Check next ADB address */
+ address++;
+ break;
+ case 2:
+ /* One device answered :
+ * make it available and relocate it to a free address
+ */
+ if (buffer[0] == ADB_CHADDR) {
+ /* device self test failed */
+ ADB_DPRINTF("device on ADB address %d self-test failed "
+ "%02x %02x %02x\n", address,
+ buffer[0], buffer[1], buffer[2]);
+ keep = 0;
+ } else {
+ //ADB_DPRINTF("device on ADB address %d self-test OK\n",
+ // address);
+ keep = 1;
+ }
+ ADB_DPRINTF("Relocate device on ADB address %d to %d (%d)\n",
+ address, adb_addresses[reloc], reloc);
+ buffer[0] = ((buffer[0] & 0x40) & ~0x90) | adb_addresses[reloc];
+ if (keep == 1)
+ buffer[0] |= 0x20;
+ buffer[1] = ADB_CHADDR_NOCOLL;
+ if (adb_reg_set(&tmp_device, 3, buffer, 2) < 0) {
+ ADB_DPRINTF("ADB device relocation failed\n");
+ return -1;
+ }
+ if (keep == 1) {
+ *cur = malloc(sizeof(adb_dev_t));
+ if (*cur == NULL) {
+ return -1;
+ }
+ (*cur)->type = address;
+ (*cur)->bus = bus;
+ (*cur)->addr = adb_addresses[reloc++];
+ /* Flush buffers */
+ adb_flush(*cur);
+ switch ((*cur)->type) {
+ case ADB_PROTECT:
+ ADB_DPRINTF("Found one protected device\n");
+ break;
+ case ADB_KEYBD:
+ ADB_DPRINTF("Found one keyboard on address %d\n", address);
+ adb_kbd_new(buf, *cur);
+ break;
+ case ADB_MOUSE:
+ ADB_DPRINTF("Found one mouse on address %d\n", address);
+ adb_mouse_new(buf, *cur);
+ break;
+ case ADB_ABS:
+ ADB_DPRINTF("Found one absolute positioning device\n");
+ break;
+ case ADB_MODEM:
+ ADB_DPRINTF("Found one modem\n");
+ break;
+ case ADB_RES:
+ ADB_DPRINTF("Found one ADB res device\n");
+ break;
+ case ADB_MISC:
+ ADB_DPRINTF("Found one ADB misc device\n");
+ break;
+ }
+ cur = &((*cur)->next);
+ }
+ break;
+ case 1:
+ case 3 ... 7:
+ /* SHOULD NOT HAPPEN : register 3 is always two bytes long */
+ ADB_DPRINTF("Invalid returned len for ADB register 3\n");
+ return -1;
+ case -1:
+ /* ADB ERROR */
+ ADB_DPRINTF("error gettting ADB register 3\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int adb_cmd (adb_dev_t *dev, uint8_t cmd, uint8_t reg,
+ uint8_t *buf, int len)
+{
+ uint8_t adb_send[ADB_BUF_SIZE], adb_rcv[ADB_BUF_SIZE];
+
+ //ADB_DPRINTF("cmd: %d reg: %d len: %d\n", cmd, reg, len);
+ if (dev->bus == NULL || dev->bus->req == NULL) {
+ ADB_DPRINTF("ERROR: invalid bus !\n");
+ for (;;);
+ }
+ /* Sanity checks */
+ if (cmd != ADB_LISTEN && len != 0) {
+ /* No buffer transmitted but for LISTEN command */
+ ADB_DPRINTF("in buffer for cmd %d\n", cmd);
+ return -1;
+ }
+ if (cmd == ADB_LISTEN && ((len < 2 || len > 8) || buf == NULL)) {
+ /* Need a buffer with a regular register size for LISTEN command */
+ ADB_DPRINTF("no/invalid buffer for ADB_LISTEN (%d)\n", len);
+ return -1;
+ }
+ if ((cmd == ADB_TALK || cmd == ADB_LISTEN) && reg > 3) {
+ /* Need a valid register number for LISTEN and TALK commands */
+ ADB_DPRINTF("invalid reg for TALK/LISTEN command (%d %d)\n", cmd, reg);
+ return -1;
+ }
+ switch (cmd) {
+ case ADB_SEND_RESET:
+ adb_send[0] = ADB_SEND_RESET;
+ break;
+ case ADB_FLUSH:
+ adb_send[0] = (dev->addr << 4) | ADB_FLUSH;
+ break;
+ case ADB_LISTEN:
+ memcpy(adb_send + 1, buf, len);
+ /* No break here */
+ case ADB_TALK:
+ adb_send[0] = (dev->addr << 4) | cmd | reg;
+ break;
+ }
+ memset(adb_rcv, 0, ADB_BUF_SIZE);
+ len = (*dev->bus->req)(dev->bus->host, adb_send, len + 1, adb_rcv);
+#ifdef DEBUG_ADB
+ //printk("%x %x %x %x\n", adb_rcv[0], adb_rcv[1], adb_rcv[2], adb_rcv[3]);
+#endif
+ switch (len) {
+ case 0:
+ /* No data */
+ break;
+ case 2 ... 8:
+ /* Register transmitted */
+ if (buf != NULL)
+ memcpy(buf, adb_rcv, len);
+ break;
+ default:
+ /* Should never happen */
+ //ADB_DPRINTF("Cmd %d returned %d bytes !\n", cmd, len);
+ return -1;
+ }
+ //ADB_DPRINTF("retlen: %d\n", len);
+
+ return len;
+}