From: gram@aim1.aztec.co.za (Graham Wheeler)
Newsgroups: comp.unix.internals,comp.unix.programmer
Subject: STREAMS debug module
Date: 30 Jul 92 08:47:33 GMT
Organization: Aztec Information Management

For those who are developing STREAMS applications and want to use the dump
module for debugging, here it is. My STREAMS manual is riddled with typos,
so I have fixed it up to the best of my ability. Also, I have the 3B2 manual
but I'm using a 386, so I've changed a couple of things (added dumpdevflag,
and made the extern declarations explicit; there is no /etc/master.d/dump
file).

Unfortunately, having created this module, I can't get autopush to accept
it, so I don't know how much use it is going to be to me or anyone. But
at least I may save some other people some time.

autpush comes up with the message: `Invalid major device number or invalid
module name or too many modules'. I have only the one module, my device
number is valid (I am just replacing ldterm with dump over the same driver,
and I never had problems with autopushing ldterm). If I do an ls -l on /dev,
there I see:

crw-rw-rw- 1 root root 77, 55 Jul 29 18:48 /dev/dump

BTW, does anyone know why I am getting a minor dev number of 55, when my
Node file is:

dump dump c 0

I have found this with other modules as well - the last entry always seems
to bomb out. If, for example, I had:

dump dump c 0
dump dump c 1
dump dump c 2

I would get minor devs 0, 1 and 55.

Why isn't anything in life simple? I've found it easier to implement a couple
of OSI protocols in STREAMS than to get the configuration working properly, the
dump module working, or a TCGETA ioctl to work (believe it or not).

Here is the code. Sorry, I don't have shar, you'll have to cut it up yourselves.

=== dump.h ==================================================================

/*
 * dump.h - header for DUMP module
 */

#define DUMPIOC		('Q'<<8)

#define DUMP_VERB	(DUMPIOC | 1)
#define DUMP_TERSE	(DUMPIOC | 2)

#define D_FREE		0
#define D_USED		1

#define D_OUT		1
#define D_IN		2

#define D_VERB		0x01

struct dm_str {
	char dm_use;
	char dm_state;
	char dm_flags;
};

=== dump.c ==========================================================

#include "sys/types.h"
#include "sys/stream.h"
#include "sys/param.h"
#include "sys/fcntl.h"
#include "sys/cmn_err.h"
#include "dump.h"
#include "sys/termio.h"

static struct module_info dumprinfo = { 0x6475, "dump", 0, INFPSZ, 0, 0 };
static struct module_info dumpwinfo = { 0x6475, "dump", 0, INFPSZ, 0, 0 };

static int dumpopen(), dumprput(), dumpwput(), dumpclose();

static struct qinit rinit = {
	dumprput, NULL, dumpopen, dumpclose, NULL, &dumprinfo, NULL };

static struct qinit winit = {
	dumpwput, NULL, NULL, NULL, NULL, &dumpwinfo, NULL };

struct streamtab dumpinfo = { &rinit, &winit, NULL, NULL };

int dm_ucnt=NUM_DUMP;

struct dm_str dm_users[NUM_DUMP];

int dumpdevflag = 0;

/*
 * dumpopen
 */

static int dumpopen(queue_t *q, dev_t *dev, int flag, int sflag) {
	register int i;

	if (q->q_ptr != NULL) {
		cmn_err(CE_CONT, "^DUMP: re-open slot %d\n",
			((struct dm_str *)q->q_ptr - dm_users));
		if (flag & O_NDELAY)
			cmn_err(CE_CONT, "^DUMP: re-open: O_NDELAY set\n");
		return 0;
	}
	for (i=0; i<dm_ucnt; i++) {
		if (dm_users[i].dm_use == D_FREE) {
			dm_users[i].dm_use = D_USED;
			dm_users[i].dm_state = 0;
			dm_users[i].dm_flags = D_VERB;
			q->q_ptr = (caddr_t)&dm_users[i];
			if (flag & O_NDELAY)
				cmn_err(CE_CONT,"^DUMP: open: O_NDELAY set\n");
			return 0;
		}
	}
	return OPENFAIL;
}

/*
 * dumpclose
 */

static int dumpclose(queue_t *q, int flag) {
	struct dm_str *d;
	d = (struct dm_str *)q->q_ptr;
	d->dm_use = D_FREE;
	d->dm_flags = 0;
	q->q_ptr = 0;
	WR(q)->q_ptr = 0;
	return 0;
}

static int dumpwput(queue_t *q, mblk_t *mp) {
	struct iocblk *iocp;
	struct dm_str *d;

	d = (struct dm_str *)q->q_ptr;
	if (mp->b_datap->db_type == M_IOCTL) {
		iocp = (struct iocblk *)mp->b_rptr;
		if ((iocp->ioc_cmd & DUMPIOC)==DUMPIOC) {
			dumpioc(q,mp);
			return 0;
		} else {
			cmn_err(CE_CONT,"^o: M_IOCTL %x, cnt %d ",
				iocp->ioc_cmd, iocp->ioc_count);
			if ((d->dm_flags & D_VERB) && mp->b_cont)
				dumpshow(mp->b_cont, iocp->ioc_cmd);
			d->dm_state = 0;
			putnext(q,mp);
		}
	} else if (mp->b_datap->db_type == M_DATA) {
		dumpgather(q,mp,D_OUT);
		putnext(q,mp);
	} else	{
		dumpctl(q,mp,d,D_OUT);
		d->dm_state = 0;
		putnext(q,mp);
	}
}

/*
 * dumprput
 */

static int dumprput(queue_t *q, mblk_t *mp) {
	struct dm_str *d;

	d = (struct dm_str *)q->q_ptr;
	if (mp->b_datap->db_type == M_DATA) {
		dumpgather(q,mp,D_IN);
	} else	{
		dumpctl(q,mp,d,D_IN);
		d->dm_state = 0;
	}
	putnext(q,mp);
}

/*
 * dumpgather
 */

dumpgather(queue_t *q, mblk_t *mp, int dir) {
	register struct dm_str *d;
	register int sx;
	register unsigned char *readp;
	register mblk_t *tmp;
	int counter;
	char junk[2];

	d = (struct dm_str *)q->q_ptr;
	if (d->dm_state != dir) {
		d->dm_state = dir;
		cmn_err(CE_CONT,"^%s",((dir==D_IN) ? "I" : "O" ));
	}
	if ((!mp->b_datap) || 
		((mp->b_rptr == mp->b_wptr) && (mp->b_cont == NULL))) {
			cmn_err(CE_CONT,"^DUMP: 0 len data msg %s\n",
				(dir==D_OUT) ? "OUT":"IN");
			return;
	}
	cmn_err(CE_CONT,"^ {{");
	tmp = mp;
	counter = 0;
	sx = splstr();
	junk[1] = ' ';
more:
	readp = tmp->b_rptr;
	while (readp < tmp->b_wptr) {
		if (d->dm_flags & D_VERB) {
			if ((*readp >= ' ') && (*readp <= '~') && !(*readp & 0x80)) {
				junk[0] = *readp;
				cmn_err(CE_CONT, "^ %s",junk);
			} else cmn_err(CE_CONT,"^ 0x%x", *readp);
		} else {
			++counter;
		}
		++readp;
	}
	if ((tmp->b_cont) && (tmp->b_datap->db_type == M_DATA)) {
		tmp = tmp->b_cont;
		goto more;
	}
	if (!(d->dm_flags & D_VERB))
		cmn_err(CE_CONT,"^%d ",counter);
	cmn_err(CE_CONT,"^}} ");
	if (tmp->b_cont && (tmp->b_datap->db_type != M_DATA))
		cmn_err(CE_CONT,"^DUMP: non-data b_cont\n");
	splx(sx);
}

/*
 * dumpioc
 */

dumpioc(queue_t *q, mblk_t *mp) {
	register struct iocblk *iocp;
	register struct dm_str *d, *rst;
	d = (struct dm_str *)q->q_ptr;
	iocp = (struct iocblk *)mp->b_datap->db_base;
	cmn_err(CE_CONT,"^DUMP: own ioctl is ");
	switch (iocp->ioc_cmd) {
	case DUMP_VERB:
		d->dm_flags |= D_VERB;
		cmn_err(CE_CONT, "^DUMP_VERB\n");
		break;
	case DUMP_TERSE:
		d->dm_flags &= ~D_VERB;
		cmn_err(CE_CONT, "^DUMP_TERSE\n");
		break;
	default:
		mp->b_datap->db_type = M_IOCNAK;
		cmn_err(CE_CONT,"^UNKNOWN DUMP IOCTL x%x\n",iocp->ioc_cmd);
		qreply(q,mp);
		return;
	}
	mp->b_datap->db_type = M_IOCACK;
	iocp->ioc_count = 0;
	qreply(q,mp);
}

/*
 * dumpctl
 */

dumpctl(queue_t *q, mblk_t *mp, struct dm_str *d, int dir) {
	cmn_err(CE_CONT,"^%s: M_",((dir==D_IN) ? "i":"o"));
	switch(mp->b_datap->db_type) {
	case M_DATA:
		cmn_err(CE_CONT,"^DATA");
		break;
	case M_READ:
		cmn_err(CE_CONT,"^READ");
		break;
	case M_IOCTL:
		cmn_err(CE_CONT,"^IOCTL");
		break;
	case M_IOCACK:
		cmn_err(CE_CONT,"^IOCACK");
		break;
	case M_IOCNAK:
		cmn_err(CE_CONT,"^IOCNAK");
		break;
	case M_IOCDATA:
		cmn_err(CE_CONT,"^IOCDATA");
		break;
	case M_CTL:
		cmn_err(CE_CONT,"^CTL");
		break;
	case M_PROTO:
		cmn_err(CE_CONT,"^PROTO");
		break;
	case M_PCPROTO:
		cmn_err(CE_CONT,"^PCPROTO");
		break;
	case M_BREAK:
		cmn_err(CE_CONT,"^BREAK");
		break;
	case M_DELAY:
		cmn_err(CE_CONT,"^DELAY");
		break;
	case M_PASSFP:
		cmn_err(CE_CONT,"^PASSFP");
		break;
	case M_SETOPTS:
		cmn_err(CE_CONT,"^SETOPTS");
		break;
	case M_SIG:
		cmn_err(CE_CONT,"^SIG (%d) ",(int)*mp->b_rptr);
		break;
	case M_ERROR:
		cmn_err(CE_CONT,"^ERROR");
		break;
	case M_HANGUP:
		cmn_err(CE_CONT,"^HANGUP");
		break;
	case M_FLUSH:
		cmn_err(CE_CONT,"^FLUSH");
		break;
	case M_PCSIG:
		cmn_err(CE_CONT,"^PCSIG (%d) ",(int)*mp->b_rptr);
		break;
	case M_COPYOUT:
		cmn_err(CE_CONT,"^COPYOUT");
		break;
	case M_COPYIN:
		cmn_err(CE_CONT,"^COPYIN");
		break;
	case M_START:
		cmn_err(CE_CONT,"^START");
		break;
	case M_STOP:
		cmn_err(CE_CONT,"^STOP");
		break;
	case M_STARTI:
		cmn_err(CE_CONT,"^STARTI");
		break;
	case M_STOPI:
		cmn_err(CE_CONT,"^STOPI");
		break;
	default:
		cmn_err(CE_CONT,"^Unknown!");
	}
}

/*
 * dumpshow
 */

dumpshow(mblk_t *mp, int cmd) {
	int i;
	struct termio *t;

	switch (cmd) {
	case TCSETAF:
		cmn_err(CE_CONT,"^TCSETAF "); goto prtall;
	case TCSETA:
		cmn_err(CE_CONT,"^TCSETA "); goto prtall;
	case TCSETAW:
		cmn_err(CE_CONT,"^TCSETAW "); goto prtall;
	case TCGETA:
		cmn_err(CE_CONT,"^TCGETA "); goto prtall;
prtall:
		t = (struct termio *) mp->b_rptr;
		cmn_err(CE_CONT,"^if = %x; of = %x; cf=%x; lf=%x\ncc=",
			t->c_iflag, t->c_oflag, t->c_cflag, t->c_lflag);
		for (i=0;i<NCC;i++) 
			cmn_err(CE_CONT,"^0x%x ",(int)t->c_cc[i]);
		cmn_err(CE_CONT,"^\n");
		break;
	default:
		return;
	}
}

===  Node  ==================================================================

dump dump c 0

=== sdevice =================================================================

dump Y 1 0 0 0 0 0 0 0 

=== Master ==================================================================

dump - ifSoc dump 0 0 1 1 -1

=============================================================================


-- 
Graham Wheeler                     | "That which is weak conquers the strong,
Software Systems Engineer/Student  | that which is soft conquers the hard."
Aztec Information Management/UCT   | 		Lao Tzu - Tao Te Ching Ch. 78
gram@aim1.aztec.co.za / gram@cs.uct.ac.za 
Newsgroups: comp.unix.internals