home *** CD-ROM | disk | FTP | other *** search
- /*
- * NES streams driver.
- *
- * Derived, through several generations, from code typed in from
- * the AT&T Streams Programmer's Guide.
- *
- * Supports Nintendeo Entertainment System joysticks via a parallel port.
- * No interrupts. No zap-gun support.
- *
- * Each minor number corresponds to one joystick plug. It should be possible
- * to hook up a few NES gizmos via parallel port.
- *
- * Copyright 1991, Lance C. Norskog
- *
- * version 0.1 - base, running with X
- * version 0.2 - switch to double-bucket input management
- */
-
- #include <sys/types.h>
- #include <sys/param.h>
- #include <sys/stream.h>
- #include <sys/stropts.h>
- #include <sys/sysmacros.h>
- #ifndef __GNUC__
- #include <sys/inline.h>
- #endif
- #include <sys/errno.h>
-
- static struct module_info rnes = {
- 3, "nes", 0, INFPSZ, 500, 100
- };
-
- static struct module_info wnes = {
- 3, "nes", 0, INFPSZ, 500, 100
- };
-
- static int nesopen(), nesclose(), neswput(), nesrsrv();
-
- static struct qinit urqinit = { /* Upper read */
- 0, nesrsrv, nesopen, nesclose, NULL, &rnes, 0
- };
-
- static struct qinit uwqinit = { /* Upper write */
- neswput, 0, nesopen, nesclose, NULL, &wnes, 0
- };
-
- struct streamtab nesinfo = {
- &urqinit, &uwqinit, NULL, NULL
- };
-
- int nes_debug = 0, nes_notimer = 0, nes_nostrobe = 0;
- #define debug(mask, x) if (nes_debug & mask) printf x;
- /* 1 for upper-level, 2 for lower-level, 4 for input data */
-
- int nes_in, /* number of input packets */
- nes_inb; /* number of input bytes */
- int nes_nomblk, /* number of allocb failures */
- nes_nodupb, /* number of dupb failures */
- nes_full, /* number of buffer-full failures */
- nes_cant, /* number of canput failures */
- nes_busy; /* number of bucket-busy */
-
- int nes_bsize = 1024; /* several seconds worth */
-
- #define NNES 1
-
- #define NES_right 0x01
- #define NES_left 0x02
- #define NES_down 0x04
- #define NES_up 0x08
- #define NES_start 0x10
- #define NES_select 0x20
- #define NES_B 0x40
- #define NES_A 0x80
-
- #define NES_SWITCHES (NES_start | NES_select | NES_A | NES_B)
- #define NES_MOVES (NES_right | NES_left | NES_up | NES_up)
-
- struct nes_nes {
- unsigned short ctrl_port, ctrl_clock, ctrl_reset,
- data_port, data_mask,
- ticks;
- unsigned char canopen;
- queue_t *rdq;
- int timer;
- int nes; /* last buttons */
- int switches;
- mblk_t *mp, *other; /* pointer to input blocks */
- } nes_nes[NNES] = {
- {
- 0x378,
- 1,
- 2,
- 0x379,
- 0x10,
- HZ,
- }
- };
-
- typedef struct nes_nes nes_t;
-
- int nnes = NNES;
-
- int nes_c1 = 20;
- int nes_c2 = 20;
- int nes_c3 = 20;
- int nes_c4 = 20;
- int nes_c5 = 20;
-
- /* move to space.c */
-
- nesinit() {
- int i, hz;
-
- for(i=0; i<nnes; i++) {
- nes_nes[i].rdq = (queue_t *) 0;
- nes_nes[i].canopen = 1;
- nes_strobe_init(&nes_nes[i]);
- hz = HZ / nes_nes[i].ticks;
- nes_nes[i].ticks = ((hz == 0) ? 1 : hz);
- nes_nes[i].switches = 0;
- }
- }
-
- nesopen(q, dev, flag, sflag)
- queue_t *q;
- {
- nes_t *nes;
- int nes_timer();
-
- if (sflag == CLONEOPEN) {
- return OPENFAIL;
- /* for (dev = 0; dev < nnes; dev++)
- if (nes_nes[dev].rdq == (queue_t *) 0)
- break; */
- } else dev = minor(dev);
- if (dev > nnes)
- return OPENFAIL;
- nes = &nes_nes[dev];
- if (nes->rdq)
- return dev;
-
- /* q_ptr is the q's repository for our per-chan structure */
- q->q_ptr = (caddr_t) nes;
- WR(q)->q_ptr = (caddr_t) nes;
- nes->rdq = q;
-
- debug(1, ("Line %d\n", __LINE__));
- /* Start timer */
- if (! nes_notimer)
- nes->timer = timeout(nes_timer, q, nes->ticks);
- nes->mp = allocb(nes_bsize, BPRI_LO);
- nes->other = allocb(nes_bsize, BPRI_LO);
- if (! nes->mp) nes_nomblk++;
-
- debug(1, ("Line %d\n", __LINE__));
- return dev;
- }
-
- static
- nesclose(q)
- queue_t *q;
- {
- nes_t *nes = (nes_t *) q->q_ptr;
- mblk_t *mp;
-
- debug(1, ("Line %d\n", __LINE__));
- untimeout(nes->timer);
- nes->rdq = (queue_t *) 0;
- if (nes->mp)
- freemsg(nes->mp);
- if (nes->other)
- freemsg(nes->other);
- nes->mp = (mblk_t *) 0;
- nes->other = (mblk_t *) 0;
- nes_strobe_init(nes);
- debug(1, ("Line %d\n", __LINE__));
- }
-
- /* This is called when, and only when, the above reader can read. */
- /*
- * Double-bucket method:
- * Only send up when other bucket is finished processing.
- * Always duplicate buffers before sending them up.
- */
- nesrsrv(q)
- queue_t *q;
- {
- nes_t *nes = (nes_t *) q->q_ptr;
- mblk_t *mp;
- int can, bytes = 0;
-
- debug(1, ("Line %d\n", __LINE__));
- if (!nes->mp)
- return;
- if (nes->other && (nes->other->b_datap->db_ref > 1)) {
- nes_busy++;
- return;
- }
- debug(1, ("Line %d\n", __LINE__));
- if (! (bytes = (nes->mp->b_wptr > nes->mp->b_rptr)))
- return;
- can = canput(q->q_next);
- debug(1, ("Line %d\n", __LINE__));
- if (! can) {
- nes_cant++;
- return;
- }
- if (!(mp = dupb(nes->mp))) {
- nes_nodupb++;
- return;
- }
-
- debug(1, ("Line %d\n", __LINE__));
- /* send up copy of current bucket */
- nes_in++;
- nes_inb += mp->b_wptr - mp->b_rptr;
- putnext(q, mp);
-
- /* Swap buckets */
- mp = nes->other;
- nes->other = nes->mp;
- nes->mp = mp;
-
- debug(1, ("Line %d\n", __LINE__));
- if (!nes->mp)
- nes->mp = allocb(nes_bsize, BPRI_LO);
- if (! nes->mp)
- nes_nomblk++;
- else /* reset read/write pointers of recycled bucket */
- nes->mp->b_rptr = nes->mp->b_wptr = nes->mp->b_datap->db_base;
- }
-
- /* This is called strioctl(), whether or not it's there! */
- neswput(q, mp)
- queue_t *q;
- mblk_t *mp;
- {
- nes_t *nes = (nes_t *) q->q_ptr;
-
- switch(mp->b_datap->db_type) {
- case M_IOCTL:
- /* process */
-
- mp->b_datap->db_type = M_IOCNAK;
- qreply(q, mp);
- break;
-
- /* flush handling must be here */
- case M_FLUSH:
- if (*mp->b_rptr & FLUSHW)
- flushq(q, FLUSHDATA);
- if (*mp->b_rptr & FLUSHR) {
- flushq(RD(q), FLUSHDATA);
- *mp->b_rptr &= ~FLUSHW;
- qreply(q, mp);
- } else
- freemsg(mp);
- break;
- default:
- freemsg(q, mp);
- }
- }
-
- nes_timer(q)
- queue_t *q;
- {
- nes_t *nes = (nes_t *) q->q_ptr;
- unsigned char bits, nes_strobe();
- mblk_t *tmp;
-
- if (nes_notimer)
- goto resched;
- debug(2, ("Line %d\n", __LINE__));
- if (!nes->mp && !(nes->mp = allocb(nes_bsize, BPRI_LO))) {
- nes_nomblk++;
- goto resched;
- }
-
- debug(2, ("Line %d\n", __LINE__));
- if (nes->mp->b_wptr == nes->mp->b_datap->db_lim) {
- nes_full++;
- goto resched;
- }
-
- debug(2, ("Line %d\n", __LINE__));
- bits = nes_strobe(nes);
- if (((bits & NES_right) && (bits & NES_left)) ||
- ((bits & NES_up) && (bits & NES_down))) {
- /* skip */
- } else {
- debug(2, ("Line %d\n", __LINE__));
- /* report on movement or change in switches */
- if (bits || ((bits & (NES_SWITCHES)) != nes->switches)) {
- *(nes->mp->b_wptr++) = bits;
- debug(2, ("Line %d\n", __LINE__));
- qenable(nes->rdq);
- nes->switches = (bits & NES_SWITCHES);
- }
- debug(2, ("Line %d\n", __LINE__));
- }
- resched:
- nes->timer = timeout(nes_timer, q, nes->ticks);
- }
-
- /*
- * Put port in quiescent state
- */
-
- nes_strobe_init(nes)
- nes_t *nes;
- {
- int i, j;
-
- if (nes_nostrobe)
- return;
- for(i = 0; i < 8; i++)
- if ((1<<i) & nes->data_mask)
- break;
- if (i == 8) {
- nes->canopen = 0;
- printf("NES%d: data_mask not set!\n", nes - nes_nes);
- }
- outb(nes->ctrl_port, 0);
- outb(nes->data_port, 0);
- }
-
- #define hold(clk) for(slow = clk; slow; slow--);
-
- /* Actually strobe the NES and add events to the record */
- unsigned char
- nes_strobe(nes)
- nes_t *nes;
- {
- int i, slow;
- unsigned char data;
- unsigned short bits = 0; /* not char! */
-
- debug(2, ("Line %d\n", __LINE__));
- if (nes_nostrobe)
- return 0;
- /*
- #ifndef __GNUC__
- intr_disable();
- #endif
- */
- debug(2, ("Line %d\n", __LINE__));
- outb(nes->ctrl_port, nes->ctrl_reset | nes->ctrl_clock);
- hold(nes_c1);
- outb(nes->ctrl_port, nes->ctrl_clock);
- hold(nes_c2);
- for(i = 0; i < 8; i++) {
- data = inb(nes->data_port);
- bits |= (data & nes->data_mask);
- hold(nes_c3);
- outb(nes->ctrl_port, 0);
- hold(nes_c4);
- outb(nes->ctrl_port, nes->ctrl_clock);
- hold(nes_c5);
- bits <<= 1;
- }
- debug(2, ("Line %d\n", __LINE__));
- /* bits now straddles word, is upwards by one */
- /* downshift into place */
- for(i = 0; i < 8; i++) {
- bits >>= 1;
- if (nes->data_mask & (1 << i))
- break;
- }
- debug(2, ("Line %d\n", __LINE__));
- /*
- #ifndef __GNUC__
- intr_restore();
- #endif
- */
- debug(2, ("Line %d\n", __LINE__));
- bits = ~bits;
- if (bits && (nes_debug & 4))
- printf("Input: 0x%x\n", bits & 0x1ff);
- return (unsigned char) bits;
- }
-
- static char *
- M_string(type)
- {
- switch(type) {
- case M_DATA: return "M_DATA";
- case M_PROTO: return "M_PROTO";
- case M_BREAK: return "M_BREAK";
- case M_PASSFP: return "M_PASSFP";
- case M_SIG: return "M_SIG";
- case M_DELAY: return "M_DELAY";
- case M_CTL: return "M_CTL";
- case M_IOCTL: return "M_IOCTL";
- case M_SETOPTS: return "M_SETOPTS";
- case M_IOCACK: return "M_IOCACK";
- case M_IOCNAK: return "M_IOCNAK";
- case M_PCPROTO: return "M_PCPROTO";
- case M_PCSIG: return "M_PCSIG";
- case M_FLUSH: return "M_FLUSH";
- case M_STOP: return "M_STOP";
- case M_START: return "M_START";
- case M_HANGUP: return "M_HANGUP";
- case M_ERROR: return "M_ERROR";
- default: return "M_UNKNOWN";
- }
- }
-
- nesintr() {;}
- /* Don't digest it here.
- if (bits & NES_right)
- nes->right++;
- if (bits & NES_left)
- nes->left++;
- if (bits & NES_up)
- nes->up++;
- if (bits & NES_down)
- nes->down++;
-
- if (bits & NES_A)
- nes->A = 1;
- if (bits & NES_B)
- nes->B = 1;
- */
-
-