static int cuda_write(struct adb_request *req) { unsigned long flags; if (req->nbytes < 2 || req->data[0] > CUDA_PACKET) { req->complete = 1; return -EINVAL; } req->next = NULL; req->sent = 0; req->complete = 0; req->reply_len = 0; spin_lock_irqsave(&cuda_lock, flags); if (current_req != 0) { last_req->next = req; last_req = req; } else { current_req = req; last_req = req; if (cuda_state == idle) cuda_start(); } spin_unlock_irqrestore(&cuda_lock, flags); return 0; }
static irqreturn_t cuda_interrupt(int irq, void *arg) { int status; struct adb_request *req = NULL; unsigned char ibuf[16]; int ibuf_len = 0; int complete = 0; spin_lock(&cuda_lock); /* On powermacs, this handler is registered for the VIA IRQ. But it uses * just the shift register IRQ -- other VIA interrupt sources are disabled. * On m68k macs, the VIA IRQ sources are dispatched individually. Unless * we are polling, the shift register IRQ flag has already been cleared. */ #ifdef CONFIG_MAC if (!arg) #endif { if ((in_8(&via[IFR]) & SR_INT) == 0) { spin_unlock(&cuda_lock); return IRQ_NONE; } else { out_8(&via[IFR], SR_INT); } } status = (~in_8(&via[B]) & (TIP|TREQ)) | (in_8(&via[ACR]) & SR_OUT); /* printk("cuda_interrupt: state=%d status=%x\n", cuda_state, status); */ switch (cuda_state) { case idle: /* CUDA has sent us the first byte of data - unsolicited */ if (status != TREQ) printk("cuda: state=idle, status=%x\n", status); (void)in_8(&via[SR]); out_8(&via[B], in_8(&via[B]) & ~TIP); cuda_state = reading; reply_ptr = cuda_rbuf; reading_reply = 0; break; case awaiting_reply: /* CUDA has sent us the first byte of data of a reply */ if (status != TREQ) printk("cuda: state=awaiting_reply, status=%x\n", status); (void)in_8(&via[SR]); out_8(&via[B], in_8(&via[B]) & ~TIP); cuda_state = reading; reply_ptr = current_req->reply; reading_reply = 1; break; case sent_first_byte: if (status == TREQ + TIP + SR_OUT) { /* collision */ out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT); (void)in_8(&via[SR]); out_8(&via[B], in_8(&via[B]) | TIP | TACK); cuda_state = idle; } else { /* assert status == TIP + SR_OUT */ if (status != TIP + SR_OUT) printk("cuda: state=sent_first_byte status=%x\n", status); out_8(&via[SR], current_req->data[1]); out_8(&via[B], in_8(&via[B]) ^ TACK); data_index = 2; cuda_state = sending; } break; case sending: req = current_req; if (data_index >= req->nbytes) { out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT); (void)in_8(&via[SR]); out_8(&via[B], in_8(&via[B]) | TACK | TIP); req->sent = 1; if (req->reply_expected) { cuda_state = awaiting_reply; } else { current_req = req->next; complete = 1; /* not sure about this */ cuda_state = idle; cuda_start(); } } else { out_8(&via[SR], req->data[data_index++]); out_8(&via[B], in_8(&via[B]) ^ TACK); } break; case reading: *reply_ptr++ = in_8(&via[SR]); if (status == TIP) { /* that's all folks */ out_8(&via[B], in_8(&via[B]) | TACK | TIP); cuda_state = read_done; } else { /* assert status == TIP | TREQ */ if (status != TIP + TREQ) printk("cuda: state=reading status=%x\n", status); out_8(&via[B], in_8(&via[B]) ^ TACK); } break; case read_done: (void)in_8(&via[SR]); if (reading_reply) { req = current_req; req->reply_len = reply_ptr - req->reply; if (req->data[0] == ADB_PACKET) { /* Have to adjust the reply from ADB commands */ if (req->reply_len <= 2 || (req->reply[1] & 2) != 0) { /* the 0x2 bit indicates no response */ req->reply_len = 0; } else { /* leave just the command and result bytes in the reply */ req->reply_len -= 2; memmove(req->reply, req->reply + 2, req->reply_len); } } current_req = req->next; complete = 1; } else { /* This is tricky. We must break the spinlock to call * cuda_input. However, doing so means we might get * re-entered from another CPU getting an interrupt * or calling cuda_poll(). I ended up using the stack * (it's only for 16 bytes) and moving the actual * call to cuda_input to outside of the lock. */ ibuf_len = reply_ptr - cuda_rbuf; memcpy(ibuf, cuda_rbuf, ibuf_len); } if (status == TREQ) { out_8(&via[B], in_8(&via[B]) & ~TIP); cuda_state = reading; reply_ptr = cuda_rbuf; reading_reply = 0; } else { cuda_state = idle; cuda_start(); } break; default: printk("cuda_interrupt: unknown cuda_state %d?\n", cuda_state); } spin_unlock(&cuda_lock); if (complete && req) { void (*done)(struct adb_request *) = req->done; mb(); req->complete = 1; /* Here, we assume that if the request has a done member, the * struct request will survive to setting req->complete to 1 */ if (done) (*done)(req); } if (ibuf_len) cuda_input(ibuf, ibuf_len); return IRQ_HANDLED; }
static irqreturn_t cuda_interrupt(int irq, void *arg) { int status; struct adb_request *req = NULL; unsigned char ibuf[16]; int ibuf_len = 0; int complete = 0; spin_lock(&cuda_lock); #ifdef CONFIG_MAC if (!arg) #endif { if ((in_8(&via[IFR]) & SR_INT) == 0) { spin_unlock(&cuda_lock); return IRQ_NONE; } else { out_8(&via[IFR], SR_INT); } } status = (~in_8(&via[B]) & (TIP|TREQ)) | (in_8(&via[ACR]) & SR_OUT); switch (cuda_state) { case idle: if (status != TREQ) printk("cuda: state=idle, status=%x\n", status); (void)in_8(&via[SR]); out_8(&via[B], in_8(&via[B]) & ~TIP); cuda_state = reading; reply_ptr = cuda_rbuf; reading_reply = 0; break; case awaiting_reply: if (status != TREQ) printk("cuda: state=awaiting_reply, status=%x\n", status); (void)in_8(&via[SR]); out_8(&via[B], in_8(&via[B]) & ~TIP); cuda_state = reading; reply_ptr = current_req->reply; reading_reply = 1; break; case sent_first_byte: if (status == TREQ + TIP + SR_OUT) { out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT); (void)in_8(&via[SR]); out_8(&via[B], in_8(&via[B]) | TIP | TACK); cuda_state = idle; } else { if (status != TIP + SR_OUT) printk("cuda: state=sent_first_byte status=%x\n", status); out_8(&via[SR], current_req->data[1]); out_8(&via[B], in_8(&via[B]) ^ TACK); data_index = 2; cuda_state = sending; } break; case sending: req = current_req; if (data_index >= req->nbytes) { out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT); (void)in_8(&via[SR]); out_8(&via[B], in_8(&via[B]) | TACK | TIP); req->sent = 1; if (req->reply_expected) { cuda_state = awaiting_reply; } else { current_req = req->next; complete = 1; cuda_state = idle; cuda_start(); } } else { out_8(&via[SR], req->data[data_index++]); out_8(&via[B], in_8(&via[B]) ^ TACK); } break; case reading: *reply_ptr++ = in_8(&via[SR]); if (status == TIP) { out_8(&via[B], in_8(&via[B]) | TACK | TIP); cuda_state = read_done; } else { if (status != TIP + TREQ) printk("cuda: state=reading status=%x\n", status); out_8(&via[B], in_8(&via[B]) ^ TACK); } break; case read_done: (void)in_8(&via[SR]); if (reading_reply) { req = current_req; req->reply_len = reply_ptr - req->reply; if (req->data[0] == ADB_PACKET) { if (req->reply_len <= 2 || (req->reply[1] & 2) != 0) { req->reply_len = 0; } else { req->reply_len -= 2; memmove(req->reply, req->reply + 2, req->reply_len); } } current_req = req->next; complete = 1; } else { ibuf_len = reply_ptr - cuda_rbuf; memcpy(ibuf, cuda_rbuf, ibuf_len); } if (status == TREQ) { out_8(&via[B], in_8(&via[B]) & ~TIP); cuda_state = reading; reply_ptr = cuda_rbuf; reading_reply = 0; } else { cuda_state = idle; cuda_start(); } break; default: printk("cuda_interrupt: unknown cuda_state %d?\n", cuda_state); } spin_unlock(&cuda_lock); if (complete && req) { void (*done)(struct adb_request *) = req->done; mb(); req->complete = 1; if (done) (*done)(req); } if (ibuf_len) cuda_input(ibuf, ibuf_len); return IRQ_HANDLED; }
static void cuda_interrupt(int irq, void *arg, struct pt_regs *regs) { int x, status; struct adb_request *req; if ((via[IFR] & SR_INT) == 0) return; status = (~via[B] & (TIP|TREQ)) | (via[ACR] & SR_OUT); eieio(); /* printk("cuda_interrupt: state=%d status=%x\n", cuda_state, status); */ switch (cuda_state) { case idle: /* CUDA has sent us the first byte of data - unsolicited */ if (status != TREQ) printk("cuda: state=idle, status=%x\n", status); x = via[SR]; eieio(); via[B] &= ~TIP; eieio(); cuda_state = reading; reply_ptr = cuda_rbuf; reading_reply = 0; break; case awaiting_reply: /* 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[SR]; eieio(); via[B] &= ~TIP; eieio(); cuda_state = reading; reply_ptr = current_req->reply; reading_reply = 1; break; case sent_first_byte: if (status == TREQ + TIP + SR_OUT) { /* collision */ via[ACR] &= ~SR_OUT; eieio(); x = via[SR]; eieio(); via[B] |= TIP | TACK; eieio(); cuda_state = idle; } else { /* assert status == TIP + SR_OUT */ if (status != TIP + SR_OUT) printk("cuda: state=sent_first_byte status=%x\n", status); via[SR] = current_req->data[1]; eieio(); via[B] ^= TACK; eieio(); data_index = 2; cuda_state = sending; } break; case sending: req = current_req; if (data_index >= req->nbytes) { via[ACR] &= ~SR_OUT; eieio(); x = via[SR]; eieio(); via[B] |= TACK | TIP; eieio(); req->sent = 1; if (req->reply_expected) { cuda_state = awaiting_reply; } else { current_req = req->next; if (req->done) (*req->done)(req); /* not sure about this */ cuda_state = idle; cuda_start(); } } else { via[SR] = req->data[data_index++]; eieio(); via[B] ^= TACK; eieio(); } break; case reading: *reply_ptr++ = via[SR]; eieio(); if (status == TIP) { /* that's all folks */ via[B] |= TACK | TIP; eieio(); cuda_state = read_done; } else { /* assert status == TIP | TREQ */ if (status != TIP + TREQ) printk("cuda: state=reading status=%x\n", status); via[B] ^= TACK; eieio(); } break; case read_done: x = via[SR]; eieio(); if (reading_reply) { req = current_req; req->reply_len = reply_ptr - req->reply; if (req->data[0] == ADB_PACKET) { /* Have to adjust the reply from ADB commands */ if (req->reply_len <= 2 || (req->reply[1] & 2) != 0) { /* the 0x2 bit indicates no response */ req->reply_len = 0; } else { /* leave just the command and result bytes in the reply */ req->reply_len -= 2; memmove(req->reply, req->reply + 2, req->reply_len); } } req->complete = 1; current_req = req->next; if (req->done) (*req->done)(req); } else { cuda_input(cuda_rbuf, reply_ptr - cuda_rbuf, regs); } if (status == TREQ) { via[B] &= ~TIP; eieio(); cuda_state = reading; reply_ptr = cuda_rbuf; reading_reply = 0; } else { cuda_state = idle; cuda_start(); } break; default: printk("cuda_interrupt: unknown cuda_state %d?\n", cuda_state); } }