Files
Arquivotheca.SunOS-4.1.4/sys/sundev/db.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

616 lines
13 KiB
C

#ifndef lint
static char sccsid[] = "@(#)db.c 1.1 94/10/31 Copyr 1988 Sun Micro";
#endif
/*
* Copyright (c) 1988 by Sun Microsystems, Inc.
*/
/*
* dialbox stream module: Converts raw dialbox stream into vuid events.
* buttonbox stream module: Converts raw buttonbox stream into vuid events.
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/termio.h>
#include <sys/termios.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/time.h>
#include <sundev/vuid_event.h>
#include <sundev/dbio.h>
/* This is so we can build on machines without the new vuid_event.h file */
#ifndef DIAL_DEVID
#define DIAL_DEVID 0x7B
#endif
#ifndef BUTTON_DEVID
#define BUTTON_DEVID 0x7A
#endif
#ifndef DIAL_DEBUG
#define db_debug(x, y, z)
#endif DIAL_DEBUG
/* Local Defines */
/* States of the input routine */
#ifndef GTCO
#define SYNC 0
#define MSB 6
#define LSB 7
#define NDIALS 8
#define DIAL_SCALE(n) ((n) * 90)
#else
#define SYNC 0
#define DIAL 1
#define SIGN 2
#define HUNDREDS 3
#define TENS 4
#define ONES 5
#define NDIALS 9
#define DIAL_SCALE(n) ((n) * 192)
/*
* convert device units to 1/64th degrees
*/
#endif
static struct module_info rminfo = {
0, "db", 0, INFPSZ, 0, 0};
static struct module_info wminfo = {
0, "db", 0, INFPSZ, 0, 0};
static int
dbopen(), dbrput(), dbwput(), dbclose(), send_event(),
button_init();
static struct qinit rinit = {
dbrput, NULL, dbopen, dbclose, NULL, &rminfo, NULL
};
static struct qinit winit = {
dbwput, NULL, NULL, NULL, NULL, &wminfo, NULL
};
struct streamtab dbinfo = {
&rinit, &winit, NULL, NULL};
/* Local state information of devices */
static struct db_state {
int read_mode; /* read mode */
int delta[NDIALS]; /* Accumulated deltas since last read */
/* The following variable are used for raw input processing */
/* together they determine the state of the input stream */
int in_state; /* Current state of input processing */
int in_dial; /* current dial being read */
int in_sign; /* direction of delta */
int in_delta; /* and its delta so far */
} db_table[1];
/*
* shound be NDB to allow for multiple instances
*/
static struct db_state *dbp = db_table;
/* ARGSUSED q */
static int
dbopen(q, dev, flag, sflag)
queue_t *q; /* pointer to read queue */
dev_t dev; /* Major/minor device number (0 for module) */
int flag; /* file open flag (0 for module) */
int sflag; /* stream open flag */
{
db_debug(1, "dbo\n", 1);
dbp->read_mode = VUID_FIRM_EVENT;
dbp->in_state = SYNC;
dbinit(WR(q));
button_init(WR(q));
return (0); /* OK */
}
static int
dbwput(q, mp) /* Write put procedure */
queue_t *q; /* pointer to write queue */
mblk_t *mp; /* message pointer */
{
switch (mp->b_datap->db_type) {
case M_IOCTL:
db_debug(1, "dbio %X\n", *(mp->b_wptr));
dbioctl(q, mp);
break;
default:
db_debug(1, "dbw %X\n", *(mp->b_wptr));
putnext(q, mp); /* Just pass the message through */
break;
}
}
static int
dbrput(q, mp) /* read put procedure */
queue_t *q; /* pointer to read queue */
mblk_t *mp; /* message pointer */
{
switch (mp->b_datap->db_type) {
case M_DATA:
switch (dbp->read_mode) {
case VUID_NATIVE: /* unfiltered */
db_debug(1, "dbn %X\n", *(mp->b_rptr));
putnext(q, mp); /* Just pass the message through */
break;
case VUID_FIRM_EVENT:
{ /* read everything and output events */
register mblk_t *bp;
mblk_t * nmp = NULL,
*nbp = NULL;
register int i,
n_events;
static Firm_event db_event;
/*
* Process all of our messages and accumulate
* deltas
*/
for (bp = mp; bp != NULL; bp = bp->b_cont) {
while (bp->b_rptr < bp->b_wptr) {
db_debug(2,
"dbf %X\n",
*(bp->b_rptr));
dbinput(*bp->b_rptr, q);
/*
* 'process' this character
*/
bp->b_rptr++;
}
}
/*
* free up old message, we are done with it
*/
freemsg(mp);
/*
* now we have read all of our data.
* Count events
*/
n_events = 0;
for (i = 0; i < NDIALS; i++) {
/* count how many dials have changed */
if (dbp->delta[i]) {
n_events++;
}
}
/*
* if we have any events, get a message
* block to put them in. if allocation
* fails, it doesn't matter since we
* still have the deltas (however, time
* info may be lost
*/
if ((n_events != 0) &&
((nmp = (mblk_t *) allocb(n_events
* sizeof (Firm_event), BPRI_MED))
!= NULL)) {
nbp = nmp;
for (i = 0; i < NDIALS; i++) {
/*
* put the events into output
* buffer
*/
if (dbp->delta[i]) {
db_event.id =
vuid_id_addr(vuid_first(DIAL_DEVID)) |
vuid_id_offset(i);
db_event.pair_type = FE_PAIR_NONE;
db_event.pair = 0;
db_event.value = DIAL_SCALE(dbp->delta[i]);
dbp->delta[i] = 0; /* clear out delta */
uniqtime(&db_event.time); /* get time */
/* Copy Event struct to new data block */
bcopy((char *) & db_event, (char *) nbp->b_wptr,
sizeof (Firm_event));
nbp->b_wptr += sizeof (Firm_event);
}
}
/* Now we have a single message block */
putnext(q, nmp); /* pass it on */
}
}
default:
db_debug(2, "default!! %X\n", *(mp->b_rptr));
break;
}
break;
default:
db_debug(2, "pass thru %X\n", *(mp->b_rptr));
putnext(q, mp); /* pass them through */
break;
}
}
/* ARGSUSED */
static int
dbclose(q, flag)
queue_t *q; /* pointer to read queue */
int flag; /* file open flags, 0 for read queue */
{
db_debug(1, "dbc\n", 1);
}
/*
* dbinput() processes a single character from the dialbox. It accumulates
* deltas in the local structure referneced by the dbp pointer. Instead of
* putting the events into an intermediate queue, it simply keeps track of
* the deltas since the last read. This may need to change if timing
* information becomes important. It does keep the buffer requirements small
* though.
*/
#ifdef DIAL_DEBUG
int db_err = 0; /* for debugging */
#endif DIAL_DEBUG
#ifndef GTCO
dbinput(c, q)
register unsigned char c; /* The input character */
queue_t *q;
/*
* and the pointer to read queue from which it came
*/
{
Firm_event db_e;
static int map[] = {
25, 26, 27, 28, 29, 30, 31, 32,
17, 18, 19, 20, 21, 22, 23, 24,
9, 10, 11, 12, 13, 14, 15, 16,
1, 2, 3, 4, 5, 6, 7, 8 };
switch (dbp->in_state) {
case SYNC: /* awaiting lead byte */
/* Input is from a button box */
if ((c >= 0x80) && (c <= 0x87)) {
dbp->in_state = MSB;
dbp->in_dial = c & 7;
} /* Input is from a button box */
else if ((c >= 0xc0) && (c <= 0xdf)) {
db_e.id = vuid_id_addr(vuid_first(BUTTON_DEVID)) |
map[c - 0xc0];
db_e.pair_type = FE_PAIR_NONE;
db_e.pair = 0;
db_e.value = 1; /* Down */
uniqtime(&db_e.time); /* get time */
/* send this event off to the queue */
send_event(&db_e, q);
} else if ((c >= 0xe0) && (c <= 0xff)) {
db_e.id = vuid_id_addr(vuid_first(BUTTON_DEVID)) |
map[c - 0xe0];
db_e.pair_type = FE_PAIR_NONE;
db_e.pair = 0;
db_e.value = 0; /* Up */
uniqtime(&db_e.time); /* get time */
/* send this event off to the queue */
send_event(&db_e, q);
} else {
/* Re-initialize, maybe box has been plugged back in */
if (c != 0x20) { /* Response from last init ? */
dbinit(WR(q));
}
}
break;
case MSB: /* Most significant, includes sign */
/*
* There is a possibility that when twisting the dials really really
* fast that data bytes will be dropped. The data very seldom falls in
* the range 0x80 through 0x87 so we ignore the chance that we are
* throwing away valid data.
*
* The consequences of not doing this is that button events are
* generated, if there is a drop out that is not taken care of this
* way...
*
*/
if ((c >= 0x80) && (c <= 0x87)) {
dbp->in_state = MSB;
dbp->in_dial = c & 7;
} else {
dbp->in_delta = (unsigned int) c;
dbp->in_state = LSB;
}
break;
case LSB:
if ((c >= 0x80) && (c <= 0x87)) {
dbp->in_state = MSB;
dbp->in_dial = c & 7;
} else {
dbp->delta[dbp->in_dial] +=
(short) ((dbp->in_delta << 8) | (c));
dbp->in_state = SYNC;
}
break;
default:
dbp->in_state = SYNC;
break;
}
}
dbinit(q)
queue_t *q; /* Write queue */
{
mblk_t * mb; /* Message Block */
static char init_cmd[] = {
0x90, 0, 0x0A
};
/*
* 0x20 is reset command for button box. Limits transmission to 30
* per second for dials
*/
/* Initialise the dialbox and button box */
if ((mb = (mblk_t *) allocb(sizeof (init_cmd), BPRI_MED)) != NULL) {
bcopy(init_cmd, (char *) mb->b_wptr, sizeof (init_cmd));
mb->b_wptr += sizeof (init_cmd);
putnext(q, mb); /* put it on the write queue */
}
}
static int
button_init(q)
queue_t *q; /* Write queue */
{
mblk_t * mb; /* Message Block */
static char init_cmd[] = {
0x20
};
/*
* 0x20 is reset command for button box. Initialise the button box
*/
if ((mb = (mblk_t *) allocb(sizeof (init_cmd), BPRI_MED)) != NULL) {
bcopy(init_cmd, (char *) mb->b_wptr, sizeof (init_cmd));
mb->b_wptr += sizeof (init_cmd);
putnext(q, mb); /* put it on the write queue */
}
}
#else GTCO
dbinput(c)
register char c;
{
if (c == 0x1B) {
dbp->in_state = DIAL; /* re sync */
} else {
switch (dbp->in_state) {
case SYNC: /* awaiting an escape */
if (c == 0x1B) {
dbp->in_state = DIAL;
dbp->in_delta = 0;
}
break;
case DIAL: /* dial number */
if (c >= '0' && c <= '8') {
dbp->in_dial = c - '0';
/*
* atoi for non strings
*/
dbp->in_state = SIGN;
} else {
dbp->in_state = SYNC;
}
break;
case SIGN: /* sign byte */
dbp->in_state = HUNDREDS;
/*
* next state, unless error
*/
switch (c) {
case '+':
dbp->in_sign = 1;
break;
case '-':
dbp->in_sign = -1;
break;
default:
dbp->in_state = SYNC;
break;
}
break;
case HUNDREDS: /* 100's */
if ((c >= '0') && (c <= '9')) {
dbp->in_delta = 100 * (c - '0');
dbp->in_state = TENS;
} else {
dbp->in_state = SYNC;
}
break;
case TENS: /* 10's */
if ((c >= '0') && (c <= '9')) {
dbp->in_delta += 10 * (c - '0');
dbp->in_state = ONES;
} else {
dbp->in_state = SYNC;
}
break;
case ONES: /* 1's */
if ((c >= '0') && (c <= '9')) {
dbp->in_delta += (c - '0');
dbp->delta[dbp->in_dial] +=
dbp->in_sign *
dbp->in_delta;
}
dbp->in_state = SYNC;
break;
default:
dbp->in_state = SYNC;
break;
}
}
return;
}
dbinit(q)
queue_t *q; /* Write queue */
{
return (0);
}
#endif
dbioctl(q, mp)
/*
* ioctl routine, called from write put routine
*/
queue_t *q; /* pointer to write queue */
mblk_t *mp; /* message pointer */
{
struct iocblk *iocp;
static unsigned char led_data[5];
mblk_t * mb; /* Message Block */
static int in_data;
int err = 0;
iocp = (struct iocblk *) mp->b_rptr; /* Ioctl is in first block */
switch (iocp->ioc_cmd) {
case VUIDGFORMAT:
{ /* Get Read mode */
register mblk_t *datap;
if ((datap = (mblk_t *) allocb(sizeof (int), BPRI_HI))
== NULL) {
/* We could do something smarter here */
err = -1;
} else {
*(int *) datap->b_wptr = dbp->read_mode;
datap->b_wptr += sizeof (int);
mp->b_cont = datap;
iocp->ioc_count = sizeof (int);
db_debug(2, "dbg %X\n", *(datap->b_wptr));
}
}
break;
case VUIDSFORMAT:
{ /* Set Read Mode */
if ((iocp->ioc_count != sizeof (int)) ||
(mp->b_cont == NULL) ||
(mp->b_cont->b_rptr == NULL)) {
err = -2;
} else {
dbp->read_mode = *(int *) mp->b_cont->b_rptr;
db_debug(2,
"dbs %X\n", *(int *) mp->b_cont->b_rptr);
}
}
break;
case DBIOBUTLITE:
{ /* Turn the specified leds of/off */
if (iocp->ioc_count != sizeof (int)) {
printf("count wrong!\n");
err = -3;
} else {
in_data = (*(int *) mp->b_cont->b_rptr);
/* Create the buffer */
led_data[0] = 0x75;
led_data[4] = ((in_data >> 24) & 0xff);
led_data[3] = ((in_data >> 16) & 0xff);
led_data[2] = ((in_data >> 8) & 0xff);
led_data[1] = ((in_data) & 0xff);
iocp->ioc_count = 0;
/* Copy this to a message block */
if ((mb = (mblk_t *)
allocb(sizeof (led_data), BPRI_MED))
!= NULL) {
bcopy((char *)led_data,
(char *) mb->b_wptr,
sizeof (led_data));
mb->b_wptr += sizeof (led_data);
putnext(q, mb);
/*
* put it on the write queue
*/
}
}
break;
}
}
if (err) {
mp->b_datap->db_type = M_IOCNAK;
} else {
mp->b_datap->db_type = M_IOCACK;
}
qreply(q, mp);
}
static int
send_event(db_ev, q)
struct firm_event *db_ev;
queue_t *q; /* and the pointer to destination queue */
{
mblk_t * nmp = NULL;
if ((nmp = (mblk_t *) allocb(sizeof (Firm_event), BPRI_MED)) != NULL) {
/* Copy Event struct to new data block */
bcopy((char *) db_ev, (char *) nmp->b_wptr,
sizeof (struct firm_event));
nmp->b_wptr += sizeof (struct firm_event);
/* Now we have a single message block */
putnext(q, nmp); /* pass it on */
} else {
printf("db: allocation failed.\n");
}
}
#ifdef DIAL_DEBUG
db_debug(level, fmt, what)
int level;
char *fmt;
int what;
{
if (db_err >= level)
printf(fmt, what);
}
#endif DIAL_DEBUG