279 lines
8.2 KiB
C
279 lines
8.2 KiB
C
/*
|
|
*
|
|
* 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 "drivers/drivers.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, 0, sizeof(int));
|
|
|
|
static void
|
|
adb_open(int *idx)
|
|
{
|
|
RET(-1);
|
|
}
|
|
|
|
static void
|
|
adb_close(int *idx)
|
|
{
|
|
}
|
|
|
|
NODE_METHODS( adb ) = {
|
|
{ "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;
|
|
|
|
fword("new-device");
|
|
|
|
push_str("adb");
|
|
fword("device-name");
|
|
|
|
push_str("adb");
|
|
fword("device-type");
|
|
|
|
if (has_pmu()) {
|
|
push_str("pmu-99");
|
|
fword("encode-string");
|
|
push_str("compatible");
|
|
fword("property");
|
|
} else {
|
|
push_str("adb");
|
|
fword("encode-string");
|
|
push_str("compatible");
|
|
fword("property");
|
|
}
|
|
|
|
PUSH(1);
|
|
fword("encode-int");
|
|
push_str("#address-cells");
|
|
fword("property");
|
|
|
|
PUSH(0);
|
|
fword("encode-int");
|
|
push_str("#size-cells");
|
|
fword("property");
|
|
|
|
BIND_NODE_METHODS(get_cur_dev(), adb);
|
|
|
|
snprintf(buf, sizeof(buf), "%s/adb", path);
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
fword("finish-device");
|
|
|
|
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;
|
|
}
|