patch-2.1.87 linux/arch/m68k/mac/adb-bus.c
Next file: linux/arch/m68k/mac/bootparse.c
Previous file: linux/arch/m68k/mac/Makefile
Back to the patch index
Back to the overall index
- Lines: 2135
- Date:
Thu Feb 12 16:30:13 1998
- Orig file:
v2.1.86/linux/arch/m68k/mac/adb-bus.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.1.86/linux/arch/m68k/mac/adb-bus.c linux/arch/m68k/mac/adb-bus.c
@@ -0,0 +1,2134 @@
+/*
+ * MACII ADB keyboard handler.
+ * Copyright (c) 1997 Alan Cox
+ *
+ * Derived from code
+ * Copyright (C) 1996 Paul Mackerras.
+ *
+ * MSch (9/97) Partial rewrite of interrupt handler to MacII style
+ * ADB handshake, based on:
+ * - Guide to Mac Hardware
+ * - Guido Koerber's session with a logic analyzer
+ */
+
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include "via6522.h"
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/adb.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/setup.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+
+
+#define MACII /* For now - will be a switch */
+
+/* Bits in B data register: all active low */
+#define TREQ 0x08 /* Transfer request (input) */
+#define TACK 0x10 /* Transfer acknowledge (output) */
+#define TIP 0x20 /* Transfer in progress (output) */
+
+/* Bits in B data register: ADB transaction states MacII */
+#define ST_MASK 0x30 /* mask for selecting ADB state bits */
+/* ADB transaction states according to GMHW */
+#define ST_CMD 0x00 /* ADB state: command byte */
+#define ST_EVEN 0x10 /* ADB state: even data byte */
+#define ST_ODD 0x20 /* ADB state: odd data byte */
+#define ST_IDLE 0x30 /* ADB state: idle, nothing to send */
+
+/* Bits in ACR */
+#define SR_CTRL 0x1c /* Shift register control bits */
+#ifdef USE_ORIG
+#define SR_EXT 0x1c /* Shift on external clock */
+#else
+#define SR_EXT 0x0c /* Shift on external clock */
+#endif
+#define SR_OUT 0x10 /* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET 0x80 /* set bits in IER */
+#define IER_CLR 0 /* clear bits in IER */
+#define SR_INT 0x04 /* Shift register full/empty */
+#define SR_DATA 0x08 /* Shift register data */
+#define SR_CLOCK 0x10 /* Shift register clock */
+
+static struct adb_handler {
+ void (*handler)(unsigned char *, int, struct pt_regs *);
+} adb_handler[16];
+
+static enum adb_state {
+ idle,
+ sent_first_byte,
+ sending,
+ reading,
+ read_done,
+ awaiting_reply
+} adb_state;
+
+static struct adb_request *current_req;
+static struct adb_request *last_req;
+static unsigned char cuda_rbuf[16];
+static unsigned char *reply_ptr;
+static int reply_len;
+static int reading_reply;
+static int data_index;
+static int first_byte;
+static int prefix_len;
+
+static int status = ST_IDLE|TREQ;
+static int last_status;
+/*static int adb_delay;*/
+int in_keybinit = 1;
+
+static void adb_start(void);
+extern void adb_interrupt(int irq, void *arg, struct pt_regs *regs);
+extern void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs);
+extern void adb_clock_interrupt(int irq, void *arg, struct pt_regs *regs);
+extern void adb_data_interrupt(int irq, void *arg, struct pt_regs *regs);
+static void adb_input(unsigned char *buf, int nb, struct pt_regs *regs);
+
+/*
+ * Misc. defines for testing
+ */
+
+extern int console_loglevel;
+
+#define ADBDEBUG_STATUS (1)
+#define ADBDEBUG_STATE (2)
+#define ADBDEBUG_READ (4)
+#define ADBDEBUG_WRITE (8)
+#define ADBDEBUG_START (16)
+#define ADBDEBUG_RETRY (32)
+#define ADBDEBUG_POLL (64)
+#define ADBDEBUG_INT (128)
+#define ADBDEBUG_PROT (256)
+#define ADBDEBUG_SRQ (512)
+#define ADBDEBUG_REQUEST (1024)
+#define ADBDEBUG_INPUT (2048)
+#define ADBDEBUG_DEVICE (4096)
+
+
+#define DEBUG_ADB
+
+#ifdef DEBUG_ADB
+#define ADBDEBUG (ADBDEBUG_READ | ADBDEBUG_START | ADBDEBUG_WRITE | ADBDEBUG_SRQ | ADBDEBUG_REQUEST)
+#else
+#define ADBDEBUG (0)
+#endif
+
+#define TRY_CUDA
+
+void adb_bus_init(void)
+{
+ unsigned long flags;
+ unsigned char c;
+
+ save_flags(flags);
+ cli();
+
+ /*
+ * Setup ADB
+ */
+
+ switch(macintosh_config->adb_type)
+ {
+
+ case MAC_ADB_II:
+ printk("adb: MacII style keyboard/mouse driver.\n");
+ /* Set the lines up. We want TREQ as input TACK|TIP as output */
+ via_write(via1, vDirB, ((via_read(via1,vDirB)|TACK|TIP)&~TREQ));
+ /*
+ * Docs suggest TREQ should be output - that seems nuts
+ * BSD agrees here :-)
+ * Setup vPCR ??
+ */
+
+#ifdef USE_ORIG
+ /* Lower the bus signals (MacII is active low it seems ???) */
+ via_write(via1, vBufB, via_read(via1, vBufB)&~TACK);
+#else
+ /* Corresponding state: idle (clear state bits) */
+ via_write(via1, vBufB, (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
+ last_status = (via_read(via1, vBufB)&~ST_MASK);
+#endif
+ /* Shift register on input */
+ c=via_read(via1, vACR);
+ c&=~SR_CTRL; /* Clear shift register bits */
+ c|=SR_EXT; /* Shift on external clock; out or in? */
+ via_write(via1, vACR, c);
+ /* Wipe any pending data and int */
+ via_read(via1, vSR);
+
+ /* This is interrupts on enable SR for keyboard */
+ via_write(via1, vIER, IER_SET|SR_INT);
+ /* This clears the interrupt bit */
+ via_write(via1, vIFR, SR_INT);
+#if 0
+ ct=1000;
+ while( ct-- && (via_read(via1, vBufB)&TREQ))
+ udelay(1000);
+ if(ct<0)
+ printk("No sync occured\n");
+ ct=1000;
+ while( ct-- && !(via_read(via1, vIFR)&SR_INT))
+ udelay(1000);
+ if(ct<0)
+ printk("No sync 2 occured\n");
+ via_read(via1, vSR);
+ via_write(via1, vBufB, via_read(via1, vBufB)|TACK);
+ while( ct-- && !(via_read(via1, vBufB)&TREQ))
+ udelay(1000);
+ if(ct<0)
+ printk("No sync 3 occured\n");
+ ct=1000;
+ while( ct-- && !(via_read(via1, vIFR)&SR_INT))
+ udelay(1000);
+ if(ct<0)
+ printk("No sync 4 occured\n");
+ via_read(via1, vSR);
+ via_write(via1, vBufB, via_read(via1, vBufB)|TIP);
+#endif
+ /*
+ * Ok we probably ;) have a ready to use adb bus. Its also
+ * hopefully idle (Im assuming the mac didnt leave a half
+ * complete transaction on booting us).
+ */
+
+ request_irq(IRQ_MAC_ADB, adb_interrupt, IRQ_FLG_LOCK,
+ "adb interrupt", adb_interrupt);
+ adb_state = idle;
+ break;
+ /*
+ * Unsupported; but later code doesn't notice !!
+ */
+ case MAC_ADB_CUDA:
+ printk("adb: CUDA interface.\n");
+#ifdef TRY_CUDA
+ /* don't know what to set up here ... */
+ adb_state = idle;
+ /* Set the lines up. We want TREQ as input TACK|TIP as output */
+ via_write(via1, vDirB, ((via_read(via1,vDirB)|TACK|TIP)&~TREQ));
+ request_irq(IRQ_MAC_ADB, adb_cuda_interrupt, IRQ_FLG_LOCK,
+ "adb CUDA interrupt", adb_cuda_interrupt);
+ break;
+#else
+ goto nosupp;
+#endif
+ case MAC_ADB_IISI:
+ printk("adb: Using IIsi hardware.\n");
+ goto nosupp;
+ default:
+ printk("adb: Unknown hardware interface.\n");
+ nosupp:
+ printk("adb: Interface unsupported.\n");
+ restore_flags(flags);
+ return;
+ }
+
+ /*
+ * XXX: interrupt only registered if supported HW !!
+ * -> unsupported HW will just time out on keyb_init!
+ */
+#if 0
+ request_irq(IRQ_MAC_ADB, adb_interrupt, IRQ_FLG_LOCK,
+ "adb interrupt", adb_interrupt);
+#endif
+#ifdef DEBUG_ADB_INTS
+ request_irq(IRQ_MAC_ADB_CL, adb_clock_interrupt, IRQ_FLG_LOCK,
+ "adb clock interrupt", adb_clock_interrupt);
+ request_irq(IRQ_MAC_ADB_SD, adb_data_interrupt, IRQ_FLG_LOCK,
+ "adb data interrupt", adb_data_interrupt);
+#endif
+
+ printk("adb: init done.\n");
+ restore_flags(flags);
+}
+
+#define WAIT_FOR(cond, what) \
+ do { \
+ for (x = 1000; !(cond); --x) { \
+ if (x == 0) { \
+ printk("Timeout waiting for " what); \
+ return 0; \
+ } \
+ __delay(100*160); \
+ } \
+ } while (0)
+
+/*
+ * Construct and send an adb request
+ * This function is the main entry point into the ADB driver from
+ * kernel code; it takes the request data supplied and populates the
+ * adb_request structure.
+ * In order to keep this interface independent from any assumption about
+ * the underlying ADB hardware, we take requests in CUDA format here,
+ * the ADB packet 'prefixed' with a packet type code.
+ * Non-CUDA hardware is confused by this, so we strip the packet type
+ * here depending on hardware type ...
+ */
+int adb_request(struct adb_request *req, void (*done)(struct adb_request *),
+ int nbytes, ...)
+{
+ va_list list;
+ int i, start;
+
+ va_start(list, nbytes);
+
+ /*
+ * skip first byte if not CUDA
+ */
+ if (macintosh_config->adb_type != MAC_ADB_CUDA) {
+ start = va_arg(list, int);
+ nbytes--;
+ }
+ req->nbytes = nbytes;
+ req->done = done;
+#if (ADBDEBUG & ADBDEBUG_REQUEST)
+ if (console_loglevel == 10)
+ printk("adb_request, data bytes: ");
+#endif
+ for (i = 0; i < nbytes; ++i) {
+ req->data[i] = va_arg(list, int);
+#if (ADBDEBUG & ADBDEBUG_REQUEST)
+ if (console_loglevel == 10)
+ printk("%x ", req->data[i]);
+#endif
+ }
+#if (ADBDEBUG & ADBDEBUG_REQUEST)
+ if (console_loglevel == 10)
+ printk(" !\n");
+#endif
+ va_end(list);
+ /*
+ * XXX: This might be fatal if no reply is generated (i.e. Listen) !
+ * Currently, the interrupt handler 'fakes' a reply on non-TALK
+ * commands for this reason.
+ * Also, we need a CUDA_AUTOPOLL emulation here for non-CUDA
+ * Macs, and some mechanism to remember the last issued TALK
+ * request for resending it repeatedly on timeout!
+ */
+ req->reply_expected = 1;
+ return adb_send_request(req);
+}
+
+/*
+ * Construct an adb request for later sending
+ * This function only populates the adb_request structure, without
+ * actually queueing it.
+ * Reason: Poll requests and Talk requests need to be handled in a way
+ * different from 'user' requests; no reply_expected is set and
+ * Poll requests need to be placed at the head of the request queue.
+ * Using adb_request results in implicit queueing at the tail of the
+ * request queue (duplicating the Poll) with reply_expected set.
+ * No adjustment of packet data is necessary, as this mechanisnm is not
+ * used by CUDA hardware (Autopoll used instead).
+ */
+int adb_build_request(struct adb_request *req, void (*done)(struct adb_request *),
+ int nbytes, ...)
+{
+ va_list list;
+ int i;
+
+ req->nbytes = nbytes;
+ req->done = done;
+ va_start(list, nbytes);
+#if (ADBDEBUG & ADBDEBUG_REQUEST)
+ if (console_loglevel == 10)
+ printk("adb__build_request, data bytes: ");
+#endif
+ /*
+ * skip first byte if not CUDA ?
+ */
+ for (i = 0; i < nbytes; ++i) {
+ req->data[i] = va_arg(list, int);
+#if (ADBDEBUG & ADBDEBUG_REQUEST)
+ if (console_loglevel == 10)
+ printk("%x ", req->data[i]);
+#endif
+ }
+#if (ADBDEBUG & ADBDEBUG_REQUEST)
+ if (console_loglevel == 10)
+ printk(" !\n");
+#endif
+ va_end(list);
+
+ req->reply_expected = 0;
+ return 0;
+}
+
+/*
+ * Send an ADB poll (Talk, tagged on the front of the request queue)
+ */
+void adb_queue_poll(void)
+{
+ static int pod=0;
+ static int in_poll=0;
+ static struct adb_request r;
+ unsigned long flags;
+
+ if(in_poll)
+ printk("Double poll!\n");
+
+ in_poll++;
+ pod++;
+ if(pod>7) /* 15 */
+ pod=0;
+
+#if (ADBDEBUG & ADBDEBUG_POLL)
+ if (console_loglevel == 10)
+ printk("adb: Polling %d\n",pod);
+#endif
+
+ /* XXX: that's a TALK, register 0, MacII version */
+ adb_build_request(&r,NULL, 1, (pod<<4|0xC));
+
+ r.reply_expected=0;
+ r.done=NULL;
+ r.sent=0;
+ r.got_reply=0;
+ r.reply_len=0;
+ save_flags(flags);
+ cli();
+ /* Poll inserted at head of queue ... */
+ r.next=current_req;
+ current_req=&r;
+ restore_flags(flags);
+ adb_start();
+ in_poll--;
+}
+
+/*
+ * Send an ADB retransmit (Talk, appended to the request queue)
+ */
+void adb_retransmit(int device)
+{
+ static int in_retransmit=0;
+ static struct adb_request rt;
+ unsigned long flags;
+
+ if(in_retransmit)
+ printk("Double retransmit!\n");
+
+ in_retransmit++;
+
+#if (ADBDEBUG & ADBDEBUG_POLL)
+ if (console_loglevel == 10)
+ printk("adb: Sending retransmit: %d\n", device);
+#endif
+
+ /* MacII version */
+ adb_build_request(&rt,NULL, 1, (device<<4|0xC));
+
+ rt.reply_expected = 0;
+ rt.done = NULL;
+ rt.sent = 0;
+ rt.got_reply = 0;
+ rt.reply_len = 0;
+ rt.next = NULL;
+
+ save_flags(flags);
+ cli();
+
+ /* Retransmit inserted at tail of queue ... */
+
+ if (current_req != NULL)
+ {
+ last_req->next = &rt;
+ last_req = &rt;
+ }
+ else
+ {
+ current_req = &rt;
+ last_req = &rt;
+ }
+
+ /* always restart driver (send_retransmit used in place of adb_start!)*/
+
+ if (adb_state == idle)
+ adb_start();
+
+ restore_flags(flags);
+ in_retransmit--;
+}
+
+/*
+ * Queue an ADB request; start ADB transfer if necessary
+ */
+int adb_send_request(struct adb_request *req)
+{
+ unsigned long flags;
+
+ req->next = 0;
+ req->sent = 0;
+ req->got_reply = 0;
+ req->reply_len = 0;
+ save_flags(flags);
+ cli();
+
+ if (current_req != NULL)
+ {
+ last_req->next = req;
+ last_req = req;
+ }
+ else
+ {
+ current_req = req;
+ last_req = req;
+ if (adb_state == idle)
+ adb_start();
+ }
+
+ restore_flags(flags);
+ return 0;
+}
+
+static int nclock, ndata;
+
+static int need_poll = 0;
+static int command_byte = 0;
+static int last_reply = 0;
+static int last_active = 0;
+
+static struct adb_request *retry_req;
+
+/*
+ * Start sending ADB packet
+ */
+static void adb_start(void)
+{
+ unsigned long flags;
+ struct adb_request *req;
+
+ /*
+ * We get here on three 'sane' conditions:
+ * 1) called from send_adb_request, if adb_state == idle
+ * 2) called from within adb_interrupt, if adb_state == idle
+ * (after receiving, or after sending a LISTEN)
+ * 3) called from within adb_interrupt, if adb_state == sending
+ * and no reply is expected (immediate next command).
+ * Maybe we get here on SRQ as well ??
+ */
+
+ /* get the packet to send */
+ req = current_req;
+ /* assert adb_state == idle */
+ if (adb_state != idle) {
+ printk("ADB: adb_start called while driver busy (%p %x %x)!\n",
+ req, adb_state, via_read(via1, vBufB)&(ST_MASK|TREQ));
+ return;
+ }
+ if (req == 0)
+ return;
+ save_flags(flags);
+ cli();
+
+#if (ADBDEBUG & ADBDEBUG_START)
+ if (console_loglevel == 10)
+ printk("adb_start: request %p ", req);
+#endif
+
+ nclock = 0;
+ ndata = 0;
+
+ /*
+ * IRQ signaled ?? (means ADB controller wants to send, or might
+ * be end of packet if we were reading)
+ */
+ if ((via_read(via1, vBufB)& TREQ) == 0)
+ {
+ switch(macintosh_config->adb_type)
+ {
+ /*
+ * FIXME - we need to restart this on a timer
+ * or a collision at boot hangs us.
+ * Never set adb_state to idle here, or adb_start
+ * won't be called again from send_request!
+ * (need to re-check other cases ...)
+ */
+ case MAC_ADB_CUDA:
+ /* printk("device busy - fail\n"); */
+ restore_flags(flags);
+ /* a byte is coming in from the CUDA */
+ return;
+ case MAC_ADB_II:
+ /*
+ * if the interrupt handler set the need_poll
+ * flag, it's hopefully a SRQ poll or re-Talk
+ * so we try to send here anyway
+ */
+ if (!need_poll) {
+ printk("device busy - retry %p state %d status %x!\n",
+ req, adb_state, via_read(via1, vBufB)&(ST_MASK|TREQ));
+ retry_req = req;
+ /* set ADB status here ? */
+ restore_flags(flags);
+ return;
+ } else {
+#if (ADBDEBUG & ADBDEBUG_START)
+ if (console_loglevel == 10)
+ printk("device busy - polling; state %d status %x!\n",
+ adb_state, via_read(via1, vBufB)&(ST_MASK|TREQ));
+#endif
+ need_poll = 0;
+ break;
+ }
+ }
+ }
+
+#if 0
+ /*
+ * Bus idle ?? Not sure about this one; SRQ might need ST_CMD here!
+ * OTOH: setting ST_CMD in the interrupt routine would make the
+ * ADB contoller shift in before this routine starts shifting out ...
+ */
+ if ((via_read(via1, vBufB)&ST_MASK) != ST_IDLE)
+ {
+#if (ADBDEBUG & ADBDEBUG_STATE)
+ if (console_loglevel == 10)
+ printk("ADB bus not idle (%x), retry later!\n",
+ via_read(via1, vBufB)&(ST_MASK|TREQ));
+#endif
+ retry_req = req;
+ restore_flags(flags);
+ return;
+ }
+#endif
+
+ /*
+ * Another retry pending? (sanity check)
+ */
+ if (retry_req) {
+#if (ADBDEBUG & ADBDEBUG_RETRY)
+ if (console_loglevel == 10)
+ if (retry_req == req)
+ /* new requests are appended at tail of request queue */
+ printk("adb_start: retry %p pending ! \n", req);
+ else
+ /* poll requests are added to the head of queue */
+ printk("adb_start: retry %p pending, req %p (poll?) current! \n",
+ retry_req, req);
+#endif
+ retry_req = NULL;
+ }
+
+ /*
+ * Seems OK, go for it!
+ */
+ switch(macintosh_config->adb_type)
+ {
+ case MAC_ADB_CUDA:
+ /* store command byte (first byte is 'type' byte) */
+ command_byte = req->data[1];
+ /* set the shift register to shift out and send a byte */
+ via_write(via1, vACR, via_read(via1, vACR)|SR_OUT);
+ via_write(via1, vSR, req->data[0]);
+ via_write(via1, vBufB, via_read(via1, vBufB)&~TIP);
+ break;
+ case MAC_ADB_II:
+ /* store command byte */
+ command_byte = req->data[0];
+ /* Output mode */
+ via_write(via1, vACR, via_read(via1, vACR)|SR_OUT);
+ /* Load data */
+ via_write(via1, vSR, req->data[0]);
+#ifdef USE_ORIG
+ /* Turn off TIP/TACK - this should tell the external logic to
+ start the external shift clock */
+/* via_write(via1, vBufB, via_read(via1, vBufB)&~(TIP|TACK));*/
+ via_write(via1, vBufB, via_read(via1, vBufB)|(TIP|TACK));
+#else
+ /* set ADB state to 'command' */
+ via_write(via1, vBufB, (via_read(via1, vBufB)&~ST_MASK)|ST_CMD);
+#endif
+ break;
+ }
+#if (ADBDEBUG & ADBDEBUG_START)
+ if (console_loglevel == 10)
+ printk("sent first byte of %d: %x, (%x %x) ... ",
+ req->nbytes, req->data[0], adb_state,
+ (via_read(via1, vBufB) & (ST_MASK|TREQ)) );
+#endif
+ adb_state = sent_first_byte;
+ data_index = 1;
+ restore_flags(flags);
+}
+
+/*
+ * Poll the ADB state (maybe obsolete now that interrupt-driven ADB runs)
+ */
+void adb_poll(void)
+{
+ unsigned char c;
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ c=via_read(via1, vIFR);
+#if (ADBDEBUG & ADBDEBUG_POLL)
+#ifdef DEBUG_ADB_INTS
+ if (console_loglevel == 10) {
+ printk("adb_poll: IFR %x state %x cl %d dat %d ",
+ c, adb_state, nclock, ndata);
+ if (c & (SR_CLOCK|SR_DATA)) {
+ if (c & SR_CLOCK)
+ printk("adb clock event ");
+ if (c & SR_DATA)
+ printk("adb data event ");
+ }
+ }
+#else
+ if (console_loglevel == 10)
+ printk("adb_poll: IFR %x state %x ",
+ c, adb_state);
+#endif
+ if (console_loglevel == 10)
+ printk("\r");
+#endif
+ if (c & SR_INT)
+ {
+#if (ADBDEBUG & ADBDEBUG_POLL)
+ if (console_loglevel == 10)
+ printk("adb_poll: adb interrupt event\n");
+#endif
+ adb_interrupt(0, 0, 0);
+ }
+ restore_flags(flags);
+}
+
+/*
+ * Debugging gimmicks
+ */
+void adb_clock_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+ nclock++;
+}
+
+void adb_data_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+ ndata++;
+}
+
+/*
+ * The notorious ADB interrupt handler - does all of the protocol handling,
+ * except for starting new send operations. Relies heavily on the ADB
+ * controller sending and receiving data, thereby generating SR interrupts
+ * for us. This means there has to be always activity on the ADB bus, otherwise
+ * the whole process dies and has to be re-kicked by sending TALK requests ...
+ * CUDA-based Macs seem to solve this with the autopoll option, for MacII-type
+ * ADB the problem isn't solved yet (retransmit of the latest active TALK seems
+ * a good choice; either on timeout or on a timer interrupt).
+ *
+ * The basic ADB state machine was left unchanged from the original MacII code
+ * by Alan Cox, which was based on the CUDA driver for PowerMac.
+ * The syntax of the ADB status lines seems to be totally different on MacII,
+ * though. MacII uses the states Command -> Even -> Odd -> Even ->...-> Idle for
+ * sending, and Idle -> Even -> Odd -> Even ->...-> Idle for receiving. Start
+ * and end of a receive packet are signaled by asserting /IRQ on the interrupt
+ * line. Timeouts are signaled by a sequence of 4 0xFF, with /IRQ asserted on
+ * every other byte. SRQ is probably signaled by 3 or more 0xFF tacked on the
+ * end of a packet. (Thanks to Guido Koerber for eavesdropping on the ADB
+ * protocol with a logic analyzer!!)
+ * CUDA seems to use /TIP -> /TIP | TACK -> /TIP -> /TIP | TACK ... -> TIP|TACK
+ * for sending, and /TIP -> /TIP | TACK -> /TIP -> /TIP | TACK ... -> TIP for
+ * receiving. No clue how timeouts are handled; SRQ seems to be sent as a
+ * separate packet. Quite a few changes have been made outside the handshake
+ * code, so I don't know if the CUDA code still behaves as before.
+ *
+ * Note: As of 21/10/97, the MacII ADB part works including timeout detection
+ * and retransmit (Talk to the last active device). Cleanup of code and
+ * testing of the CUDA functionality is required, though.
+ * Note2: As of 13/12/97, CUDA support is definitely broken ...
+ * Next TODO: implementation of IIsi ADB protocol (maybe the USE_ORIG
+ * conditionals can be a start?)
+ */
+void adb_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+ int x, adbdir;
+ struct adb_request *req;
+
+ last_status = status;
+
+#ifdef USE_ORIG
+ status = (~via_read(via1, vBufB) & (TIP|TREQ)) | (via_read(via1, vACR) & SR_OUT);
+#else
+ if (macintosh_config->adb_type==MAC_ADB_CUDA)
+ status = (~via_read(via1, vBufB) & (TIP|TREQ)) | (via_read(via1, vACR) & SR_OUT);
+ else
+ /* status bits (0x8->0x20) and direction (0x10 ??) CLASH !! */
+ status = (via_read(via1, vBufB) & (ST_MASK|TREQ));
+#endif
+ adbdir = (via_read(via1, vACR) & SR_OUT);
+#if (ADBDEBUG & ADBDEBUG_INT)
+ if (console_loglevel == 10)
+ printk("adb_interrupt: state=%d status=%x last=%x direction=%x\n",
+ adb_state, status, last_status, adbdir);
+#endif
+
+
+ switch (adb_state)
+ {
+ case idle:
+ if(macintosh_config->adb_type==MAC_ADB_CUDA)
+ {
+ /* CUDA has sent us the first byte of data - unsolicited */
+ if (status != TREQ)
+ printk("cuda: state=idle, status=%x\n", status);
+ x = via_read(via1, vSR);
+ via_write(via1, vBufB, via_read(via1,vBufB)&~TIP);
+ }
+ else if(macintosh_config->adb_type==MAC_ADB_II)
+ {
+#if (ADBDEBUG & ADBDEBUG_STATUS)
+ if (status == TREQ && !adbdir)
+ /* that's: not IRQ, idle, input -> weird */
+ printk("adb_macII: idle, status=%x dir=%x\n",
+ status, adbdir);
+#endif
+ x = via_read(via1, vSR);
+ first_byte = x;
+#if (ADBDEBUG & ADBDEBUG_READ)
+ if (console_loglevel == 10)
+ printk("adb_macII: receiving unsol. packet: %x (%x %x) ",
+ x, adb_state, status);
+#endif
+#ifdef USE_ORIG
+ via_write(via1, vBufB, via_read(via1, vBufB)&~(TIP|TACK));
+#else
+ /* set ADB state = even for first data byte */
+ via_write(via1, vBufB, (via_read(via1, vBufB)&~ST_MASK)|ST_EVEN);
+#endif
+ }
+ adb_state = reading;
+ reply_ptr = cuda_rbuf;
+ reply_len = 0;
+ reading_reply = 0;
+ prefix_len = 0;
+ if (macintosh_config->adb_type==MAC_ADB_II) {
+ *reply_ptr++ = ADB_PACKET;
+ *reply_ptr++ = first_byte;
+ *reply_ptr++ = command_byte; /*first_byte;*/
+ reply_len = 3;
+ prefix_len = 3;
+ }
+ break;
+
+ case awaiting_reply:
+ if(macintosh_config->adb_type==MAC_ADB_CUDA)
+ {
+ /* CUDA has sent us the first byte of data of a reply */
+ if (status != TREQ)
+ printk("cuda: state=awaiting_reply, status=%x\n", status);
+ x = via_read(via1, vSR);
+ via_write(via1,vBufB,
+ via_read(via1, vBufB)&~TIP);
+ } else if(macintosh_config->adb_type==MAC_ADB_II) {
+ /* handshake etc. for II ?? */
+ x = via_read(via1, vSR);
+ first_byte = x;
+#if (ADBDEBUG & ADBDEBUG_READ)
+ if (console_loglevel == 10)
+ printk("adb_macII: reading reply: %x (%x %x) ",
+ x, adb_state, status);
+#endif
+#ifdef USE_ORIG
+ via_write(via1, vBufB, via_read(via1, vBufB)&~(TIP|TACK));
+#else
+ /* set ADB state = even for first data byte */
+ via_write(via1, vBufB, (via_read(via1, vBufB)&~ST_MASK)|ST_EVEN);
+#endif
+ }
+ adb_state = reading;
+ reply_ptr = current_req->reply;
+ reading_reply = 1;
+ reply_len = 0;
+ prefix_len = 0;
+ if (macintosh_config->adb_type==MAC_ADB_II) {
+ *reply_ptr++ = ADB_PACKET;
+ *reply_ptr++ = first_byte;
+ *reply_ptr++ = first_byte; /* should be command byte */
+ reply_len = 3;
+ prefix_len = 3;
+ }
+ break;
+
+ case sent_first_byte:
+ if(macintosh_config->adb_type==MAC_ADB_CUDA)
+ {
+ if (status == TREQ + TIP + SR_OUT)
+ {
+ /* collision */
+ via_write(via1, vACR,
+ via_read(via1, vACR)&~SR_OUT);
+ x = via_read(via1, vSR);
+ via_write(via1, vBufB,
+ via_read(via1,vBufB)|TIP|TACK);
+ adb_state = idle;
+ }
+ else
+ {
+ /* assert status == TIP + SR_OUT */
+ if (status != TIP + SR_OUT)
+ printk("cuda: state=sent_first_byte status=%x\n", status);
+ via_write(via1,vSR,current_req->data[1]);
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)^TACK);
+ data_index = 2;
+ adb_state = sending;
+ }
+ }
+ else if(macintosh_config->adb_type==MAC_ADB_II)
+ {
+ /* how to detect a collision here ?? */
+#if (ADBDEBUG & ADBDEBUG_WRITE)
+ if (console_loglevel == 10)
+ printk(" sending: %x (%x %x) ",
+ current_req->data[1], adb_state, status);
+#endif
+ /* maybe we're already done (Talk, or Poll)? */
+ if (data_index >= current_req->nbytes)
+ {
+ /* assert it's a Talk ?? */
+ if ( (command_byte&0xc) != 0xc
+ && console_loglevel == 10 )
+ printk("ADB: single byte command, no Talk: %x!\n",
+ command_byte);
+#if (ADBDEBUG & ADBDEBUG_WRITE)
+ if (console_loglevel == 10)
+ printk(" -> end (%d of %d) (%x %x)!\n",
+ data_index, current_req->nbytes, adb_state, status);
+#endif
+ current_req->sent = 1;
+ if (current_req->reply_expected)
+ {
+#if (ADBDEBUG & ADBDEBUG_WRITE)
+ if (console_loglevel == 10)
+ printk("ADB: reply expected on poll!\n");
+#endif
+ adb_state = awaiting_reply;
+ reading_reply = 0;
+ } else {
+#if (ADBDEBUG & ADBDEBUG_WRITE)
+ if (console_loglevel == 10)
+ printk("ADB: no reply for poll, not calling done()!\n");
+#endif
+ req = current_req;
+ current_req = req->next;
+#if 0 /* XXX Not sure about that one ... probably better enabled */
+ if (req->done)
+ (*req->done)(req);
+#endif
+ adb_state = idle;
+ reading_reply = 0;
+ }
+ /* set to shift in */
+ via_write(via1, vACR,
+ via_read(via1, vACR) & ~SR_OUT);
+ x=via_read(via1, vSR);
+#ifdef USE_ORIG
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)|TACK|TIP);
+#else
+ /* set ADB state idle - might get SRQ */
+ via_write(via1, vBufB,
+ (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
+#endif
+ break;
+ }
+#if (ADBDEBUG & ADBDEBUG_STATUS)
+ if(!(status==(ST_CMD|TREQ) && adbdir == SR_OUT))
+ printk("adb_macII: sent_first_byte, weird status=%x dir=%x\n",
+ status, adbdir);
+#endif
+ /* SR already set to shift out; send byte */
+ via_write(via1, vSR, current_req->data[1]);
+#ifdef USE_ORIG
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)^TACK);
+#else
+ /* set state to ST_EVEN (first byte was: ST_CMD) */
+ via_write(via1, vBufB,
+ (via_read(via1, vBufB)&~ST_MASK)|ST_EVEN);
+#endif
+ data_index=2;
+ adb_state = sending;
+ }
+ break;
+
+ case sending:
+ req = current_req;
+ if (data_index >= req->nbytes)
+ {
+ /* end of packet */
+ if(macintosh_config->adb_type==MAC_ADB_CUDA)
+ {
+ via_write(via1, vACR,
+ via_read(via1, vACR)&~SR_OUT);
+ x = via_read(via1, vSR);
+ via_write(via1, vBufB,
+ via_read(via1,vBufB)|TACK|TIP);
+ }
+ else if(macintosh_config->adb_type==MAC_ADB_II)
+ {
+ /*
+ * XXX Not sure: maybe only switch to
+ * input mode on Talk ??
+ */
+#if (ADBDEBUG & ADBDEBUG_WRITE)
+ if (console_loglevel == 10)
+ printk(" -> end (%d of %d) (%x %x)!\n",
+ data_index-1, req->nbytes, adb_state, status);
+#endif
+ /* set to shift in */
+ via_write(via1, vACR,
+ via_read(via1, vACR) & ~SR_OUT);
+ x=via_read(via1, vSR);
+#ifdef USE_ORIG
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)|TACK|TIP);
+#else
+ /* set ADB state idle - might get SRQ */
+ via_write(via1, vBufB,
+ (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
+#endif
+ }
+ req->sent = 1;
+ if (req->reply_expected)
+ {
+ /*
+ * maybe fake a reply here on Listen ??
+ * Otherwise, a Listen hangs on success
+ */
+ if ( ((req->data[0]&0xc) == 0xc) )
+ adb_state = awaiting_reply;
+ else {
+ /*
+ * Reply expected, but none
+ * possible -> fake reply.
+ * Problem: sending next command
+ * should probably be done
+ * without setting bus to 'idle'!
+ * (except if no more commands)
+ */
+#if (ADBDEBUG & ADBDEBUG_PROT)
+ printk("ADB: reply expected on Listen, faking reply\n");
+#endif
+ /* make it look weird */
+ /* XXX: return reply_len -1? */
+ /* XXX: fake ADB header? */
+ req->reply[0] = req->reply[1] = req->reply[2] = 0xFF;
+ req->reply_len = 3;
+ req->got_reply = 1;
+ current_req = req->next;
+ if (req->done)
+ (*req->done)(req);
+ /*
+ * ready with this one, run
+ * next command or repeat last
+ * Talk (=idle on II)
+ */
+ /* set state to idle !! */
+ adb_state = idle;
+ if (current_req || retry_req)
+ adb_start();
+ }
+ }
+ else
+ {
+ current_req = req->next;
+ if (req->done)
+ (*req->done)(req);
+ /* not sure about this */
+ /*
+ * MS: Must set idle, no new request
+ * started else !
+ */
+ adb_state = idle;
+ /*
+ * requires setting ADB state to idle,
+ * maybe read a byte ! (done above)
+ */
+ if (current_req || retry_req)
+ adb_start();
+ }
+ }
+ else
+ {
+ if(macintosh_config->adb_type==MAC_ADB_CUDA)
+ {
+ via_write(via1, vSR, req->data[data_index++]);
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)^TACK);
+ }
+ else if(macintosh_config->adb_type==MAC_ADB_II)
+ {
+#if (ADBDEBUG & ADBDEBUG_WRITE)
+ if (console_loglevel == 10)
+ printk(" %x (%x %x) ",
+ req->data[data_index], adb_state, status);
+#endif
+ via_write(via1, vSR, req->data[data_index++]);
+#ifdef USE_ORIG
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)^TACK);
+#else
+ /* invert state bits, toggle ODD/EVEN */
+ x = via_read(via1, vBufB);
+ via_write(via1, vBufB,
+ (x&~ST_MASK)|~(x&ST_MASK));
+#endif
+ }
+ }
+ break;
+
+ case reading:
+#ifdef POLL_ON_TIMEOUT
+ if((reply_len-prefix_len)==3 && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0)
+#else
+ if( (first_byte == 0xFF && (reply_len-prefix_len)==2
+ && memcmp(reply_ptr-2,"\xFF\xFF",2)==0) ||
+ ((reply_len-prefix_len)==3
+ && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0))
+#endif
+ {
+ /*
+ * possible timeout (in fact, most probably a
+ * timeout, since SRQ can't be signaled without
+ * transfer on the bus).
+ * The last three bytes seen were FF, together
+ * with the starting byte (in case we started
+ * on 'idle' or 'awaiting_reply') this probably
+ * makes four. So this is mostl likely #5!
+ * The timeout signal is a pattern 1 0 1 0 0..
+ * on /INT, meaning we missed it :-(
+ */
+ x = via_read(via1, vSR);
+ if (x != 0xFF)
+ printk("ADB: mistaken timeout/SRQ!\n");
+
+ /*
+ * ADB status bits: either even or odd.
+ * adb_state: need to set 'idle' here.
+ * Maybe saner: set 'need_poll' or
+ * 'need_resend' here, fall through to
+ * read_done ??
+ */
+#if (ADBDEBUG & ADBDEBUG_READ)
+ if (console_loglevel == 10)
+ printk(" -> read aborted: %x (%x %x)!\n",
+ x, adb_state, status);
+#endif
+
+#ifdef USE_ORIG
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)|TACK|TIP);
+#else
+#if 0 /* XXX leave status unchanged!! - need to check this again! */
+ /* set ADB state to idle (required by adb_start()) */
+ via_write(via1, vBufB,
+ (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
+#endif
+#endif
+
+ /*
+ * What if the timeout happens on reading a
+ * reply ?? Assemble error reply and call
+ * current_request->done()? Keep request
+ * on queue?
+ */
+
+ /* prevent 'busy' in adb_start() */
+ need_poll = 1;
+
+ /*
+ * Timeout: /IRQ alternates high/low during
+ * 4 'FF' bytes (1 0 1 0 0...)
+ * We're on byte 5, so we need one
+ * more backlog here (TBI) ....
+ */
+ if ((status&TREQ) != (last_status&TREQ)) {
+#if (ADBDEBUG & ADBDEBUG_SRQ)
+ if (console_loglevel == 10)
+ printk("ADB: reply timeout, resending!\n");
+#endif
+ /*
+ * first byte received should be the
+ * command byte timing out !!
+ */
+ if (first_byte != 0xff)
+ command_byte = first_byte;
+
+ /*
+ * compute target for retransmit: if
+ * last_active is set, use that one,
+ * else use command_byte
+ */
+ if (last_active == -1)
+ last_active = (command_byte&0xf0)>>4;
+ adb_state = idle;
+ /* resend if TALK, don't poll! */
+ if (current_req)
+ adb_start();
+ else
+ /*
+ * XXX: need to count the timeouts ??
+ * restart last active TALK ??
+ * If no current_req, reuse old one!
+ */
+ adb_retransmit(last_active);
+
+ } else {
+ /*
+ * SRQ: NetBSD suggests /IRQ is asserted!?
+ */
+ if (status&TREQ)
+ printk("ADB: SRQ signature w/o /INT!\n");
+#if (ADBDEBUG & ADBDEBUG_SRQ)
+ if (console_loglevel == 10)
+ printk("ADB: empty SRQ packet!\n");
+#endif
+ /* Terminate the SRQ packet and poll */
+ adb_state = idle;
+ adb_queue_poll();
+ }
+ /*
+ * Leave ADB status lines unchanged (means /IRQ
+ * will still be low when entering adb_start!)
+ */
+ break;
+ }
+ if((reply_len-prefix_len)>3 && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0)
+ {
+ /* SRQ tacked on data packet */
+ /* Check /IRQ here ?? */
+#if (ADBDEBUG & ADBDEBUG_SRQ)
+ if (console_loglevel == 10)
+ printk("\nADB: Packet with SRQ!\n");
+#endif
+ /* Terminate the packet (SRQ never ends) */
+ x = via_read(via1, vSR);
+ adb_state = read_done;
+ reply_len -= 3;
+ reply_ptr -= 3;
+ need_poll = 1;
+ /* need to continue; next byte not seen else */
+ /*
+ * XXX: not at all sure here; maybe need to
+ * send away the reply and poll immediately?
+ */
+ } else {
+ /* Sanity check */
+ if(reply_len>15)
+ reply_len=0;
+ /* read byte */
+ *reply_ptr = via_read(via1, vSR);
+ x = *reply_ptr;
+#if (ADBDEBUG & ADBDEBUG_READ)
+ if (console_loglevel == 10)
+ printk(" %x (%x %x) ",
+ *reply_ptr, adb_state, status);
+#endif
+ reply_ptr++;
+ reply_len++;
+ }
+ /* The usual handshake ... */
+ if(macintosh_config->adb_type==MAC_ADB_CUDA)
+ {
+ if (status == TIP)
+ {
+ /* that's all folks */
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)|TACK|TIP);
+ adb_state = read_done;
+ }
+ else
+ {
+ /* assert status == TIP | TREQ */
+ if (status != TIP + TREQ)
+ printk("cuda: state=reading status=%x\n", status);
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)^TACK);
+ }
+ }
+ if(macintosh_config->adb_type==MAC_ADB_II)
+ {
+ /*
+ * NetBSD hints that the next to last byte
+ * is sent with IRQ !!
+ * Guido found out it's the last one (0x0),
+ * but IRQ should be asserted already.
+ * Problem with timeout detection: First
+ * transition to /IRQ might be second
+ * byte of timeout packet!
+ * Timeouts are signaled by 4x FF.
+ */
+ if(!(status&TREQ) && x == 0x00) /* != 0xFF */
+ {
+#if (ADBDEBUG & ADBDEBUG_READ)
+ if (console_loglevel == 10)
+ printk(" -> read done!\n");
+#endif
+#ifdef USE_ORIG
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)|TACK|TIP);
+#else
+#if 0 /* XXX: we take one more byte (why?), so handshake! */
+ /* set ADB state to idle */
+ via_write(via1, vBufB,
+ (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
+#else
+ /* invert state bits, toggle ODD/EVEN */
+ x = via_read(via1, vBufB);
+ via_write(via1, vBufB,
+ (x&~ST_MASK)|~(x&ST_MASK));
+#endif
+#endif
+ /* adjust packet length */
+ reply_len--;
+ reply_ptr--;
+ adb_state = read_done;
+ }
+ else
+ {
+#if (ADBDEBUG & ADBDEBUG_STATUS)
+ if(status!=TIP+TREQ)
+ printk("macII_adb: state=reading status=%x\n", status);
+#endif
+#ifdef USE_ORIG
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)^TACK);
+#else
+ /* not caught: ST_CMD */
+ /* required for re-entry 'reading'! */
+ if ((status&ST_MASK) == ST_IDLE) {
+ /* (in)sanity check - set even */
+ via_write(via1, vBufB,
+ (via_read(via1, vBufB)&~ST_MASK)|ST_EVEN);
+ } else {
+ /* invert state bits, toggle ODD/EVEN */
+ x = via_read(via1, vBufB);
+ via_write(via1, vBufB,
+ (x&~ST_MASK)|~(x&ST_MASK));
+ }
+#endif
+ }
+ }
+ break;
+
+ case read_done:
+ x = via_read(via1, vSR);
+#if (ADBDEBUG & ADBDEBUG_READ)
+ if (console_loglevel == 10)
+ printk("ADB: read done: %x (%x %x)!\n",
+ x, adb_state, status);
+#endif
+ if (reading_reply)
+ {
+ req = current_req;
+ req->reply_len = reply_ptr - req->reply;
+ req->got_reply = 1;
+ current_req = req->next;
+ if (req->done)
+ (*req->done)(req);
+ }
+ else
+ {
+ adb_input(cuda_rbuf, reply_ptr - cuda_rbuf, regs);
+ }
+
+ /*
+ * remember this device ID; it's the latest we got a
+ * reply from!
+ */
+ last_reply = command_byte;
+ last_active = (command_byte&0xf0)>>4;
+
+
+ /*
+ * Assert status = ST_IDLE ??
+ */
+ /*
+ * SRQ seen before, initiate poll now
+ */
+ if (need_poll) {
+#if (ADBDEBUG & ADBDEBUG_POLL)
+ if (console_loglevel == 10)
+ printk("ADB: initiate poll!\n");
+#endif
+ adb_state = idle;
+ /*
+ * set ADB status bits?? (unchanged above!)
+ */
+ adb_queue_poll();
+ need_poll = 0;
+ /* hope this is ok; queue_poll runs adb_start */
+ break;
+ }
+
+#ifdef USE_ORIG
+ /*
+ * This will fail - TREQ is active low -> 0 is IRQ !!
+ */
+ if (status == TREQ)
+ {
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)|~TIP);
+#else
+ /*
+ * /IRQ seen, so the ADB controller has data for us
+ */
+ if (!(status&TREQ))
+ {
+ /* set ADB state to idle */
+ via_write(via1, vBufB,
+ (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
+#endif
+ adb_state = reading;
+ reply_ptr = cuda_rbuf;
+ reply_len = 0;
+ prefix_len = 0;
+ if (macintosh_config->adb_type==MAC_ADB_II) {
+ *reply_ptr++ = ADB_PACKET;
+ *reply_ptr++ = command_byte;
+ reply_len = 2;
+ prefix_len = 2;
+ }
+ reading_reply = 0;
+ }
+ else
+ {
+ /*
+ * no IRQ, send next packet or wait
+ */
+ adb_state = idle;
+ if (current_req)
+ adb_start();
+ else
+ adb_retransmit(last_active);
+ }
+ break;
+
+ default:
+#if (ADBDEBUG & ADBDEBUG_STATE)
+ printk("adb_interrupt: unknown adb_state %d?\n", adb_state);
+#endif
+ }
+}
+
+/*
+ * Restart of CUDA support: please modify this interrupt handler while
+ * working at the Quadra etc. ADB driver. We can try to merge them later, or
+ * remove the CUDA stuff from the MacII handler
+ */
+
+void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+ int x, status;
+ struct adb_request *req;
+
+ status = (~via_read(via1, vBufB) & (TIP|TREQ)) | (via_read(via1, vACR) & SR_OUT);
+ if (console_loglevel == 10)
+ printk("adb_interrupt: state=%d status=%x\n", adb_state, status);
+
+ switch (adb_state)
+ {
+ case idle:
+ if(macintosh_config->adb_type==MAC_ADB_CUDA)
+ {
+ /* CUDA has sent us the first byte of data - unsolicited */
+ if (status != TREQ)
+ printk("cuda: state=idle, status=%x want=%x\n",
+ status, TREQ);
+ x = via_read(via1, vSR);
+ via_write(via1, vBufB, via_read(via1,vBufB)&~TIP);
+ }
+ else if(macintosh_config->adb_type==MAC_ADB_II)
+ {
+ if (status != TREQ)
+ printk("adb_macII: state=idle status=%x want=%x\n",
+ status, TREQ);
+ x = via_read(via1, vSR);
+ via_write(via1, vBufB, via_read(via1, vBufB)&~(TIP|TACK));
+ }
+ adb_state = reading;
+ reply_ptr = cuda_rbuf;
+ reply_len = 0;
+ reading_reply = 0;
+ break;
+
+ case awaiting_reply:
+ if(macintosh_config->adb_type==MAC_ADB_CUDA)
+ {
+ /* CUDA has sent us the first byte of data of a reply */
+ if (status != TREQ)
+ printk("cuda: state=awaiting_reply, status=%x want=%x\n",
+ status, TREQ);
+ x = via_read(via1, vSR);
+ via_write(via1,vBufB,
+ via_read(via1, vBufB)&~TIP);
+ }
+ adb_state = reading;
+ reply_ptr = current_req->reply;
+ reading_reply = 1;
+ reply_len = 0;
+ break;
+
+ case sent_first_byte:
+ if(macintosh_config->adb_type==MAC_ADB_CUDA)
+ {
+ if (status == TREQ + TIP + SR_OUT)
+ {
+ /* collision */
+ if (console_loglevel == 10)
+ printk("cuda: send collision!\n");
+ via_write(via1, vACR,
+ via_read(via1, vACR)&~SR_OUT);
+ x = via_read(via1, vSR);
+ via_write(via1, vBufB,
+ via_read(via1,vBufB)|TIP|TACK);
+ adb_state = idle;
+ }
+ else
+ {
+ /* assert status == TIP + SR_OUT */
+ if (status != TIP + SR_OUT)
+ printk("cuda: state=sent_first_byte status=%x want=%x\n",
+ status, TIP + SR_OUT);
+ via_write(via1,vSR,current_req->data[1]);
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)^TACK);
+ data_index = 2;
+ adb_state = sending;
+ }
+ }
+ else if(macintosh_config->adb_type==MAC_ADB_II)
+ {
+ if(status!=TIP+SR_OUT)
+ printk("adb_macII: state=send_first_byte status=%x want=%x\n",
+ status, TIP+SR_OUT);
+ via_write(via1, vSR, current_req->data[1]);
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)^TACK);
+ data_index=2;
+ adb_state = sending;
+ }
+ break;
+
+ case sending:
+ req = current_req;
+ if (data_index >= req->nbytes)
+ {
+ if(macintosh_config->adb_type==MAC_ADB_CUDA)
+ {
+ via_write(via1, vACR,
+ via_read(via1, vACR)&~SR_OUT);
+ x = via_read(via1, vSR);
+ via_write(via1, vBufB,
+ via_read(via1,vBufB)|TACK|TIP);
+ }
+ else if(macintosh_config->adb_type==MAC_ADB_II)
+ {
+ via_write(via1, vACR,
+ via_read(via1, vACR) & ~SR_OUT);
+ x=via_read(via1, vSR);
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)|TACK|TIP);
+ }
+ req->sent = 1;
+ if (req->reply_expected)
+ {
+ adb_state = awaiting_reply;
+ }
+ else
+ {
+ current_req = req->next;
+ if (req->done)
+ (*req->done)(req);
+ /* not sure about this */
+ adb_state = idle;
+ adb_start();
+ }
+ }
+ else
+ {
+ if(macintosh_config->adb_type==MAC_ADB_CUDA)
+ {
+ via_write(via1, vSR, req->data[data_index++]);
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)^TACK);
+ }
+ else if(macintosh_config->adb_type==MAC_ADB_II)
+ {
+ via_write(via1, vSR, req->data[data_index++]);
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)^TACK);
+ }
+ }
+ break;
+
+ case reading:
+ if(reply_len==3 && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0)
+ {
+ /* Terminate the SRQ packet */
+ printk("CUDA: Got an SRQ\n");
+ adb_state = idle;
+ adb_queue_poll();
+ break;
+ }
+ /* Sanity check - botched in orig. code! */
+ if(reply_len>15) {
+ printk("CUDA: reply buffer overrun!\n");
+ /* wrap buffer */
+ reply_len=0;
+ if (reading_reply)
+ reply_ptr = current_req->reply;
+ else
+ reply_ptr = cuda_rbuf;
+ }
+ *reply_ptr = via_read(via1, vSR);
+ if (console_loglevel == 10)
+ printk(" %p-> %x (%x %x) ",
+ reply_ptr, *reply_ptr, adb_state, status);
+ reply_ptr++;
+ reply_len++;
+ if(macintosh_config->adb_type==MAC_ADB_CUDA)
+ {
+ if (status == TIP)
+ {
+ /* that's all folks */
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)|TACK|TIP);
+ adb_state = read_done;
+ }
+ else
+ {
+ /* assert status == TIP | TREQ */
+ if (status != TIP + TREQ)
+ printk("cuda: state=reading status=%x want=%x\n",
+ status, TIP + TREQ);
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)^TACK);
+ }
+ }
+ if(macintosh_config->adb_type==MAC_ADB_II)
+ {
+ if( status == TIP)
+ {
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)|TACK|TIP);
+ adb_state = read_done;
+ }
+ else
+ {
+ if(status!=TIP+TREQ)
+ printk("macII_adb: state=reading status=%x\n", status);
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)^TACK);
+ }
+ }
+ break;
+
+ case read_done:
+ x = via_read(via1, vSR);
+ if (reading_reply)
+ {
+ req = current_req;
+ req->reply_len = reply_ptr - req->reply;
+ req->got_reply = 1;
+ current_req = req->next;
+ if (req->done)
+ (*req->done)(req);
+ }
+ else
+ {
+ adb_input(cuda_rbuf, reply_ptr - cuda_rbuf, regs);
+ }
+
+ if (status == TREQ)
+ {
+ via_write(via1, vBufB,
+ via_read(via1, vBufB)|~TIP);
+ adb_state = reading;
+ reply_ptr = cuda_rbuf;
+ reading_reply = 0;
+ }
+ else
+ {
+ adb_state = idle;
+ adb_start();
+ }
+ break;
+
+ default:
+ printk("adb_interrupt: unknown adb_state %d?\n", adb_state);
+ }
+}
+
+/*
+ * The 'reply delivery' routine; determines which device sent the
+ * request and calls the appropriate handler.
+ * Reply data are expected in CUDA format (again, argh...) so we need
+ * to fake this in the interrupt handler for MacII.
+ * Only one handler per device ID is currently possible.
+ * XXX: is the ID field here representing the default or real ID?
+ */
+static void adb_input(unsigned char *buf, int nb, struct pt_regs *regs)
+{
+ int i, id;
+
+ switch (buf[0])
+ {
+ case ADB_PACKET:
+ /* what's in buf[1] ?? */
+ id = buf[2] >> 4;
+#if 0
+ xmon_printf("adb packet: ");
+ for (i = 0; i < nb; ++i)
+ xmon_printf(" %x", buf[i]);
+ xmon_printf(", id = %d\n", id);
+#endif
+#if (ADBDEBUG & ADBDEBUG_INPUT)
+ if (console_loglevel == 10) {
+ printk("adb_input: adb packet ");
+ for (i = 0; i < nb; ++i)
+ printk(" %x", buf[i]);
+ printk(", id = %d\n", id);
+ }
+#endif
+ if (adb_handler[id].handler != 0)
+ {
+ (*adb_handler[id].handler)(buf, nb, regs);
+ }
+ break;
+
+ default:
+#if (ADBDEBUG & ADBDEBUG_INPUT)
+ if (console_loglevel == 10) {
+ printk("adb_input: data from via (%d bytes):", nb);
+ for (i = 0; i < nb; ++i)
+ printk(" %.2x", buf[i]);
+ printk("\n");
+ }
+#endif
+ }
+}
+
+/* Ultimately this should return the number of devices with
+ the given default id. */
+
+int adb_register(int default_id,
+ void (*handler)(unsigned char *, int, struct pt_regs *))
+{
+ if (adb_handler[default_id].handler != 0)
+ panic("Two handlers for ADB device %d\n", default_id);
+ adb_handler[default_id].handler = handler;
+ return 1;
+}
+
+/*
+ * /dev/adb device driver.
+ */
+
+#define ADB_MAJOR 56 /* major number for /dev/adb */
+
+#define ADB_MAX_MINOR 64 /* range of ADB minors */
+#define ADB_TYPE_SHIFT 4 /* # bits for device ID/type in subdevices */
+
+#define ADB_TYPE_RAW 0 /* raw device; unbuffered */
+#define ADB_TYPE_BUFF 1 /* raw device; buffered */
+#define ADB_TYPE_COOKED 2 /* 'cooked' device */
+
+
+extern void adbdev_init(void);
+
+struct adbdev_state {
+ struct adb_request req;
+};
+
+static struct wait_queue *adb_wait;
+
+static int adb_wait_reply(struct adbdev_state *state, struct file *file)
+{
+ int ret = 0;
+ struct wait_queue wait = { current, NULL };
+
+ add_wait_queue(&adb_wait, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+
+ while (!state->req.got_reply) {
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&adb_wait, &wait);
+
+ return ret;
+}
+
+static void adb_write_done(struct adb_request *req)
+{
+ if (!req->got_reply) {
+ req->reply_len = 0;
+ req->got_reply = 1;
+ }
+ wake_up_interruptible(&adb_wait);
+}
+
+struct file_operations *adb_raw[16];
+struct file_operations *adb_buffered[16];
+struct file_operations *adb_cooked[16];
+
+static int adb_open(struct inode *inode, struct file *file)
+{
+ int adb_type, adb_subtype;
+ struct adbdev_state *state;
+
+ if (MINOR(inode->i_rdev) > ADB_MAX_MINOR)
+ return -ENXIO;
+
+ switch (MINOR(inode->i_rdev) >> ADB_TYPE_SHIFT) {
+ case ADB_TYPE_RAW:
+ /* see code below */
+ break;
+ case ADB_TYPE_BUFF:
+ /* TBI */
+ return -ENXIO;
+ case ADB_TYPE_COOKED:
+ /* subtypes such as kbd, mouse, ... */
+ adb_subtype = MINOR(inode->i_rdev) & ~ADB_TYPE_SHIFT;
+ if ((file->f_op = adb_cooked[adb_subtype]))
+ return file->f_op->open(inode,file);
+ else
+ return -ENODEV;
+ }
+
+ state = kmalloc(sizeof(struct adbdev_state), GFP_KERNEL);
+ if (state == 0)
+ return -ENOMEM;
+ file->private_data = state;
+ state->req.reply_expected = 0;
+ return 0;
+}
+
+static void adb_release(struct inode *inode, struct file *file)
+{
+ struct adbdev_state *state = file->private_data;
+
+ if (state) {
+ file->private_data = NULL;
+ if (state->req.reply_expected && !state->req.got_reply)
+ if (adb_wait_reply(state, file))
+ return;
+ kfree(state);
+ }
+ return;
+}
+
+static int adb_lseek(struct inode *inode, struct file *file,
+ off_t offset, int origin)
+{
+ return -ESPIPE;
+}
+
+static int adb_read(struct inode *inode, struct file *file,
+ char *buf, int count)
+{
+ int ret;
+ struct adbdev_state *state = file->private_data;
+
+ if (count < 2)
+ return -EINVAL;
+ if (count > sizeof(state->req.reply))
+ count = sizeof(state->req.reply);
+ ret = verify_area(VERIFY_WRITE, buf, count);
+ if (ret)
+ return ret;
+
+ if (!state->req.reply_expected)
+ return 0;
+
+ ret = adb_wait_reply(state, file);
+ if (ret)
+ return ret;
+
+ state->req.reply_expected = 0;
+ ret = state->req.reply_len;
+ copy_to_user(buf, state->req.reply, ret);
+
+ return ret;
+}
+
+static int adb_write(struct inode *inode, struct file *file,
+ const char *buf, int count)
+{
+ int ret, i;
+ struct adbdev_state *state = file->private_data;
+
+ if (count < 2 || count > sizeof(state->req.data))
+ return -EINVAL;
+ ret = verify_area(VERIFY_READ, buf, count);
+ if (ret)
+ return ret;
+
+ if (state->req.reply_expected && !state->req.got_reply) {
+ /* A previous request is still being processed.
+ Wait for it to finish. */
+ ret = adb_wait_reply(state, file);
+ if (ret)
+ return ret;
+ }
+
+ state->req.nbytes = count;
+ state->req.done = adb_write_done;
+ state->req.got_reply = 0;
+ copy_from_user(state->req.data, buf, count);
+#if 0
+ switch (adb_hardware) {
+ case ADB_NONE:
+ return -ENXIO;
+ case ADB_VIACUDA:
+ state->req.reply_expected = 1;
+ cuda_send_request(&state->req);
+ break;
+ default:
+#endif
+ if (state->req.data[0] != ADB_PACKET)
+ return -EINVAL;
+ for (i = 1; i < state->req.nbytes; ++i)
+ state->req.data[i] = state->req.data[i+1];
+ state->req.reply_expected =
+ ((state->req.data[0] & 0xc) == 0xc);
+ adb_send_request(&state->req);
+#if 0
+ break;
+ }
+#endif
+
+ return count;
+}
+
+static struct file_operations adb_fops = {
+ adb_lseek,
+ adb_read,
+ adb_write,
+ NULL, /* no readdir */
+ NULL, /* no poll yet */
+ NULL, /* no ioctl yet */
+ NULL, /* no mmap */
+ adb_open,
+ adb_release
+};
+
+int adbdev_register(int subtype, struct file_operations *fops)
+{
+ if (subtype < 0 || subtype > 15)
+ return -EINVAL;
+ if (adb_cooked[subtype])
+ return -EBUSY;
+ adb_cooked[subtype] = fops;
+ return 0;
+}
+
+int adbdev_unregister(int subtype)
+{
+ if (subtype < 0 || subtype > 15)
+ return -EINVAL;
+ if (!adb_cooked[subtype])
+ return -ENODEV;
+ adb_cooked[subtype] = NULL;
+ return 0;
+}
+
+void adbdev_init()
+{
+ if (register_chrdev(ADB_MAJOR, "adb", &adb_fops))
+ printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR);
+}
+
+
+#if 0 /* old ADB device */
+
+/*
+ * Here are the file operations we export for /dev/adb.
+ */
+
+#define ADB_MINOR 140 /* /dev/adb is c 10 140 */
+
+extern void adbdev_inits(void);
+
+struct adbdev_state {
+ struct adb_request req;
+};
+
+static struct wait_queue *adb_wait;
+
+static int adb_wait_reply(struct adbdev_state *state, struct file *file)
+{
+ int ret = 0;
+ struct wait_queue wait = { current, NULL };
+
+#if (ADBDEBUG & ADBDEBUG_DEVICE)
+ printk("ADB request: wait_reply (blocking ... \n");
+#endif
+
+ add_wait_queue(&adb_wait, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+
+ while (!state->req.got_reply) {
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&adb_wait, &wait);
+
+ return ret;
+}
+
+static void adb_write_done(struct adb_request *req)
+{
+ if (!req->got_reply) {
+ req->reply_len = 0;
+ req->got_reply = 1;
+ }
+ wake_up_interruptible(&adb_wait);
+}
+
+static int adb_open(struct inode *inode, struct file *file)
+{
+ struct adbdev_state *state;
+
+ state = kmalloc(sizeof(struct adbdev_state), GFP_KERNEL);
+ if (state == 0)
+ return -ENOMEM;
+ file->private_data = state;
+ state->req.reply_expected = 0;
+ return 0;
+}
+
+static void adb_release(struct inode *inode, struct file *file)
+{
+ struct adbdev_state *state = file->private_data;
+
+ if (state) {
+ file->private_data = NULL;
+ if (state->req.reply_expected && !state->req.got_reply)
+ if (adb_wait_reply(state, file))
+ return;
+ kfree(state);
+ }
+ return;
+}
+
+static int adb_lseek(struct inode *inode, struct file *file,
+ off_t offset, int origin)
+{
+ return -ESPIPE;
+}
+
+static int adb_read(struct inode *inode, struct file *file,
+ char *buf, int count)
+{
+ int ret;
+ struct adbdev_state *state = file->private_data;
+
+ if (count < 2)
+ return -EINVAL;
+ if (count > sizeof(state->req.reply))
+ count = sizeof(state->req.reply);
+ ret = verify_area(VERIFY_WRITE, buf, count);
+ if (ret)
+ return ret;
+
+ if (!state->req.reply_expected)
+ return 0;
+
+ ret = adb_wait_reply(state, file);
+ if (ret)
+ return ret;
+
+ ret = state->req.reply_len;
+ memcpy_tofs(buf, state->req.reply, ret);
+ state->req.reply_expected = 0;
+
+ return ret;
+}
+
+static int adb_write(struct inode *inode, struct file *file,
+ const char *buf, int count)
+{
+ int ret;
+ struct adbdev_state *state = file->private_data;
+
+ if (count < 2 || count > sizeof(state->req.data))
+ return -EINVAL;
+ ret = verify_area(VERIFY_READ, buf, count);
+ if (ret)
+ return ret;
+
+ if (state->req.reply_expected && !state->req.got_reply) {
+ /* A previous request is still being processed.
+ Wait for it to finish. */
+ ret = adb_wait_reply(state, file);
+ if (ret)
+ return ret;
+ }
+
+ state->req.nbytes = count;
+ state->req.done = adb_write_done;
+ memcpy_fromfs(state->req.data, buf, count);
+ state->req.reply_expected = 1;
+ state->req.got_reply = 0;
+ adb_send_request(&state->req);
+
+ return count;
+}
+
+static struct file_operations adb_fops = {
+ adb_lseek,
+ adb_read,
+ adb_write,
+ NULL, /* no readdir */
+ NULL, /* no select */
+ NULL, /* no ioctl */
+ NULL, /* no mmap */
+ adb_open,
+ adb_release
+};
+
+static struct miscdevice adb_dev = {
+ ADB_MINOR,
+ "adb",
+ &adb_fops
+};
+
+void adbdev_init(void)
+{
+ misc_register(&adb_dev);
+}
+
+#endif /* old ADB device */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov