~kris/9p

9hist

6de675e78382921545dd741041adeba4a1cd05c5 — David du Colombier 36 years ago 2024b22
Plan 9 from Bell Labs 1990-03-12
M gnot/boot.c => gnot/boot.c +116 -14
@@ 6,7 6,8 @@
Fcall	hdr;
char	buf[100];

void	error(char *);
void	error(char*);
void	sendmsg(int, char*);

main(int argc, char *argv[])
{


@@ 18,22 19,123 @@ main(int argc, char *argv[])
	open("#c/cons", OWRITE);
	open("#c/cons", OWRITE);

	do{
		print("user: ");
		n = read(0, buf, sizeof buf);
	}while(n==0 || n==1);
	fd = open("#c/user", 0);
	if(fd < 0)
		error("#c/user");
	n = read(fd, buf, sizeof buf-1);
	if(n < 0)
		error("can't read #c/cons; please reboot");
	buf[n-1] = 0;
		error("can't read #c/user; please reboot");
	buf[n] = 0;
	print("hello %s!\n", buf);
	if(pipe(p) == -1)
		error("pipe");
	if(write(p[1], "hohoHO!", 8) != 8)
	close(fd);

	/*
	 *  grab the incon,
	 *  push the dk multiplexor onto it,
	 *  and use line 1 as the signalling channel.
	 */
	cfd = open("#i/ctl", 2);
	if(cfd < 0)
		error("opening #i/ctl");
	sendmsg(cfd, "push dkmux");
	sendmsg(cfd, "config 1 16");
	print("dkmux configured\n");

	/*
	 *  open a datakit channel and call ken via r70, leave the
	 *  incon ctl channel open
	 */
	fd = open("#k/2/data", 2);
	if(fd < 0)
		error("opening #k/2/data");
	cfd = open("#k/2/ctl", 2);
	if(cfd < 0)
		error("opening #k/2/ctl");
	print("#k/2/ctl open\n");
	sendmsg(cfd, "connect r70.nonet!bootes!fs");
	print("connected to r70.nonet!bootes!fs\n");
	close(cfd);

/*
	for(;;){
		print("ding\n");
		sleep(10000);
	}
/**/

	/*
	 *  talk to the file server
	 */
	print("nop...");
	hdr.type = Tnop;
	n = convS2M(&hdr, buf);
	if(write(fd, buf, n) != n)
		error("write nop");
	n = read(fd, buf, sizeof buf);
	if(n <= 0)
		error("read nop");
	if(convM2S(buf, &hdr, n) == 0) {
		print("n = %d; buf = %.2x %.2x %.2x %.2x\n",
			n, buf[0], buf[1], buf[2], buf[3]);
		error("format nop");
	}
	if(hdr.type != Rnop)
		error("not Rnop");

	print("session...");
	hdr.type = Tsession;
	hdr.lang = 'v';
	n = convS2M(&hdr, buf);
	if(write(fd, buf, n) != n)
		error("write session");
	n = read(fd, buf, sizeof buf);
	if(n <= 0)
		error("read session");
	if(convM2S(buf, &hdr, n) == 0)
		error("format session");
	if(hdr.type != Rsession)
		error("not Rsession");
	if(hdr.err){
		print("error %d;", hdr.err);
		error("remote error");
	}

	print("post...");
	sprint(buf, "#s/%s", "bootes");
	f = create(buf, 1, 0666);
	if(f < 0)
		error("create");
	sprint(buf, "%d", fd);
	if(write(f, buf, strlen(buf)) != strlen(buf))
		error("write");
	if(read(p[0], buf, 8) != 8)
		error("read");
	print("%s\n", buf);
	for(;;);
	close(f);
	sprint(buf, "#s/%s", "bootes");
	f = create("#s/boot", 1, 0666);
	if(f < 0)
		error("create");
	sprint(buf, "%d", fd);
	if(write(f, buf, strlen(buf)) != strlen(buf))
		error("write");
	close(f);
	
	print("mount...");
	if(bind("/", "/", MREPL) < 0)
		error("bind");
	if(mount(fd, "/", MAFTER|MCREATE, "") < 0)
		error("mount");
	print("success\n");
	execl("/68020/init", "init", 0);
	error("/68020/init");
}

void
sendmsg(int fd, char *msg)
{
	int n;

	n = strlen(msg);
	if(write(fd, msg, n) != n)
		error(msg);
}

void

M gnot/boot.h => gnot/boot.h +2197 -819
@@ 1,7 1,7 @@
ulong bootcode[]={
	0x00000107,
	0x00000d9c,
	0x000001bc,
	0x0000207a,
	0x00000464,
	0x00000130,
	0x00000000,
	0x00002020,


@@ 9,894 9,2102 @@ ulong bootcode[]={
	0x00000000,
	0x2f012f00,
	0x2c7c0000,
	0xbffe610c,
	0xdffe610c,
	0x2f006100,
	0x01604fef,
	0x04ce4fef,
	0x000c4e75,
	0x4fefffc8,
	0x42a7486e,
	0x81126100,
	0x0d664878,
	0x20444878,
	0x0001486e,
	0x811a6100,
	0x0d5a4878,
	0x20384878,
	0x0001486e,
	0x81226100,
	0x0d4e486e,
	0x812a6100,
	0x017a4878,
	0x202c42a7,
	0x486e812a,
	0x61002022,
	0x2f400050,
	0x4aaf0050,
	0x6c0a486e,
	0x81326100,
	0x0404588f,
	0x4878001b,
	0x486f002c,
	0x2f2f0058,
	0x61002004,
	0x240042e7,
	0x2f40005a,
	0x44df6c0e,
	0x486e813a,
	0x610003de,
	0x242f005c,
	0x588f4237,
	0x29200034,
	0x486f0034,
	0x486e815c,
	0x61000496,
	0x2f2f0064,
	0x61001fac,
	0x48780002,
	0x486e8167,
	0x61001fbe,
	0x2f400074,
	0x4aaf0074,
	0x6c0a486e,
	0x816e6100,
	0x03a0588f,
	0x486e817d,
	0x2f2f0078,
	0x61000362,
	0x486e8188,
	0x2f2f0080,
	0x61000356,
	0x486e8194,
	0x6100044e,
	0x48780002,
	0x486e81a6,
	0x61001f7e,
	0x4a8042e7,
	0x2f40008e,
	0x44df6c0a,
	0x486e81b0,
	0x6100035e,
	0x588f4878,
	0x0002486e,
	0x81c26100,
	0x1f5c4a80,
	0x42e72f40,
	0x009a44df,
	0x6c0a486e,
	0x81cb6100,
	0x033c588f,
	0x486e81dc,
	0x61000402,
	0x486e81eb,
	0x2f2f00a0,
	0x610002f6,
	0x486e8207,
	0x610003ee,
	0x2f2f00a8,
	0x61001f04,
	0x486e8229,
	0x610003de,
	0x422e854e,
	0x486f0084,
	0x486e854e,
	0x61000fba,
	0x2f002f40,
	0x00b4486f,
	0x00902f2f,
	0x00bc6100,
	0x1f04b0af,
	0x00bc670a,
	0x486e8230,
	0x610002de,
	0x588f4878,
	0x001c486f,
	0x002842a7,
	0x61000d3e,
	0x24004a82,
	0x4fef0010,
	0x67e07001,
	0xb08267da,
	0x4a8242e7,
	0x2f420046,
	0x44df6c0c,
	0x486e8131,
	0x6178242f,
	0x0048588f,
	0x42372920,
	0x001f486f,
	0x0020486e,
	0x81536100,
	0x0132486f,
	0x00206100,
	0x0ce472ff,
	0xb2806608,
	0x486e815e,
	0x614c588f,
	0x48780008,
	0x486e8163,
	0x2f2f0030,
	0x61000ce4,
	0x7208b280,
	0x6708486e,
	0x816b612e,
	0x009c2f2f,
	0x00c86100,
	0x1ede2f40,
	0x00c84aaf,
	0x00c86e0a,
	0x486e823a,
	0x610002ba,
	0x588f2f2f,
	0x00c8486e,
	0x854e486f,
	0x00ac6100,
	0x170a4a80,
	0x6638102f,
	0x00b349c0,
	0x2f00102f,
	0x00b649c0,
	0x2f00102f,
	0x00b949c0,
	0x2f00102f,
	0x00bc49c0,
	0x2f002f2f,
	0x00e4486e,
	0x82436100,
	0x0348486e,
	0x82666100,
	0x02704fef,
	0x001c0c2e,
	0x0001854e,
	0x670a486e,
	0x82716100,
	0x025c588f,
	0x486e827a,
	0x61000322,
	0x1d7c0002,
	0x854e1d7c,
	0x00768558,
	0x486f00b4,
	0x486e854e,
	0x61000ef6,
	0x2f002f40,
	0x00e4486f,
	0x00c02f2f,
	0x00ec6100,
	0x1e40b0af,
	0x00ec670a,
	0x486e8285,
	0x6100021a,
	0x588f4878,
	0x0008486f,
	0x003c2f2f,
	0x00386100,
	0x0cc07208,
	0xb2806708,
	0x486e8171,
	0x6110588f,
	0x486f0044,
	0x486e8176,
	0x610000d4,
	0x60fe4fef,
	0xffc04857,
	0x42a76100,
	0x0c8c486f,
	0x00082f2f,
	0x0050486e,
	0x817a4878,
	0x00026100,
	0x011a42a7,
	0x61744fef,
	0x005c4e75,
	0x598f4281,
	0x001c486f,
	0x00cc2f2f,
	0x00f86100,
	0x1e1a2f40,
	0x00f84aaf,
	0x00f86e0a,
	0x486e8293,
	0x610001f6,
	0x588f2f2f,
	0x00f8486e,
	0x854e486f,
	0x00dc6100,
	0x16464a80,
	0x660a486e,
	0x82a06100,
	0x01d8588f,
	0x0c2e0003,
	0x854e670a,
	0x486e82af,
	0x610001c6,
	0x588f4a6e,
	0x8552671c,
	0x302e8552,
	0x48c02f00,
	0x486e82bc,
	0x6100027e,
	0x486e82c6,
	0x610001a6,
	0x4fef000c,
	0x486e82d3,
	0x6100026a,
	0x486e82e1,
	0x486e82db,
	0x486f00ec,
	0x6100032a,
	0x487801b6,
	0x48780001,
	0x486f00f8,
	0x61001d6e,
	0x4a8042e7,
	0x2f40011a,
	0x44df6c0a,
	0x486e82e8,
	0x61000166,
	0x588f2f2f,
	0x0124486e,
	0x82ef486f,
	0x01046100,
	0x02f4486f,
	0x01086100,
	0x0dea588f,
	0x2f00486f,
	0x010c2f2f,
	0x012c6100,
	0x1d542f00,
	0x486f0118,
	0x61000dd0,
	0x588f221f,
	0xb280670a,
	0x486e82f2,
	0x61000122,
	0x588f2f2f,
	0x01306100,
	0x1d06486e,
	0x82fe486e,
	0x82f8486f,
	0x01206100,
	0x02a84878,
	0x01b64878,
	0x0001486e,
	0x83056100,
	0x1cec4a80,
	0x42e72f40,
	0x014e44df,
	0x6c0a486e,
	0x830d6100,
	0x00e4588f,
	0x2f2f0158,
	0x486e8314,
	0x486f0138,
	0x61000272,
	0x486f013c,
	0x61000d68,
	0x588f2f00,
	0x486f0140,
	0x2f2f0160,
	0x61001cd2,
	0x2f00486f,
	0x014c6100,
	0x0d4e588f,
	0x221fb280,
	0x670a486e,
	0x83176100,
	0x00a0588f,
	0x2f2f0164,
	0x61001c84,
	0x486e831d,
	0x6100015e,
	0x42a7486e,
	0x8328486e,
	0x83266100,
	0x0d364a80,
	0x6c08486e,
	0x832a6174,
	0x588f486e,
	0x83314878,
	0x0006486e,
	0x832f2f2f,
	0x01906100,
	0x1c664a80,
	0x6c08486e,
	0x83326154,
	0x588f486e,
	0x83386100,
	0x011c42a7,
	0x486e834d,
	0x486e8341,
	0x61000cc8,
	0x486e8352,
	0x61364fef,
	0x01b04e75,
	0x598f2f2f,
	0x000c6100,
	0x0cc62f00,
	0x2f400008,
	0x2f2f0014,
	0x2f2f0014,
	0x61001c2e,
	0xb0af0010,
	0x67082f2f,
	0x001c6108,
	0x588f4fef,
	0x00144e75,
	0x4fefffc0,
	0x485742a7,
	0x61001bf0,
	0x486f0008,
	0x2f2f0050,
	0x486e835e,
	0x48780002,
	0x6100011a,
	0x42a76174,
	0x4fef005c,
	0x4e75598f,
	0x42817021,
	0xb0816f1e,
	0x4ab61d20,
	0x8466660e,
	0x2daf0008,
	0x1d208466,
	0x7001588f,
	0x4e755281,
	0x7021b081,
	0x6f1e4ab6,
	0x1d2081be,
	0x660e2daf,
	0x00081d20,
	0x81be7001,
	0x6ee24280,
	0x588f4e75,
	0x52817021,
	0xb0816ee2,
	0x4280588f,
	0x4e75598f,
	0x206f0008,
	0x42827021,
	0xb0826f18,
	0x20362d20,
	0x81beb088,
	0x660642b6,
	0x2d2081be,
	0x52827021,
	0xb0826ee8,
	0x598f206f,
	0x00084282,
	0x7021b082,
	0x6f182036,
	0x2d208466,
	0xb0886606,
	0x42b62d20,
	0x84665282,
	0x7021b082,
	0x6ee8588f,
	0x4e754aaf,
	0x00046606,
	0x42a7610c,
	0x588f486e,
	0x83ae6104,
	0x588f4e75,
	0x4aaf0004,
	0x660642a7,
	0x610c588f,
	0x486e81a6,
	0x6104588f,
	0x4e75518f,
	0x72204a81,
	0x6d222eb6,
	0x1d2081be,
	0x4a976712,
	0x2f410004,
	0x42b61d20,
	0x81be4eb7,
	0x0151222f,
	0x00045381,
	0x4a816cde,
	0x2f2f000c,
	0x61000bc8,
	0x4fef000c,
	0x4e754fef,
	0xeff841ef,
	0x100c5888,
	0x2f082f2f,
	0x101041ef,
	0x0010d1fc,
	0x00001000,
	0x2f08486f,
	0x00146100,
	0x01402200,
	0x41ef0018,
	0x92882f01,
	0x486f001c,
	0x48780001,
	0x61000ba0,
	0x240042e7,
	0x2f40001e,
	0x44df6c1a,
	0x52ae800e,
	0x700ab0ae,
	0x800e6e0e,
	0x48780001,
	0x6100ff5a,
	0x242f0020,
	0x588f2002,
	0x4fef1024,
	0x4e754fef,
	0xeff841ef,
	0x10105888,
	0x2f082f2f,
	0x101441ef,
	0x0010d1fc,
	0x00001000,
	0x2f08486f,
	0x00146100,
	0x00d82200,
	0x41ef0018,
	0x92882f01,
	0x486f001c,
	0x2f2f1024,
	0x61000b38,
	0x240042e7,
	0x2f40001e,
	0x44df6c1a,
	0x52ae800e,
	0x700ab0ae,
	0x800e6e0e,
	0x48780001,
	0x6100fef2,
	0x242f0020,
	0x588f2002,
	0x4fef1024,
	0x4e75518f,
	0x226f000c,
	0x2eae8012,
	0x518f7220,
	0x4a816d22,
	0x2eb61d20,
	0x84664a97,
	0x67122f41,
	0x000442b6,
	0x1d208466,
	0x4eb70151,
	0x222f0004,
	0x53814a81,
	0x6cde2f2f,
	0x000c6100,
	0x1b204fef,
	0x000c4e75,
	0x4fefeff8,
	0x41ef100c,
	0x58882f08,
	0x2f2f1010,
	0x41ef0010,
	0xd1fc0000,
	0x10002f08,
	0x486f0014,
	0x61000140,
	0x220041ef,
	0x00189288,
	0x2f01486f,
	0x001c4878,
	0x00016100,
	0x1b102400,
	0x42e72f40,
	0x001e44df,
	0x6c1a52ae,
	0x800e700a,
	0xb0ae800e,
	0x6e0e4878,
	0x00016100,
	0xff5a242f,
	0x0020588f,
	0x20024fef,
	0x10244e75,
	0x4fefeff8,
	0x41ef1010,
	0x58882f08,
	0x2f2f0014,
	0x48691000,
	0x2f096174,
	0x2d6f0010,
	0x801290af,
	0x001c4fef,
	0x00184e75,
	0x142f0007,
	0xc43c007f,
	0x100249c0,
	0x4a360920,
	0x8092661e,
	0x701eb0ae,
	0x80166e04,
	0x70014e75,
	0x202e8016,
	0x120249c1,
	0x1d801920,
	0x809252ae,
	0x2f2f1014,
	0x41ef0010,
	0xd1fc0000,
	0x10002f08,
	0x486f0014,
	0x610000d8,
	0x220041ef,
	0x00189288,
	0x2f01486f,
	0x001c2f2f,
	0x10246100,
	0x1aa82400,
	0x42e72f40,
	0x001e44df,
	0x6c1a52ae,
	0x800e700a,
	0xb0ae800e,
	0x6e0e4878,
	0x00016100,
	0xfef2242f,
	0x0020588f,
	0x20024fef,
	0x10244e75,
	0x518f226f,
	0x000c2eae,
	0x801241ef,
	0x00105888,
	0x2f082f2f,
	0x00144869,
	0x10002f09,
	0x61742d6f,
	0x00108012,
	0x90af001c,
	0x4fef0018,
	0x4e75142f,
	0x0007c43c,
	0x007f1002,
	0x49c04a36,
	0x09208092,
	0x661e701e,
	0xb0ae8016,
	0x6e047001,
	0x4e75202e,
	0x80161202,
	0x49c11036,
	0x19208092,
	0x49c02daf,
	0x00080d20,
	0x801a4280,
	0x4e75206f,
	0x00042f2f,
	0x000c2f2f,
	0x000c4868,
	0x04002f08,
	0x61064fef,
	0x00104e75,
	0x4fefffe4,
	0x226f0028,
	0x2f6e8006,
	0x00042eae,
	0x80022d6f,
	0x00208006,
	0x202f0024,
	0x53802d40,
	0x80021019,
	0x49c02400,
	0x7025b082,
	0x675a4a82,
	0x661a4236,
	0x01618006,
	0x202e8006,
	0x2d6f0004,
	0x80062d57,
	0x80024fef,
	0x001c4e75,
	0x202e8006,
	0xb0ae8002,
	0x640a1d82,
	0x01618006,
	0x52ae8006,
	0x52ae8012,
	0x700ab082,
	0x660642ae,
	0x801260b2,
	0x7009b082,
	0x66ac7007,
	0xd0ae8012,
	0xc0bcffff,
	0xfff82d40,
	0x8012609a,
	0x428474ff,
	0x2a0242af,
	0x00101419,
	0x49c22f49,
	0x00284283,
	0x702db082,
	0x660c7401,
	0x26021419,
	0x49c22f49,
	0x00287030,
	0xb0826e08,
	0x7039b082,
	0x6c0000b6,
	0x4a836702,
	0x4484702e,
	0xb082676a,
	0x4a826606,
	0x53892f49,
	0x00282f2f,
	0x00102f05,
	0x2f45001c,
	0x2f042f44,
	0x00242f2f,
	0x0038727f,
	0xc2821036,
	0x49c11d80,
	0x19208092,
	0x49c02076,
	0x52ae8016,
	0x120249c1,
	0x10361920,
	0x809249c0,
	0x2daf0008,
	0x0d20801a,
	0x4e90226f,
	0x00382a2f,
	0x0024282f,
	0x00282400,
	0x6c164482,
	0x85af0020,
	0x42804e75,
	0x206f0004,
	0x2f2f000c,
	0x2f2f000c,
	0x48680400,
	0x2f086106,
	0x4fef0010,
	0x4e754fef,
	0xffe4226f,
	0x00282f6e,
	0x80060004,
	0x2eae8002,
	0x2d6f0020,
	0x8006202f,
	0x00245380,
	0x2d408002,
	0x101949c0,
	0x24007025,
	0xb082675a,
	0x4a82661a,
	0x42360161,
	0x8006202e,
	0x80062d6f,
	0x00048006,
	0x2d578002,
	0x4fef001c,
	0x4e75202e,
	0x8006b0ae,
	0x8002640a,
	0x1d820161,
	0x800652ae,
	0x800652ae,
	0x8012700a,
	0xb0826606,
	0x42ae8012,
	0x60b27009,
	0xb08266ac,
	0x7007d0ae,
	0x8012c0bc,
	0xfffffff8,
	0x2d408012,
	0x609a4284,
	0x74ff2a02,
	0x42af0010,
	0x141949c2,
	0x2f490028,
	0x4283702d,
	0xb082660c,
	0x74012602,
	0x141949c2,
	0x20022f49,
	0x00384fef,
	0x001060a8,
	0x202f003c,
	0xd0822f40,
	0x003c4fef,
	0x00106000,
	0xfef21019,
	0x49c02400,
	0x2f490028,
	0x7030b082,
	0x6e867039,
	0xb0826d80,
	0x4a856c02,
	0x42852005,
	0x2205d281,
	0xd281d081,
	0xd080d082,
	0x90bc0000,
	0x00302a00,
	0x6e087039,
	0xb0826c00,
	0x00b64a83,
	0x67024484,
	0x702eb082,
	0x676a4a82,
	0x66065389,
	0x2f490028,
	0x2f2f0010,
	0x2f052f45,
	0x001c2f04,
	0x2f440024,
	0x2f2f0038,
	0x727fc282,
	0x10361920,
	0x809249c0,
	0x20760d20,
	0x801a4e90,
	0x226f0038,
	0x2a2f0024,
	0x282f0028,
	0x24006c16,
	0x448285af,
	0x00201419,
	0x49c22002,
	0x2f490038,
	0x4fef0010,
	0x60a8202f,
	0x003cd082,
	0x2f40003c,
	0x4fef0010,
	0x6000fef2,
	0x101949c0,
	0x24002f49,
	0x002860cc,
	0x20042204,
	0x00287030,
	0xb0826e86,
	0x7039b082,
	0x6d804a85,
	0x6c024285,
	0x20052205,
	0xd281d281,
	0xd081d080,
	0xd08290bc,
	0x00000030,
	0x28001019,
	0x2a001019,
	0x49c02400,
	0x2f490028,
	0x6000ff1c,
	0x4fefffc4,
	0x282f004c,
	0x226f0040,
	0x2a2f0050,
	0x206f0048,
	0x262f0004,
	0x4286700f,
	0xc0847201,
	0xb0816d00,
	0x00f27209,
	0xb0816e00,
	0x00ea3036,
	0x0b208190,
	0x4efb0000,
	0x7004c084,
	0x66044a83,
	0x6d72422f,
	0x0039741c,
	0x20034c45,
	0x00012801,
	0x7030d880,
	0x7039b084,
	0x6c047027,
	0xd8802202,
	0x1f842920,
	0x001c7002,
	0xb0826f2a,
	0x4a86670a,
	0x53821fbc,
	0x002d2920,
	0x001c4878,
	0xffff2f2f,
	0x00484877,
	0x29200024,
	0x610000e0,
	0x202f0018,
	0x4fef0048,
	0x4e752003,
	0x60cc2004,
	0x2204d281,
	0xd281d081,
	0xd080d082,
	0x90bc0000,
	0x00302800,
	0x101949c0,
	0x24002f49,
	0x00286000,
	0xff1c4fef,
	0xffc4282f,
	0x004c226f,
	0x00402a2f,
	0x0050206f,
	0x0048262f,
	0x00044286,
	0x700fc084,
	0x7201b081,
	0x6d0000f2,
	0x7209b081,
	0x6e0000ea,
	0x30360b20,
	0x83984efb,
	0x00007004,
	0xc0846604,
	0x4a836d72,
	0x422f0039,
	0x741c2003,
	0x4c450001,
	0x2600b0fc,
	0x00006d08,
	0x701e9088,
	0xb0826f04,
	0x4a836fbc,
	0x53826094,
	0x44837c01,
	0x60882611,
	0x70042f40,
	0x000c4a83,
	0x6c044483,
	0x7c01422f,
	0x0039741c,
	0x28017030,
	0xd8807039,
	0xb0846c04,
	0x7027d880,
	0x22021f84,
	0x2920001c,
	0x7002b082,
	0x6f2a4a86,
	0x670a5382,
	0x1fbc002d,
	0x2920001c,
	0x4878ffff,
	0x2f2f0048,
	0x48772920,
	0x00246100,
	0x00e0202f,
	0x00184fef,
	0x00484e75,
	0x20034c45,
	0x08012801,
	0x7030d880,
	0x7039b084,
	0x6c047027,
	0xd8802202,
	0x1f842920,
	0x001c7002,
	0xb0826e00,
	0xff782003,
	0x4c450801,
	0x2600b0fc,
	0x00006d08,
	0x701e9088,
	0xb0826f06,
	0x4a836f00,
	0xff5c5382,
	0x60ba2611,
	0x70042f40,
	0x000c6000,
	0xff182611,
	0x34034283,
	0x36027004,
	0x2f40000c,
	0x6000ff06,
	0x00012600,
	0xb0fc0000,
	0x6d08701e,
	0x9088b082,
	0x6f044a83,
	0x6fbc5382,
	0x60944483,
	0x7c016088,
	0x26117004,
	0x2f40000c,
	0x6000fefa,
	0x4a836c04,
	0x44837c01,
	0x422f0039,
	0x741c2003,
	0x4c450801,
	0x28017030,
	0xd8807039,
	0xb0846c04,
	0x7027d880,
	0x22021f84,
	0x2920001c,
	0x7002b082,
	0x6e00ff78,
	0x20034c45,
	0x08012600,
	0xb0fc0000,
	0x6d08701e,
	0x9088b082,
	0x6f064a83,
	0x6f00ff5c,
	0x538260ba,
	0x26117004,
	0x2f40000c,
	0x6000feee,
	0x6000ff18,
	0x26113403,
	0x360248c3,
	0x42833602,
	0x70042f40,
	0x000c6000,
	0xfedc2611,
	0xff062611,
	0x70042f40,
	0x000c6000,
	0xfed04fef,
	0xfff4242f,
	0x0010282f,
	0x0014262f,
	0x0018226e,
	0x8002206e,
	0x80064281,
	0x24424a1a,
	0x67065281,
	0x4a1a66fa,
	0x4a846d1e,
	0x2001b284,
	0x6c18b1c9,
	0x640810fc,
	0x00202d48,
	0x800652ae,
	0x80125281,
	0x2001b284,
	0x6de82442,
	0x101a49c0,
	0x24006726,
	0x4a8367f4,
	0xb1c96406,
	0x10c22d48,
	0x800652ae,
	0x8012700a,
	0xb0826636,
	0x42ae8012,
	0x5383101a,
	0xfefa2611,
	0x70042f40,
	0x000c6000,
	0xfeee2611,
	0x34033602,
	0x48c37004,
	0x2f40000c,
	0x6000fedc,
	0x26117004,
	0x2f40000c,
	0x6000fed0,
	0x4feffff4,
	0x242f0010,
	0x282f0014,
	0x262f0018,
	0x226e8002,
	0x206e8006,
	0x42812442,
	0x4a1a6706,
	0x52814a1a,
	0x66fa4a84,
	0x6d1e2001,
	0xb2846c18,
	0xb1c96408,
	0x10fc0020,
	0x2d488006,
	0x52ae8012,
	0x52812001,
	0xb2846de8,
	0x2442101a,
	0x49c02400,
	0x66da4a84,
	0x6c1e2404,
	0x4482b282,
	0x6c16b1c9,
	0x640810fc,
	0x00202d48,
	0x800652ae,
	0x80125281,
	0xb2826dea,
	0x4fef000c,
	0x4e757009,
	0xb08266c8,
	0x7007d0ae,
	0x8012c0bc,
	0xfffffff8,
	0x2d408012,
	0x60b64878,
	0xffff42a7,
	0x486e818a,
	0x6100ff3c,
	0x42804fef,
	0x67264a83,
	0x67f4b1c9,
	0x640610c2,
	0x2d488006,
	0x52ae8012,
	0x700ab082,
	0x663642ae,
	0x80125383,
	0x101a49c0,
	0x240066da,
	0x4a846c1e,
	0x24044482,
	0xb2826c16,
	0xb1c96408,
	0x10fc0020,
	0x2d488006,
	0x52ae8012,
	0x5281b282,
	0x6dea4fef,
	0x000c4e75,
	0x598f2037,
	0x01610008,
	0x1e80422f,
	0x00014878,
	0xffff2f2f,
	0x0010486f,
	0x00086100,
	0xff167004,
	0x4fef0010,
	0x4e754878,
	0x000a2f2f,
	0x00142f2f,
	0x00142f2f,
	0x00142f2f,
	0x00146100,
	0xfd8c4fef,
	0x00144e75,
	0x70fe4e75,
	0x7001c0af,
	0x00106704,
	0x70f84e75,
	0x70ff4e75,
	0x48780008,
	0x2f2f0014,
	0x2f2f0014,
	0x2f2f0014,
	0x2f2f0014,
	0x6100fd5a,
	0x4fef0014,
	0x4e752f2f,
	0x000c2f2f,
	0x000c2f37,
	0x0161000c,
	0x6100feac,
	0x7009b082,
	0x66c87007,
	0xd0ae8012,
	0xc0bcffff,
	0xfff82d40,
	0x801260b6,
	0x4878ffff,
	0x42a7486e,
	0x83726100,
	0xff3c4280,
	0x4fef000c,
	0x4e75598f,
	0x20370161,
	0x00081e80,
	0x422f0001,
	0x4878ffff,
	0x2f2f0010,
	0x486f0008,
	0x6100ff16,
	0x70044fef,
	0x000c4e75,
	0x70fc4e75,
	0x48780010,
	0x00104e75,
	0x4878000a,
	0x2f2f0014,
	0x2f2f0014,
	0x2f2f0014,
	0x2f2f0014,
	0x6100fd1e,
	0x6100fd8c,
	0x4fef0014,
	0x4e75202e,
	0x8006b0ae,
	0x8002640c,
	0x1dbc0025,
	0x01618006,
	0x52ae8006,
	0x42804e75,
	0x4fefff80,
	0x2e2f0090,
	0x42af000c,
	0xf22f543a,
	0x0084f293,
	0x0016f22f,
	0x54000084,
	0xf200001a,
	0xf22f7400,
	0x008452af,
	0x000c42af,
	0x001cf23c,
	0x40000000,
	0x0000f200,
	0x0100f22f,
	0x4e7570fe,
	0x4e757001,
	0xc0af0010,
	0x670470f8,
	0x4e7570ff,
	0x4e754878,
	0x00082f2f,
	0x00142f2f,
	0x00142f2f,
	0x00142f2f,
	0x00146100,
	0xfd5a4fef,
	0x00144e75,
	0x2f2f000c,
	0x2f2f000c,
	0x2f370161,
	0x000c6100,
	0xfeac7004,
	0x4fef000c,
	0x4e7570fc,
	0x4e754878,
	0x00102f2f,
	0x00142f2f,
	0x00142f2f,
	0x00142f2f,
	0x00146100,
	0xfd1e4fef,
	0x00144e75,
	0x202e8006,
	0xb0ae8002,
	0x640c1dbc,
	0x00250161,
	0x800652ae,
	0x80064280,
	0x4e754fef,
	0xff802e2f,
	0x009042af,
	0x000cf22f,
	0x543a0084,
	0xf28100ee,
	0x486f001c,
	0xf2930016,
	0xf22f5400,
	0x0088f227,
	0x74006100,
	0x0412f22f,
	0x40000028,
	0xf23c5423,
	0x3fd34413,
	0x55475a32,
	0xf22f6000,
	0x0028202f,
	0x0028e280,
	0x2f400024,
	0x44802f00,
	0x6100053a,
	0xf22f5423,
	0x0094f22f,
	0x74000030,
	0x0084f200,
	0x001af22f,
	0x74000084,
	0x52af000c,
	0x42af001c,
	0xf23c4000,
	0x00000000,
	0xf2000100,
	0xf22f543a,
	0x0084f281,
	0x00ee486f,
	0x001cf22f,
	0x54000088,
	0xf2277400,
	0x61000412,
	0xf22f4000,
	0x0028f23c,
	0x54233fd3,
	0x44135547,
	0x5a32f22f,
	0x60000028,
	0x202f0028,
	0x90af002c,
	0xe2802f40,
	0x00244480,
	0x2f006100,
	0x05202e2f,
	0x00a4f22f,
	0x54230034,
	0xf2000100,
	0x053af22f,
	0x54230094,
	0xf22f7400,
	0x0030202f,
	0x002890af,
	0x002c2f00,
	0x61000520,
	0x2e2f00a4,
	0xf22f5423,
	0x0034f200,
	0x0100f23c,
	0x40000000,
	0x0001f200,
	0x0838f295,
	0x003453af,
	0x0030202f,
	0x002c90af,
	0x00302f00,
	0x610004f0,
	0x2e2f00a8,
	0xf22f5423,
	0x0038f200,
	0x0100f23c,
	0x40000000,
	0x0001f200,
	0x0838588f,
	0xf292ffd0,
	0xf23c4000,
	0x00000001,
	0x0000000a,
	0xf2000838,
	0xf2950034,
	0x53af0030,
	0x202f002c,
	0x90af0030,
	0x4fef0014,
	0xf2920034,
	0x52af001c,
	0x202f0018,
	0x90af001c,
	0x2f006100,
	0x04f02e2f,
	0x00a8f22f,
	0x54230038,
	0x04aa2e2f,
	0x0094f22f,
	0x54230024,
	0xf2000100,
	0xf23c4000,
	0x00000001,
	0x0000000a,
	0xf2000838,
	0x588ff292,
	0xffd0f23c,
	0x40000000,
	0x000af200,
	0x08384fef,
	0x0014f292,
	0x003452af,
	0x001c202f,
	0x001890af,
	0x001c2f00,
	0x610004aa,
	0x2e2f0094,
	0xf22f5423,
	0x0024f200,
	0x0100f23c,
	0x40000000,
	0x000af200,
	0x0838588f,
	0xf295ffd0,
	0x4a876c02,
	0x7e087067,
	0x588ff295,
	0xffd04a87,
	0x6c027e08,
	0x7067b0af,
	0x00986606,
	0x4a876e00,
	0x02ac701e,
	0xb0876c02,
	0x7e1e7402,
	0xd4872f47,
	0x00907066,
	0xb0af0098,
	0x66064a87,
	0x6e0002ac,
	0x701eb087,
	0x6c027e1e,
	0x7402d487,
	0x6616202f,
	0x001cd480,
	0x6e0e7401,
	0xf23c4000,
	0x00000000,
	0xf2000100,
	0x7020b082,
	0x6e187065,
	0xb0af0098,
	0x66067eff,
	0x2f470090,
	0x7066b0af,
	0x00986616,
	0x202f001c,
	0xd4806e0e,
	0x7401f23c,
	0x40000000,
	0x0000f200,
	0x01007020,
	0xb0826e18,
	0x7065b0af,
	0x00986606,
	0x7eff2f47,
	0x00907065,
	0x2f400098,
	0x6000fe9c,
	0x4284b882,
	0x6c3cf203,
	0x6100f203,
	0x4000f200,
	0x0838f295,
	0x00045383,
	0x70652f40,
	0x00986000,
	0xfe9c4284,
	0xb8826c3c,
	0xf2036100,
	0xf2034000,
	0xf2000128,
	0x7030d083,
	0x22041f80,
	0x49200059,
	0xf2000880,
	0xf23c4123,
	0x0000000a,
	0xf2000800,
	0x5284b882,
	0x6dc47605,
	0x70ffd082,
	0x24004a82,
	0x6d382002,
	0x41f72920,
	0x00591010,
	0x49c0d083,
	0x10804283,
	0x0c370039,
	0xf2000838,
	0xf2950004,
	0x5383f203,
	0x4000f200,
	0x01287030,
	0xd0832204,
	0x1f804920,
	0x0059f200,
	0x0880f23c,
	0x41230000,
	0x000af200,
	0x08005284,
	0xb8826dc4,
	0x760570ff,
	0xd0822400,
	0x4a826d38,
	0x200241f7,
	0x29200059,
	0x6f162002,
	0x41f72920,
	0x00591010,
	0x49c090bc,
	0x0000000a,
	0x10805283,
	0x53824a82,
	0x6cc87a01,
	0x4a83670c,
	0x1f7c0031,
	0x005852af,
	0x001c4285,
	0x42824aaf,
	0x000c670a,
	0x1fbc002d,
	0x29200030,
	0x52824283,
	0x7801d887,
	0x2c077067,
	0xb0af0098,
	0x661270fb,
	0xb0af001c,
	0x6e0a202f,
	0x001cb087,
	0x6f00017a,
	0x7066b0af,
	0x0098661a,
	0x262f001c,
	0x44836c02,
	0x4283b687,
	0x6f022604,
	0x202f001c,
	0xd8806c02,
	0x42844a83,
	0x6f222003,
	0xd084b086,
	0x660a1fbc,
	0x002e2920,
	0x101049c0,
	0xd0831080,
	0x42830c37,
	0x00392920,
	0x00596f16,
	0x200241f7,
	0x29200059,
	0x101049c0,
	0x90bc0000,
	0x000a1080,
	0x52835382,
	0x4a826cc8,
	0x7a014a83,
	0x670c1f7c,
	0x00310058,
	0x52af001c,
	0x42854282,
	0x4aaf000c,
	0x670a1fbc,
	0x002d2920,
	0x00305282,
	0x1fbc0030,
	0x29200030,
	0x52825383,
	0x4a836ede,
	0x4a846f28,
	0x42837801,
	0xd8872c07,
	0x7067b0af,
	0x00986612,
	0x70fbb0af,
	0x001c6e0a,
	0x202f001c,
	0xb0876f00,
	0x017a7066,
	0xb0af0098,
	0x661a262f,
	0x001c4483,
	0x6c024283,
	0xb6876f02,
	0x2604202f,
	0x001cd880,
	0x6c024284,
	0x4a836f22,
	0x2003d084,
	0xb086660a,
	0x1fbc002e,
	0x29200030,
	0x52822202,
	0x1fb75920,
	0x00582920,
	0x52821fbc,
	0x00302920,
	0x00305282,
	0x52855384,
	0x4a846ed8,
	0x7067b0af,
	0x53834a83,
	0x6ede4a84,
	0x6f282003,
	0xd084b086,
	0x660a1fbc,
	0x002e2920,
	0x00305282,
	0x22021fb7,
	0x59200058,
	0x29200030,
	0x52825285,
	0x53844a84,
	0x6ed87067,
	0xb0af0098,
	0x670000c2,
	0x7068b0af,
	0x00986700,
	0x00c27068,
	0x00b87065,
	0xb0af0098,
	0x670000b8,
	0x7065b0af,
	0x00986726,
	0x7067b0af,
	0x0098671e,
	0x42372920,
	0x00304878,
	0xffff2f2f,
	0x0090486f,
	0x00386100,
	0xfb5e7008,
	0x4fef008c,
	0x4e751fbc,
	0x00652920,
	0x00305282,
	0x1fbc002b,
	0x67267067,
	0xb0af0098,
	0x671e4237,
	0x29200030,
	0x5282262f,
	0x001c6c0a,
	0x1fbc002d,
	0x2920002f,
	0x44837064,
	0xb0836e26,
	0x20034c7c,
	0x08010000,
	0x0064d0bc,
	0x00000030,
	0x22021f80,
	0x4878ffff,
	0x2f2f0090,
	0x486f0038,
	0x6100fb5e,
	0x70084fef,
	0x008c4e75,
	0x1fbc0065,
	0x29200030,
	0x52822003,
	0x52821fbc,
	0x002b2920,
	0x00305282,
	0x262f001c,
	0x6c0a1fbc,
	0x002d2920,
	0x002f4483,
	0x7064b083,
	0x6e262003,
	0x4c7c0801,
	0x00000064,
	0x26012003,
	0x4c7c0801,
	0x0000000a,
	0xd0bc0000,
	0x00302202,
	0x1f802920,
	0x00305282,
	0x20034c7c,
	0x08010000,
	0x000a2001,
	0xd0bc0000,
	0x00301f80,
	0x00642601,
	0x20034c7c,
	0x08010000,
	0x000ad0bc,
	0x00000030,
	0x22021f80,
	0x29200030,
	0x52822003,
	0x4c7c0801,
	0x0000000a,
	0x2001d0bc,
	0x00000030,
	0x1f802920,
	0x00305282,
	0x6000ff5c,
	0x76ffd682,
	0x4a836d0a,
	0x0c370030,
	0x39200030,
	0x67242803,
	0x4a846d00,
	0xff320c37,
	0x002e4920,
	0x0030660e,
	0x2403b883,
	0x6700ff20,
	0x52826000,
	0xff5c76ff,
	0xd6824a83,
	0x6d0a0c37,
	0x00303920,
	0x00306724,
	0x28034a84,
	0x6d00ff32,
	0x0c37002e,
	0x49200030,
	0x660e2403,
	0xb8836700,
	0xff205282,
	0x6000ff1a,
	0x538460de,
	0x538360ca,
	0x76ff96af,
	0x001c6c02,
	0x42832c07,
	0x9caf001c,
	0x70682f40,
	0x00986000,
	0xfe705387,
	0x6000fd52,
	0x48780065,
	0xff1a5384,
	0x60de5383,
	0x60ca76ff,
	0x96af001c,
	0x6c024283,
	0x2c079caf,
	0x001c7068,
	0x2f400098,
	0x6000fe70,
	0x53876000,
	0xfd524878,
	0x00652f2f,
	0x00142f2f,
	0x00142f2f,
	0x0014f237,
	0x54000161,
	0x0014f227,
	0x74006100,
	0xfbee4fef,
	0x00184e75,
	0x48780066,
	0x2f2f0014,
	0x2f2f0014,
	0x2f2f0014,
	0xf2375400,
	0x01610014,
	0xf2277400,
	0x6100fbee,
	0x6100fbc8,
	0x4fef0018,
	0x4e754878,
	0x00662f2f,
	0x00672f2f,
	0x00142f2f,
	0x00142f2f,
	0x0014f237,
	0x54000161,
	0x0014f227,
	0x74006100,
	0xfbc84fef,
	0xfba24fef,
	0x00184e75,
	0x48780067,
	0x2f2f0014,
	0x2f2f0014,
	0x2f2f0014,
	0xf2375400,
	0x01610014,
	0xf2277400,
	0x6100fba2,
	0x4fef0018,
	0x518ff22f,
	0x5500000c,
	0x206f0014,
	0xf200083a,
	0xf28e0010,
	0x4290f23c,
	0x40000000,
	0x0000508f,
	0x4e75f217,
	0x75002017,
	0x7214e2a0,
	0xc0bc0000,
	0x07ff90bc,
	0x000003fe,
	0x20800297,
	0x800fffff,
	0x00973fe0,
	0x0000f217,
	0x5400508f,
	0x4e75518f,
	0xf22f5500,
	0x000c206f,
	0x000c242f,
	0x0014f200,
	0x083af28e,
	0x00104290,
	0xf23c4000,
	0x00000000,
	0x508f4e75,
	0xf2177500,
	0x20177214,
	0xe2a0c0bc,
	0x000ef23c,
	0x40000000,
	0x0000508f,
	0x4e75f217,
	0x75002017,
	0x7214e2a0,
	0xc0bc0000,
	0x07ffd480,
	0x6e0cf23c,
	0x40000000,
	0x0000508f,
	0x4e750c82,
	0x000007ff,
	0x90bc0000,
	0x03fe2080,
	0x0297800f,
	0xffff0097,
	0x3fe00000,
	0x6d0c7022,
	0x2d40800a,
	0x243c0000,
	0x07ff0297,
	0x800fffff,
	0x20027214,
	0xe3a08197,
	0xf2175400,
	0x508f4e75,
	0x518ff22f,
	0x5500000c,
	0x242f0014,
	0xf200083a,
	0xf28e000e,
	0xf23c4000,
	0x00000000,
	0x508f4e75,
	0xf2177500,
	0x20177214,
	0xe2a0c0bc,
	0x000007ff,
	0xd4806e0c,
	0xf23c4000,
	0x00000000,
	0x508f4e75,
	0x0c820000,
	0x07ff6d0c,
	0x70222d40,
	0x800a243c,
	0x4feffff4,
	0x206f0018,
	0xf22f5500,
	0x0010f23c,
	0x40000000,
	0x0001f200,
	0x0838f295,
	0x0018f23c,
	0x40000000,
	0x0000f210,
	0x7400f200,
	0x08004fef,
	0x000c4e75,
	0xf22f7500,
	0x0004242f,
	0x00047214,
	0xe2a2c4bc,
	0x000007ff,
	0x0297800f,
	0xffff2002,
	0x7214e3a0,
	0x8197f217,
	0x5400508f,
	0x4e754fef,
	0xfff4206f,
	0x0018f22f,
	0x55000010,
	0xf23c4000,
	0x00000001,
	0xf2000838,
	0xf2950018,
	0xf23c4000,
	0x00000000,
	0x94bc0000,
	0x03fe7015,
	0xb0826d2c,
	0x203c001f,
	0xffffe4a0,
	0x4680c1af,
	0x000442af,
	0x0008f22f,
	0x54000004,
	0xf2107400,
	0xf2000800,
	0xf22f5428,
	0x00044fef,
	0x000c4e75,
	0x7035b082,
	0x6de072ea,
	0xd282203c,
	0x7fffffff,
	0xe2a04680,
	0xc1af0008,
	0x60ccf22f,
	0x40120004,
	0x4e7541ef,
	0x00045888,
	0x2f082f2f,
	0x00086100,
	0x0f68508f,
	0x4e75226f,
	0x00044a19,
	0x670c2449,
	0x4a1966fc,
	0x93ca2009,
	0x4e757000,
	0x4e757002,
	0x4e404e75,
	0x598f226f,
	0x0008206f,
	0x000c10d1,
	0x101149c0,
	0x4201b001,
	0x6d0005f0,
	0x7227b001,
	0x6e0005e8,
	0x30360b20,
	0x83c64efb,
	0x00002008,
	0x90af000c,
	0x588f4e75,
	0x30290002,
	0x10803029,
	0x000248c0,
	0xe0801140,
	0x00015488,
	0x30290004,
	0x10803029,
	0x000448c0,
	0xe0801140,
	0x00015488,
	0x2029000a,
	0x10802029,
	0x000ae080,
	0x11400001,
	0x548860ba,
	0x30290002,
	0x10803029,
	0x000248c0,
	0xe0801140,
	0x00015488,
	0x20290006,
	0x10802029,
	0x0006e080,
	0x11400001,
	0x20290006,
	0x7210e2a0,
	0x11400002,
	0x20290006,
	0x7218e2a0,
	0x11400003,
	0x42280004,
	0x42280005,
	0x42280006,
	0x42280007,
	0x50882029,
	0x000a1080,
	0x2029000a,
	0xe0801140,
	0x00015488,
	0x52882f29,
	0x000a2f29,
	0x000e2f08,
	0x2f48000c,
	0x61000df2,
	0x206f000c,
	0x20370162,
	0x0014000a,
	0xd1c04fef,
	0x000c6000,
	0xff323029,
	0x00021080,
	0x30290002,
	0x48c0e080,
	0x11400001,
	0x54883029,
	0x00041080,
	0x30290004,
	0x48c0e080,
	0x11400001,
	0x54882029,
	0x000a1080,
	0x2029000a,
	0xe0801140,
	0x00015488,
	0x52882f29,
	0x000a2f29,
	0x000e2f08,
	0x2f48000c,
	0x61000d8e,
	0x206f000c,
	0x20370162,
	0x0014000a,
	0xd1c04fef,
	0x000c6000,
	0xfece3029,
	0x00021080,
	0x30290002,
	0x48c0e080,
	0x11400001,
	0x54882029,
	0x00061080,
	0x20290006,
	0xe0801140,
	0x00012029,
	0x00067210,
	0xe2a01140,
	0x00022029,
	0x00067218,
	0xe2a01140,
	0x00034228,
	0x00044228,
	0x00054228,
	0x00064228,
	0x00075088,
	0x2029000a,
	0x10802029,
	0x000ae080,
	0x11400001,
	0x54886000,
	0xfe6a3029,
	0x00021080,
	0x30290002,
	0x48c0e080,
	0x11400001,
	0x54883029,
	0x00041080,
	0x30290004,
	0x48c0e080,
	0x11400001,
	0x54884878,
	0x001c4869,
	0x00062f08,
	0x2f48000c,
	0x61000cda,
	0x206f000c,
	0x701cd1c0,
	0x4fef000c,
	0x4e75f22f,
	0x75000004,
	0x242f0004,
	0x7214e2a2,
	0xc4bc0000,
	0x07ff94bc,
	0x000003fe,
	0x7015b082,
	0x6d2c203c,
	0x001fffff,
	0xe4a04680,
	0xc1af0004,
	0x42af0008,
	0xf22f5400,
	0x0004f210,
	0x7400f200,
	0x0800f22f,
	0x54280004,
	0x6000fe20,
	0x30290002,
	0x10803029,
	0x000248c0,
	0xe0801140,
	0x00015488,
	0x30290006,
	0x10803029,
	0x000648c0,
	0xe0801140,
	0x00015488,
	0x6000fdf4,
	0x30290002,
	0x10803029,
	0x000248c0,
	0xe0801140,
	0x00015488,
	0x30290004,
	0x10803029,
	0x000448c0,
	0xe0801140,
	0x00015488,
	0x48780040,
	0x48690006,
	0x2f082f48,
	0x000c6100,
	0x0c64206f,
	0x000c7040,
	0xd1c04fef,
	0x000c6000,
	0xfdaa3029,
	0x00021080,
	0x30290002,
	0x48c0e080,
	0x11400001,
	0x54883029,
	0x00041080,
	0x30290004,
	0x48c0e080,
	0x11400001,
	0x54886000,
	0xfd7e3029,
	0x00021080,
	0x30290002,
	0x48c0e080,
	0x11400001,
	0x54883029,
	0x00041080,
	0x30290004,
	0x48c0e080,
	0x11400001,
	0x54886000,
	0xfd523029,
	0x00021080,
	0x30290002,
	0x48c0e080,
	0x11400001,
	0x54884878,
	0x00404869,
	0x00062f08,
	0x2f48000c,
	0x61000bd6,
	0x206f000c,
	0x7040d1c0,
	0x4fef000c,
	0x4e757035,
	0xb0826de0,
	0x72ead282,
	0x203c7fff,
	0xffffe2a0,
	0x4680c1af,
	0x000860cc,
	0xf22f4012,
	0x6000fd1c,
	0x30290002,
	0x10803029,
	0x000248c0,
	0xe0801140,
	0x00015488,
	0x30290004,
	0x10803029,
	0x000448c0,
	0xe0801140,
	0x00015488,
	0x48780040,
	0x48690006,
	0x2f082f48,
	0x000c6100,
	0x0b8c206f,
	0x000c7040,
	0xd1c04fef,
	0x000c6000,
	0xfcd23029,
	0x00021080,
	0x30290002,
	0x48c0e080,
	0x11400001,
	0x54886000,
	0xfcba6000,
	0xff3a3029,
	0x00021080,
	0x30290002,
	0x48c0e080,
	0x11400001,
	0x54884878,
	0x001c4869,
	0x000a2f08,
	0x2f48000c,
	0x61000b3a,
	0x206f000c,
	0x701cd1c0,
	0x4fef000c,
	0x6000fc80,
	0x60c460aa,
	0x6000fc82,
	0x30290002,
	0x10803029,
	0x000248c0,
	0xe0801140,
	0x00015488,
	0x20290006,
	0x10802029,
	0x0006e080,
	0x11400001,
	0x20290006,
	0x7210e2a0,
	0x11400002,
	0x20290006,
	0x7218e2a0,
	0x11400003,
	0x42280004,
	0x42280005,
	0x42280006,
	0x42280007,
	0x50882029,
	0x000a1080,
	0x2029000a,
	0xe0801140,
	0x00015488,
	0x2f29000a,
	0x2f29000e,
	0x2f082f48,
	0x000c6100,
	0x0ab0206f,
	0x000c2037,
	0x01620014,
	0x000ad1c0,
	0x4fef000c,
	0x6000fbf0,
	0x30290002,
	0x10803029,
	0x000248c0,
	0xe0801140,
	0x00015488,
	0x30290004,
	0x10803029,
	0x000448c0,
	0xe0801140,
	0x00015488,
	0x2029000a,
	0x10802029,
	0x000ae080,
	0x11400001,
	0x54882f29,
	0x000a2f29,
	0x000e2f08,
	0x2f48000c,
	0x61000a4e,
	0x206f000c,
	0x20370162,
	0x0014000a,
	0xd1c04fef,
	0x000c6000,
	0xfb8e6000,
	0xfcbe3029,
	0x00021080,
	0x30290002,
	0x48c0e080,
	0x11400001,
	0x54883029,
	0x00041080,
	0x30290004,
	0x48c0e080,
	0x11400001,
	0x54882029,
	0x000c1080,
	0x2029000c,
	0xe0881140,
	0x00012029,
	0x000c7210,
	0xe2a81140,
	0x00022029,
	0x000c7218,
	0xe2a81140,
	0x00035888,
	0x6000fb34,
	0x30290002,
	0x10803029,
	0x000248c0,
	0xe0801140,
	0x00015488,
	0x4878001c,
	0x4869000a,
	0x2f082f48,
	0x000c6100,
	0x09b8226f,
	0x0014206f,
	0x000c701c,
	0xd1c02029,
	0x00061080,
	0x20290006,
	0xe0801140,
	0x00012029,
	0x00067210,
	0xe2a01140,
	0x00022029,
	0x00067218,
	0xe2a01140,
	0x00035888,
	0x10e90026,
	0x4fef000c,
	0x6000facc,
	0x6000ff40,
	0x30290002,
	0x10803029,
	0x000248c0,
	0xe0801140,
	0x00015488,
	0x10e90026,
	0x6000faac,
	0x60de6000,
	0xfdf26000,
	0xfe243029,
	0x00021080,
	0x30290002,
	0x48c0e080,
	0x11400001,
	0x54883029,
	0x00081080,
	0x30290008,
	0x48c0e080,
	0x11400001,
	0x54886000,
	0xfa763029,
	0x00021080,
	0x30290002,
	0x48c0e080,
	0x11400001,
	0x54884878,
	0x001c4869,
	0x00062f08,
	0x2f48000c,
	0x610008fa,
	0x701cd1af,
	0x000c4878,
	0x001c4877,
	0x01620018,
	0x00222f2f,
	0x00146100,
	0x08e0206f,
	0x0018701c,
	0xd1c04fef,
	0x00186000,
	0xfa261011,
	0x49c02f00,
	0x486e837a,
	0x6100ee02,
	0x42804fef,
	0x000c4e75,
	0x30290004,
	0x10803029,
	0x000448c0,
	0xe0801140,
	0x00015488,
	0x6000f9f8,
	0x10e9000a,
	0x6000f9f0,
	0x6000f9ec,
	0x60fa598f,
	0x206f000c,
	0x4878001c,
	0x2f2f000c,
	0x2f082f48,
	0x000c6100,
	0x087c226f,
	0x0014206f,
	0x000c701c,
	0xd1c02029,
	0x001c1080,
	0x2029001c,
	0xe0801140,
	0x00012029,
	0x001c7210,
	0xe2a01140,
	0x00022029,
	0x001c7218,
	0xe2a01140,
	0x00035888,
	0x58882029,
	0x00241080,
	0x20290024,
	0xe0801140,
	0x00012029,
	0x00247210,
	0xe2a01140,
	0x00022029,
	0x00247218,
	0xe2a01140,
	0x00035888,
	0x20290028,
	0x10802029,
	0x0028e080,
	0x11400001,
	0x20290028,
	0x7210e2a0,
	0x11400002,
	0x20290028,
	0x7218e2a0,
	0x11400003,
	0x58882029,
	0x002c1080,
	0x2029002c,
	0xe0801140,
	0x00012029,
	0x002c7210,
	0xe2a01140,
	0x00022029,
	0x002c7218,
	0xe2a01140,
	0x00035888,
	0x20290034,
	0x10802029,
	0x0034e080,
	0x11400001,
	0x20290034,
	0x7210e2a0,
	0x11400002,
	0x20290034,
	0x7218e2a0,
	0x11400003,
	0x42280004,
	0x42280005,
	0x42280006,
	0x42280007,
	0x50883029,
	0x00381080,
	0x30290038,
	0x48c0e080,
	0x11400001,
	0x54883029,
	0x003a1080,
	0x3029003a,
	0x48c0e080,
	0x11400001,
	0x54883029,
	0x00201080,
	0x30290020,
	0x48c0e080,
	0x11400001,
	0x54883029,
	0x00221080,
	0x30290022,
	0x48c0e080,
	0x11400001,
	0x54882008,
	0x90af0018,
	0x4fef0010,
	0x4e75598f,
	0x226f000c,
	0x206f0008,
	0x12981011,
	0x49c04201,
	0xb0016d00,
	0x057e7227,
	0xb0016e00,
	0x05763036,
	0x0b208416,
	0x4efb0000,
	0x202f0008,
	0xd0af0010,
	0xb0886608,
	0x202f0010,
	0x588f4e75,
	0x4280588f,
	0x4e754280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x33400002,
	0x54884280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x33400004,
	0x54884280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x2340000a,
	0x548860a8,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00025488,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80814281,
	0x12280002,
	0x7410e5a1,
	0x80814281,
	0x12280003,
	0x7418e5a1,
	0x80812340,
	0x00065088,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80812340,
	0x000a5488,
	0x52882348,
	0x000e2029,
	0x000ad1c0,
	0x6000ff46,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00025488,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00045488,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80812340,
	0x000a5488,
	0x52882348,
	0x000e2029,
	0x000ad1c0,
	0x6000fefa,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00025488,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80814281,
	0x12280002,
	0x7410e5a1,
	0x80814281,
	0x12280003,
	0x7418e5a1,
	0x80812340,
	0x00065088,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80812340,
	0x000a5488,
	0x6000fea2,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00025488,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00045488,
	0x4878001c,
	0x2f082f48,
	0x00084869,
	0x00066100,
	0x056c206f,
	0x000c701c,
	0xd1c04fef,
	0x000c6000,
	0xfe584280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x33400002,
	0x54884280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x33400006,
	0x54886000,
	0xfe2c4280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x33400002,
	0x54884280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x33400004,
	0x54884878,
	0x00402f08,
	0x2f480008,
	0x48690006,
	0x610004f6,
	0x206f000c,
	0x7040d1c0,
	0x4fef000c,
	0x6000fde2,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00025488,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00045488,
	0x6000fdb6,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00025488,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00045488,
	0x6000fd8a,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00025488,
	0x48780040,
	0x2f082f48,
	0x00084869,
	0x00066100,
	0x0468206f,
	0x000c7040,
	0xd1c04fef,
	0x000c6000,
	0xfd544280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x33400002,
	0x54884280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x33400004,
	0x54884878,
	0x00402f08,
	0x2f480008,
	0x48690006,
	0x6100041e,
	0x206f000c,
	0x7040d1c0,
	0x4fef000c,
	0x6000fd0a,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00025488,
	0x6000fcf2,
	0x6000ff3a,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00025488,
	0x4878001c,
	0x2f082f48,
	0x00084869,
	0x000a6100,
	0x03cc206f,
	0x000c701c,
	0xd1c04fef,
	0x000c6000,
	0xfcb860c4,
	0x60aa6000,
	0xfcca4280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x33400002,
	0x54884280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x42811228,
	0x00027410,
	0xe5a18081,
	0x42811228,
	0x00037418,
	0xe5a18081,
	0x23400006,
	0x50884280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x2340000a,
	0x54882348,
	0x000e2029,
	0x000ad1c0,
	0x6000fc4e,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00025488,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00045488,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80812340,
	0x000a5488,
	0x2348000e,
	0x2029000a,
	0xd1c06000,
	0xfc046000,
	0xfd084280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x33400002,
	0x54884280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x33400004,
	0x54884280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x42811228,
	0x00027410,
	0xe5a18081,
	0x42811228,
	0x00037418,
	0xe5a18081,
	0x2340000c,
	0x58886000,
	0xfba84280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x33400002,
	0x54884878,
	0x001c2f08,
	0x2f480008,
	0x4869000a,
	0x61000286,
	0x226f0018,
	0x206f000c,
	0x701cd1c0,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80814281,
	0x12280002,
	0x7410e5a1,
	0x80814281,
	0x12280003,
	0x7418e5a1,
	0x80812340,
	0x00065888,
	0x13580026,
	0x4fef000c,
	0x6000fb3e,
	0x6000ff3c,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00025488,
	0x13580026,
	0x6000fb1e,
	0x60de6000,
	0xfe2c6000,
	0xfe5e4280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x33400002,
	0x54884280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x33400008,
	0x54886000,
	0xfae84280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x33400002,
	0x54884878,
	0x001c2f08,
	0x2f480008,
	0x48690006,
	0x610001c6,
	0x701cd1af,
	0x000c4878,
	0x001c2f2f,
	0x00104877,
	0x01620020,
	0x00226100,
	0x01ac206f,
	0x0018701c,
	0xd1c04fef,
	0x00186000,
	0xfa981011,
	0x49c02f00,
	0x486e8388,
	0x6100e6ce,
	0x42804fef,
	0x000c4e75,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00045488,
	0x6000fa6a,
	0x1358000a,
	0x6000fa62,
	0x6000fa5e,
	0x60fa598f,
	0x206f0008,
	0x4878001c,
	0x2f082f48,
	0x00082f2f,
	0x00146100,
	0x0148226f,
	0x0018206f,
	0x000c701c,
	0xd1c04280,
	0x10104281,
	0x12280001,
	0xe1818081,
	0x42811228,
	0x00027410,
	0xe5a18081,
	0x42811228,
	0x00037418,
	0xe5a18081,
	0x2340001c,
	0x58885888,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80814281,
	0x12280002,
	0x7410e5a1,
	0x80814281,
	0x12280003,
	0x7418e5a1,
	0x80812340,
	0x00245888,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80814281,
	0x12280002,
	0x7410e5a1,
	0x80814281,
	0x12280003,
	0x7418e5a1,
	0x80812340,
	0x00285888,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80814281,
	0x12280002,
	0x7410e5a1,
	0x80814281,
	0x12280003,
	0x7418e5a1,
	0x80812340,
	0x002c5888,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80814281,
	0x12280002,
	0x7410e5a1,
	0x80814281,
	0x12280003,
	0x7418e5a1,
	0x80812340,
	0x00345088,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00385488,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x003a5488,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00205488,
	0x42801010,
	0x42811228,
	0x0001e181,
	0x80813340,
	0x00225488,
	0x200890af,
	0x00144fef,
	0x00104e75,
	0x202f000c,
	0x6720246f,
	0x0004226f,
	0x00082209,
	0x828082af,
	0x0004c27c,
	0x0003671e,
	0xb5c9620c,
	0x14d95380,
	0x66fa202f,
	0x00044e75,
	0x70154e40,
	0x4e757008,
	0xd3c0d5c0,
	0x15215380,
	0x66fa200a,
	0x4e75b5c9,
	0x620c24d9,
	0x598066fa,
	0x202f0004,
	0x4e75d3c0,
	0xd5c02521,
	0x598066fa,
	0x200a4e75,
	0x70084e40,
	0x4e757004,
	0x4e404e75,
	0x70164e40,
	0x4e757006,
	0x4e404e75,
	0x70064e40,
	0x4e75700e,
	0x70074e40,
	0x4e75700d,
	0x4e404e75,
	0x700f4e40,
	0x4e757014,
	0x700e4e40,
	0x4e75700f,
	0x4e404e75,
	0x70144e40,
	0x4e750000,
	0x00000000,
	0x00000000,
	0x00000000,
	0x00000000,
	0x00000000,
	0x0000000d,
	0x000026f6,
	0x0000270c,
	0x00002732,
	0x00002750,
	0x00002754,
	0x00002764,
	0x00002782,
	0x0000279c,
	0x000027a0,
	0x000027be,
	0x00002bcc,
	0x00002bf2,
	0x00002c18,
	0x000d0000,
	0x2a640000,
	0x2a7a0000,
	0x2aa00000,
	0x2abe0000,
	0x2ac20000,
	0x2ad20000,
	0x2af00000,
	0x2b0a0000,
	0x2b0e0000,
	0x2b2c0000,
	0x2f3a0000,
	0x2f600000,
	0x2f860000,
	0x00000000,
	0x00000000,
	0x00000000,


@@ 922,8 2130,8 @@ ulong bootcode[]={
	0x00000000,
	0x00000000,
	0x00000000,
	0x00000009,
	0x00000000,
	0x00090000,
	0x00000000,
	0x00000000,
	0x00000000,


@@ 938,57 2146,227 @@ ulong bootcode[]={
	0x00000000,
	0x00000000,
	0x00000000,
	0x00000001,
	0x020a0b0c,
	0x03000000,
	0x04000005,
	0x00000006,
	0x00070000,
	0x08000000,
	0x0001020a,
	0x0b0c0300,
	0x00000400,
	0x00050000,
	0x00060007,
	0x00000800,
	0x00000000,
	0x23632f63,
	0x6f6e7300,
	0x23632f63,
	0x6f6e7300,
	0x23632f63,
	0x6f6e7300,
	0x75736572,
	0x3a200063,
	0x616e2774,
	0x20726561,
	0x64202363,
	0x00002363,
	0x2f636f6e,
	0x733b2070,
	0x6c656173,
	0x65207265,
	0x626f6f74,
	0x0068656c,
	0x6c6f2025,
	0x73210a00,
	0x70697065,
	0x00686f68,
	0x6f484f21,
	0x73002363,
	0x2f636f6e,
	0x73002363,
	0x2f636f6e,
	0x73002363,
	0x2f757365,
	0x72002363,
	0x2f757365,
	0x72006361,
	0x6e277420,
	0x72656164,
	0x2023632f,
	0x75736572,
	0x3b20706c,
	0x65617365,
	0x20726562,
	0x6f6f7400,
	0x68656c6c,
	0x6f202573,
	0x210a0023,
	0x692f6374,
	0x6c006f70,
	0x656e696e,
	0x67202369,
	0x2f63746c,
	0x00707573,
	0x6820646b,
	0x6d757800,
	0x636f6e66,
	0x69672031,
	0x20313600,
	0x646b6d75,
	0x7820636f,
	0x6e666967,
	0x75726564,
	0x0a00236b,
	0x2f322f64,
	0x61746100,
	0x6f70656e,
	0x696e6720,
	0x236b2f32,
	0x2f646174,
	0x6100236b,
	0x2f322f63,
	0x746c006f,
	0x70656e69,
	0x6e672023,
	0x6b2f322f,
	0x63746c00,
	0x236b2f32,
	0x2f63746c,
	0x206f7065,
	0x6e0a0063,
	0x6f6e6e65,
	0x63742072,
	0x37302e6e,
	0x6f6e6574,
	0x21626f6f,
	0x74657321,
	0x66730063,
	0x6f6e6e65,
	0x63746564,
	0x20746f20,
	0x7237302e,
	0x6e6f6e65,
	0x7421626f,
	0x6f746573,
	0x2166730a,
	0x006e6f70,
	0x2e2e2e00,
	0x77726974,
	0x65206e6f,
	0x70007265,
	0x6164206e,
	0x6f70006e,
	0x203d2025,
	0x643b2062,
	0x7566203d,
	0x20252e32,
	0x7820252e,
	0x32782025,
	0x2e327820,
	0x252e3278,
	0x0a00666f,
	0x726d6174,
	0x206e6f70,
	0x006e6f74,
	0x20526e6f,
	0x70007365,
	0x7373696f,
	0x6e2e2e2e,
	0x00777269,
	0x74650072,
	0x65616400,
	0x25730a00,
	0x74652073,
	0x65737369,
	0x6f6e0072,
	0x65616420,
	0x73657373,
	0x696f6e00,
	0x666f726d,
	0x61742073,
	0x65737369,
	0x6f6e006e,
	0x6f742052,
	0x73657373,
	0x696f6e00,
	0x6572726f,
	0x72202564,
	0x3b007265,
	0x6d6f7465,
	0x20657272,
	0x6f720070,
	0x6f73742e,
	0x2e2e0023,
	0x732f2573,
	0x00626f6f,
	0x74657300,
	0x63726561,
	0x74650025,
	0x64007772,
	0x69746500,
	0x23732f25,
	0x7300626f,
	0x6f746573,
	0x0023732f,
	0x626f6f74,
	0x3a202573,
	0x3a202573,
	0x00637265,
	0x61746500,
	0x25640077,
	0x72697465,
	0x006d6f75,
	0x6e742e2e,
	0x2e002f00,
	0x2f006269,
	0x6e64002f,
	0x00006d6f,
	0x756e7400,
	0x73756363,
	0x6573730a,
	0x002f3638,
	0x3032302f,
	0x696e6974,
	0x00696e69,
	0x74002f36,
	0x38303230,
	0x2f696e69,
	0x7400626f,
	0x6f743a20,
	0x25733a20,
	0x25730a00,
	0x00000000,
	0x00002a2a,
	0x2a000000,
	0x00006261,
	0x64207479,
	0x70653a20,
	0x25640a00,
	0x62616420,
	0x74797065,
	0x3a202564,
	0x0a000000,
	0x2a2a2a00,
	0x00000000,
	0x01280116,
	0x00e0010a,
	0x00fe00ec,
	0x00e000e0,
	0x00840000,
	0x6e6f6e2d,
	0x7a65726f,
	0x20657869,
	0x74207374,
	0x61747573,
	0x00000000,
	0x0000f78,
	0x00000128,
	0x011600e0,
	0x010a00fe,
	0x00ec00e0,
	0x00e00084,
	0x00006e6f,
	0x6e2d7a65,
	0x726f2065,
	0x78697420,
	0x73746174,
	0x75730000,
	0x00000618,
	0x0614060c,
	0x05f405de,
	0x05de05de,
	0x05de05de,
	0x05de058e,
	0x05580562,
	0x055e055a,
	0x0558053c,
	0x053804d0,
	0x047a0476,
	0x0414038c,
	0x03880386,
	0x0384034e,
	0x034a0332,
	0x02e802b2,
	0x0286025a,
	0x021001e4,
	0x019a0136,
	0x00d20048,
	0x000c05a6,
	0x05a2059a,
	0x0582056c,
	0x056c056c,
	0x056c056c,
	0x056c051c,
	0x04e604f0,
	0x04ec04e8,
	0x04e604ca,
	0x04c6045c,
	0x04040400,
	0x03b60354,
	0x0350034e,
	0x034c0316,
	0x031202fa,
	0x02b0027a,
	0x024e0222,
	0x01d801ac,
	0x0162010a,
	0x00be005a,
	0x001c0000,
	0x00024fe,
	0x,
};

M gnot/clock.c => gnot/clock.c +3 -1
@@ 98,8 98,10 @@ clock(Ureg *ur)
	SYNCREG[1] = 0x5F;	/* clear interrupt */
	m->ticks++;
	p = m->proc;
	if(p)
	if(p){
		p->pc = ur->pc;
		p->time[p->insyscall]++;
	}
	if(canlock(&m->alarmlock)){
		if(m->alarm){
			a = m->alarm;

M gnot/dat.h => gnot/dat.h +2 -2
@@ 281,7 281,6 @@ struct Seg
struct Proc
{
	Label	sched;
	Lock;
	Mach	*mach;			/* machine running this proc */
	char	text[NAMELEN];
	Proc	*rnext;			/* next process in run queue */


@@ 422,7 421,8 @@ struct Stream {
#define STREAMTYPE(x)	((x)&0x1f)
#define STREAMID(x)	(((x)&~CHDIR)>>5)
#define STREAMQID(i,t)	(((i)<<5)|(t))
#define PUTNEXT(q,b)	(*(q)->next->put)((q)->next, bp)
#define PUTNEXT(q,b)	(*(q)->next->put)((q)->next, b)
#define BLEN(b)		((b)->wptr - (b)->rptr)

/*
 *  stream file qid's & high water mark

M gnot/devcons.c => gnot/devcons.c +9 -3
@@ 248,7 248,8 @@ readnum(ulong off, char *buf, ulong n, ulong val, int size)

	numbconv(&op, 10);
	tmp[size-1] = ' ';
	off %= size;
	if(off >= size)
		return 0;
	if(off+n > size)
		n = size-off;
	memcpy(buf, tmp+off, n);


@@ 261,7 262,8 @@ readstr(ulong off, char *buf, ulong n, char *str)
	int size;

	size = strlen(str);
	off %= size;
	if(off >= size)
		return 0;
	if(off+n > size)
		n = size-off;
	memcpy(buf, str+off, n);


@@ 380,7 382,9 @@ consread(Chan *c, void *buf, long n)
		return i;

	case Qcputime:
		k = c->offset % sizeof tmp;
		k = c->offset;
		if(k >= sizeof tmp)
			return 0;
		if(k+n > sizeof tmp)
			n = sizeof tmp - k;
		/* easiest to format in a separate buffer and copy out */


@@ 513,8 517,10 @@ typedef struct Incon{
	unsigned char	send;		unsigned char u7;
}Incon;

/*
inconintr(Ureg *ur)
{
	int x;
	x = ((Incon*)0x40700000)->status;
}
*/

A gnot/devdk.c => gnot/devdk.c +1462 -0
@@ 0,0 1,1462 @@
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"errno.h"

#define NOW (MACHP(0)->ticks)
#define DPRINT if(0)

enum {
	/*
	 *  configuration parameters
	 */
	Ndk = 2,		/* max dks */

	/*
	 *  relative or immutable
	 */
	Nline = 256,		/* max lines per dk */
	Ndir = Nline + 1,	/* entries in the dk directory */
	Nsubdir = 5,		/* entries in the sub directory */
};

typedef struct Dkmsg	Dkmsg;
typedef struct Line	Line;
typedef struct Dk	Dk;

/*
 *  types of possible dkcalls
 */
enum {
	Dial,
	Announce,
	Redial
};

/*
 *  format of messages to/from the datakit controller on the common
 *  signalling line
 */
struct Dkmsg {
	uchar	type;
	uchar	srv;
	uchar	param0l;
	uchar	param0h;
	uchar	param1l;
	uchar	param1h;
	uchar	param2l;
	uchar	param2h;
	uchar	param3l;
	uchar	param3h;
	uchar	param4l;
	uchar	param4h;
};

/*
 *  message codes (T_xxx == dialin.type, D_xxx == dialin.srv)
 */
#define	T_SRV	1		/* service request */
#define   D_SERV	1		/* (host to dkmux) announce a service */
#define   D_DIAL	2		/* (host to dkmux) connect to a service */
#define   D_XINIT	7		/* (dkmux to host) line has been spliced */
#define	T_REPLY	2		/* reply to T_SRV/D_SERV or T_SRV/D_DIAL */
#define	  D_OK		1		/* not used */
#define	  D_OPEN	2		/* (dkmux to host) connection established */
#define	  D_FAIL	3		/* (dkmux to host) connection failed */
#define	T_CHG	3		/* linege the status of a connection */
#define	  D_CLOSE	1		/* close the connection */
#define	  D_ISCLOSED	2		/* (dkmux to host) confirm a close */
#define	  D_CLOSEALL	3		/* (dkmux to host) close all connections */
#define	  D_REDIAL	6		/* (host to dkmux) redial a call */
#define	T_ALIVE	4		/* (host to dkmux) keep alive message */
#define	  D_CONTINUE	0		/* host has not died since last msg */
#define	  D_RESTART	1		/* host has restarted */
#define   D_MAXCHAN	2		/* request maximum line number */
#define	T_RESTART 8		/* (dkmux to host) datakit restarted */

/*
 *  macros for cracking/forming the window negotiation parameter
 */
#define MIN(x,y)  (x < y ? x : y)
#define W_WINDOW(o,d,t)  ((o<<8) | (d<<4) | t | 0100000)
#define W_VALID(x)  ((x) & 0100000)
#define W_ORIG(x)  (((x)>>8) & 017)
#define W_DEST(x)  (((x)>>4) & 017)
#define W_TRAF(x)  ((x) & 017)
#define W_DESTMAX(x,y)  (W_WINDOW(W_ORIG(x),MIN(W_DEST(x),y),W_TRAF(x)))
#define W_LIMIT(x,y)  (W_WINDOW(MIN(W_ORIG(x),y),MIN(W_DEST(x),y),W_TRAF(x)))
#define	W_VALUE(x)	(1<<((x)+4))
#define WS_2K	7

/*
 *  one per datakit line
 */
struct Line {
	QLock;
	Rendez	r;		/* wait here for dial */
	int	state;		/* dial state */
	int	err;		/* dialing error (if non zero) */
	int	window;		/* negotiated window */
	int	timestamp;	/* timestamp of last call received on this line */
	int	calltolive;	/* multiple of 15 seconds for dialing state to last */
	Queue	*rq;
	char	addr[64];
	char	raddr[64];
	char	ruser[32];
	char	other[64];
	Dk *dp;			/* interface contianing this line */
};

/*
 *  a dkmux dk.  one exists for every stream that a 
 *  dkmux line discipline is pushed onto.
 */
struct Dk {
	QLock;
	int	ref;
	char	name[64];	/* dk name */	
	Queue	*wq;		/* dk output queue */
	int	lines;		/* number of lines */
	int	ncsc;		/* csc line number */
	Chan	*csc;		/* common signalling line */
	Line	line[Nline];
};
static Dk dk[Ndk];

/*
 *  conversation states (for Line.state)
 */
typedef enum {
	Lclosed=0,
	Lopened,		/* opened but no call out */
	Lconnected,		/* opened and a call set up on htis line */
	Lrclose,		/* remote end has closed down */
	Llclose,		/* local end has closed down */
	Ldialing,		/* dialing a new call */
	Llistening,		/* this line listening for calls */
	Lackwait,		/* incoming call waiting for ack/nak */
	Laccepting,		/* waiting for user to accept or reject the call */
} Lstate;

/*
 *  datakit error to errno 
 */
enum {
	DKok,
	DKbusy,
	DKnetotl,
	DKdestotl,
	DKbadnet,
	DKnetbusy,
	DKinuse,
	DKreject,
};
int dkerr[]={
	[DKok]Egreg,
	[DKbusy]Einuse,		/* destination busy */
	[DKnetotl]Enetotl,	/* network not answering */
	[DKdestotl]Edestotl,	/* destination not answering */ 
	[DKbadnet]Ebadnet,	/* unassigned destination */
	[DKnetbusy]Enetbusy,	/* network overload */
	[DKinuse]Einuse,	/* server already exists */
	[DKreject]Erejected	/* call rejected by destination */
};
#define DKERRS sizeof(dkerr)/sizeof(int)

/*
 *  imported
 */
extern Qinfo urpinfo;

/*
 *  predeclared
 */
Chan*		dkattach(char*);
static void	dkmuxconfig(Dk*, Block*);
static int	dkmesg(Dk*, int, int, int, int);
static void	dkcsckproc(void*);
static int	dklisten(Chan*);
static void	dkanswer(Chan*, int, int);
static void	dkwindow(Chan*);
static void	dkcall(int, Chan*, char*, char*, char*);
static void	dktimer(void*);
static void	dkchgmesg(Dk*, Dkmsg*, int);
static void	dkreplymesg(Dk*, Dkmsg*, int);
Chan*		dkopen(Chan*, int);

/*
 *  the datakit multiplexor stream module definition
 */
static void dkmuxopen(Queue *, Stream *);
static void dkmuxclose(Queue *);
static void dkmuxoput(Queue *, Block *);
static void dkmuxiput(Queue *, Block *);
Qinfo dkmuxinfo = { dkmuxiput, dkmuxoput, dkmuxopen, dkmuxclose, "dkmux" };

/*
 *  a new dkmux.  find a free dk structure and assign it to this queue.
 */
static void
dkmuxopen(Queue *q, Stream *s)
{
	Dk *dp;
	int i;

	for(dp = dk; dp < &dk[Ndk]; dp++){
		if(dp->wq == 0){
			qlock(dp);
			if(dp->wq) {
				/* someone was faster than us */
				qunlock(dp);
				continue;
			}
			q->ptr = q->other->ptr = (void *)dp;
			dp->csc = 0;
			dp->ncsc = 4;
			dp->lines = 16;
			dp->name[0] = 0;
			dp->wq = WR(q);
			qunlock(dp);
			return;
		}
	}
	error(0, Enoifc);
}

/*
 *  close down a dkmux
 */
static void
dkmuxclose(Queue *q)
{
	Dk *dp;

	dp = (Dk *)q->ptr;
	qlock(dp);
	if(dp->csc)
		close(dp->csc);
	dp->wq = 0;
	qunlock(dp);
}

/*
 *  handle configuration
 */
static void
dkmuxoput(Queue *q, Block *bp)
{
	Dk *dp;

	dp = (Dk *)q->ptr;
	if(bp->type != M_DATA){
		if(streamparse("config", bp))
			dkmuxconfig(dp, bp);
		else
			PUTNEXT(q, bp);
		return;
	}
	PUTNEXT(q, bp);
}

/*
 *  gather a message and send it up the appropriate stream
 *
 *  The first two bytes of each message contains the channel
 *  number, low order byte first.
 *
 *  Simplifying assumption:  one put == one message && the channel number
 *	is in the first block.  If this isn't true, demultiplexing will not
  *	work.
 */
static void
dkmuxiput(Queue *q, Block *bp)
{
	Dk *dp;
	Line *lp;
	int line;

	dp = (Dk *)q->ptr;
	if(bp->type != M_DATA){
		PUTNEXT(q, bp);
		return;
	}

	line = bp->rptr[0] | (bp->rptr[1]<<8);
	bp->rptr += 2;
	if(line<0 || line>=dp->lines){
		print("dkmuxiput bad line %d\n", line);
		freeb(bp);
		return;
	}

	lp = &dp->line[line];
	if(canqlock(lp)){
		if(lp->rq)
			PUTNEXT(lp->rq, bp);
		else{
			print("dkmuxiput unopened line %d\n", line);
			freeb(bp);
		}
		qunlock(lp);
	} else {
		print("dkmuxiput unopened line %d\n", line);
		freeb(bp);
	}
}

/*
 *  the datakit line stream module definition
 */
static void dkstopen(Queue *, Stream *);
static void dkstclose(Queue *);
static void dkoput(Queue *, Block *);
static void dkiput(Queue *, Block *);
Qinfo dkinfo = { dkiput, dkoput, dkstopen, dkstclose, "dk" };

/*
 *  open and save a pointer to the conversation
 */
static void
dkstopen(Queue *q, Stream *s)
{
	Dk *dp;
	Line *lp;

	dp = &dk[s->dev];
	q->other->ptr = q->ptr = lp = &dp->line[s->id];
	lp->dp = dp;
	lp->rq = q;
}

/*
 *  close down a datakit conversation
 */
static void
dkstclose(Queue *q)
{
	Dk *dp;
	Line *lp;

	lp = (Line *)q->ptr;
	dp = lp->dp;

	/*
	 *  shake hands with dk
	 */
	switch(lp->state){
	case Lclosed:
	case Llclose:
		break;

	case Lrclose:
		dkmesg(dp, T_CHG, D_CLOSE, lp - dp->line, 0);
		lp->state = Lclosed;
		break;

	case Lackwait:
		dkmesg(dp, T_CHG, D_CLOSE, lp - dp->line, 0);
		lp->state = Llclose;
		break;

	case Llistening:
		dkmesg(dp, T_CHG, D_CLOSE, lp - dp->line, 0);
		lp->state = Llclose;
		break;

	case Lconnected:
		dkmesg(dp, T_CHG, D_CLOSE, lp - dp->line, 0);
		lp->state = Llclose;
		break;

	case Lopened:
		lp->state = Lclosed;
	}

	qlock(lp);
	lp->rq = 0;
	qunlock(lp);
}

/*
 *  this is only called by hangup
 */
static void
dkiput(Queue *q, Block *bp)
{
	PUTNEXT(q, bp);
}

/*
 *  we assume that each put is a message.
 *
 *  add a 2 byte channel number to the start of each message
 */
static void
dkoput(Queue *q, Block *bp)
{
	Line *lp;
	Dk *dp;
	int line;

	if(bp->type != M_DATA){
		freeb(bp);
		error(0, Ebadarg);
	}

	lp = (Line *)q->ptr;
	dp = lp->dp;
	line = lp - dp->line;

	if(bp->base && bp->rptr - bp->base >= 2)
		bp->rptr -= 2;
	bp->rptr[0] = line;
	bp->rptr[1] = line>>8;

	PUTNEXT(dp->wq, bp);
}

/*
 *  configure a datakit multiplexor.  this takes 3 arguments separated
 *  by spaces:
 *	the line number of the common signalling channel (must be > 0)
 *	the number of lines in the device (optional)
 *	the name of the dk (optional)
 *
 *  we can configure only once
 */
static void
dkmuxconfig(Dk *dp, Block *bp)
{
	Chan *c;
	char *fields[3];
	int n;
	char buf[64];
	static int dktimeron;

	if(dp->csc != 0){
		freeb(bp);
		error(0, Ebadarg);
	}

	/*
	 *  parse
	 */
	n = getfields((char *)bp->rptr, fields, 3, ' ');
	switch(n){
	case 3:
		strncpy(dp->name, fields[2], sizeof(dp->name));
	case 2:
		dp->lines = strtoul(fields[1], 0, 0);
	case 1:
		dp->ncsc = strtoul(fields[0], 0, 0);
		break;
	default:
		freeb(bp);
		error(0, Ebadarg);
	}
	freeb(bp);
	if(dp->ncsc <= 0 || dp->lines <= dp->ncsc){
		dp->lines = 16;
		error(0, Ebadarg);
	}

	/*
	 *  open a stream for the csc and push urp onto it
	 */
	c = 0;
	if(waserror()){
		if(c)
			close(c);
		nexterror();
	}
	c = dkattach(dp->name);
	c->qid = STREAMQID(dp->ncsc, Sdataqid);
	dkopen(c, ORDWR);
	dp->csc = c;

	/*
	 *  start a process to deal with it
	 */
	sprint(buf, "**csckproc%d**", dp->ncsc);
	kproc(buf, dkcsckproc, dp);
	poperror();

	/*
	 *  start a keepalive process if one doesn't exist
	 */
	if(dktimeron == 0){
		dktimeron = 1;
		kproc("**dktimer**", dktimer, 0);
	}
}

/*
 *  qid's
 */
enum {
	/*
	 *  per line
	 */
	Daddrqid,
	Dlistenqid,
	Draddrqid,
	Duserqid,
	Dotherqid,
	Dlineqid,

	/*
	 *  per device
	 */
	Dcloneqid,
};

/*
 *  the dk directory
 */
Dirtab dkdir[Ndir];

/*
 *  the per stream directory structure
 */
Dirtab dksubdir[]={
	"addr",		Daddrqid,	0,	0600,
	"listen",	Dlistenqid,	0,	0600,
	"other",	Dotherqid,	0,	0600,
	"raddr",	Draddrqid,	0, 	0600,
	"ruser",	Duserqid,	0, 	0600,
};

/*
 *  dk file system.  most of the calls use dev.c to access the dk
 *  directory and stream.c to access the dk devices.
 */
void
dkreset(void)
{
}

/*
 *  create the dk directory.  the files are `clone' and stream
 *  directories '1' to '32' (or whatever Nline is in decimal)
 */
void
dkinit(void)
{
	int i;

	/*
	 *  create the directory.
	 */
	/*
	 *  the circuits
	 */
	for(i = 1; i < Nline; i++) {
		sprint(dkdir[i].name, "%d", i);
		dkdir[i].qid = CHDIR|STREAMQID(i, Dlineqid);
		dkdir[i].length = 0;
		dkdir[i].perm = 0600;
	}

	/*
	 *  the clone device
	 */
	strcpy(dkdir[0].name, "clone");
	dkdir[0].qid = Dcloneqid;
	dkdir[0].length = 0;
	dkdir[0].perm = 0600;
}

Chan*
dkattach(char *spec)
{
	Chan *c;
	Dk *dp;
print("attach\n");

	/*
	 *  find a multiplexor with the same name
	 */
	for(dp = dk; dp < &dk[Ndk]; dp++){
		qlock(dp);
print("name %s %lux\n", dp->name, dp->wq);
		if(dp->wq && strcmp(spec, dp->name)==0) {
			dp->ref++;
			qunlock(dp);
			break;
		}
		qunlock(dp);
	}
	if(dp == &dk[Ndk])
		error(0, Enoifc);
	c = devattach('k', spec);
	c->dev = dp - dk;
print("attach done\n");
	return c;
}

Chan*
dkclone(Chan *c, Chan *nc)
{
	Dk *dp;

	dp = &dk[c->dev];
	qlock(dp);
	dp->ref++;
	qunlock(dp);
	return devclone(c, nc);
}

int	 
dkwalk(Chan *c, char *name)
{
	if(c->qid == CHDIR)
		return devwalk(c, name, dkdir, dk[c->dev].lines, devgen);
	else
		return devwalk(c, name, dksubdir, Nsubdir, streamgen);
}

void	 
dkstat(Chan *c, char *dp)
{
	if(c->qid == CHDIR)
		devstat(c, dp, dkdir, dk[c->dev].lines, devgen);
	else
		devstat(c, dp, dksubdir, Nsubdir, streamgen);
}

/*
 *  opening a dk device allocates a Line.  Opening the `clone'
 *  device is a ``macro'' for finding a free Line and opening
 *  it's ctl file.
 *
 *  opening the `listen' sub device is a macro for listening for
 *  a new call.  Lile `clone' the ctl file of the new channel is
 *  returned.
 */
Chan*
dkopen(Chan *c, int omode)
{
	extern Qinfo dkinfo;
	Stream *s;
	Line *lp, *end;
	Dk *dp;
	int line;

	if(c->qid == Dcloneqid){
		/*
		 *  get an unused device and open it's control file
		 */
		dp = &dk[c->dev];
		end = &dp->line[dp->lines];
		for(lp = &dp->line[dp->ncsc+1]; lp < end; lp++){
			if(lp->state == Lclosed && canqlock(lp)){
				if(lp->state != Lclosed){
					qunlock(lp);
					continue;
				}
				c->qid = STREAMQID(lp-dp->line, Sctlqid);
				qunlock(lp);
				break;
			}
		}
		if(lp == end)
			error(0, Enodev);
		streamopen(c, &dkinfo);
		pushq(c->stream, &urpinfo);
	} else if(STREAMTYPE(c->qid) == Dlistenqid){
		/*
		 *  listen for a call and open the control file for the
		 *  channel on which the call arrived.
		 */
		line = dklisten(c);
		c->qid = STREAMQID(line, Sctlqid);
		streamopen(c, &dkinfo);
		pushq(c->stream, &urpinfo);
		dkwindow(c);
	} else if(c->qid != CHDIR){
		/*
		 *  open whatever c points to, make sure it has an urp
		 */
		streamopen(c, &dkinfo);
		if(strcmp(c->stream->procq->next->info->name, "urp")!=0)
			pushq(c->stream, &urpinfo);
	}

	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

void	 
dkcreate(Chan *c, char *name, int omode, ulong perm)
{
	error(0, Eperm);
}

void	 
dkclose(Chan *c)
{
	Dk *dp;

	/* real closing happens in lancestclose */
	if(c->qid != CHDIR)
		streamclose(c);

	dp = &dk[c->dev];
	qlock(dp);
	dp->ref--;
	qunlock(dp);
}

long	 
dkread(Chan *c, void *a, long n)
{
	int t;
	Line *lp;

	t = STREAMTYPE(c->qid);
	if(t>=Slowqid || t==Dlineqid)
		return streamread(c, a, n);
	if(c->qid == CHDIR)
		return devdirread(c, a, n, dkdir, dk[c->dev].lines, devgen);

	lp = &dk[c->dev].line[STREAMID(c->qid)];
	switch(t){
	case Daddrqid:
		return stringread(c, a, n, lp->addr);
	case Draddrqid:
		return stringread(c, a, n, lp->raddr);
	case Duserqid:
		return stringread(c, a, n, lp->ruser);
	}
	error(0, Eperm);
}

long	 
dkwrite(Chan *c, void *a, long n)
{
	int t;
	char buf[256];
	char *field[5];
	int m;

	t = STREAMTYPE(c->qid);

	/*
	 *  get data dispatched as quickly as possible
	 */
	if(t == Sdataqid)
		return streamwrite(c, a, n, 0);

	/*
	 *  easier to do here than in dkoput
	 */
	if(t == Sctlqid){
		strncpy(buf, a, sizeof buf);
		m = getfields(buf, field, 5, ' ');
		if(strcmp(field[0], "connect")==0){
			if(m < 2)
				error(0, Ebadarg);
			dkcall(Dial, c, field[1], 0, 0);
		} else if(strcmp(field[0], "announce")==0){
			if(m < 2)
				error(0, Ebadarg);
			dkcall(Announce, c, field[1], 0, 0);
		} else if(strcmp(field[0], "redial")==0){
			if(m < 4)
				error(0, Ebadarg);
			dkcall(Redial, c, field[1], field[2], field[3]);
		} else if(strcmp(field[0], "accept")==0){
			if(m < 2)
				error(0, Ebadarg);
			dkanswer(c, strtoul(field[1], 0, 0), 0);
		} else if(strcmp(field[0], "reject")==0){
			if(m < 3)
				error(0, Ebadarg);
			dkanswer(c, strtoul(field[1], 0, 0), strtoul(field[2], 0, 0));
		} else
			return streamwrite(c, a, n, 0);
		return n;
	}

	if(t >= Slowqid)
		return streamwrite(c, a, n, 0);

	error(0, Eperm);
}

void	 
dkremove(Chan *c)
{
	error(0, Eperm);
}

void	 
dkwstat(Chan *c, char *dp)
{
	error(0, Eperm);
}

void	 
dkerrstr(Error *e, char *buf)
{
	rooterrstr(e, buf);
}

void	 
dkuserstr(Error *e, char *buf)
{
	extern consuserstr(Error *, char *);

	consuserstr(e, buf);
}

/*
 *  send a message to the datakit on the common signaling line
 */
static int
dkmesg(Dk *dp, int type, int srv, int p0, int p1)
{
	Dkmsg d;
	Block *bp;

	if(dp->csc == 0)
		return -1;
	if(waserror())
		return -1;
	d.type = type;
	d.srv = srv;
	d.param0l = p0;
	d.param0h = p0>>8;
	d.param1l = p1;
	d.param1h = p1>>8;
	d.param2l = 0;
	d.param2h = 0;
	d.param3l = 0;
	d.param3h = 0;
	d.param4l = 0;
	d.param4h = 0;
	streamwrite(dp->csc, (char *)&d, sizeof(Dkmsg), 1);
	poperror();
	return 0;
}

/*
 *  call out on a datakit
 */
static int
calldone(void *a)
{
	Line *lp;

	lp = (Line *)a;
	return lp->state != Ldialing;
}
static void
dkcall(int type, Chan *c, char *addr, char *nuser, char *machine)
{
 	char dialstr[66];
	int line;
	char dialtone;
	int t_val, d_val;
	Dk *dp;
	Line *lp;
	Chan *dc;

	line = STREAMID(c->qid);
	dp = &dk[c->dev];
	lp = &dp->line[line];

	/*
	 *  only dial on virgin lines
	 */
	if(lp->state != Lclosed)
		error(0, Ebadarg);

	DPRINT("dkcall(line=%d, type=%d, dest=%s)\n", line, type, addr);

	/*
	 *  build dial string (guard against new lines)
	 */
	if(strchr(addr, '\n'))
		error(0, Ebadarg);
	if(strlen(addr)+strlen(u->p->pgrp->user)+2 >= sizeof(dialstr))
		error(0, Ebadarg);
	strcpy(dialstr, addr);
	switch(type){
	case Dial:
		t_val = T_SRV;
		d_val = D_DIAL;
		strcat(dialstr, "\n");
		strcat(dialstr, u->p->pgrp->user);
		strcat(dialstr, "\n");
		break;
	case Announce:
		t_val = T_SRV;
		d_val = D_SERV;
		break;
	case Redial:
		t_val = T_CHG;
		d_val = D_REDIAL;
		strcat(dialstr, "\n");
		strcat(dialstr, nuser);
		strcat(dialstr, "\n");
		strcat(dialstr, machine);
		strcat(dialstr, "\n");
		break;
	}

	/*
	 *  open the data file
	 */
	dc = dkattach(dp->name);
	if(waserror()){
		close(dc);
		nexterror();
	}
	dc->qid = STREAMQID(line, Sdataqid);
	dkopen(dc, ORDWR);

	lp->calltolive = 4;
	lp->state = Ldialing;

	/*
	 *  tell the controller we want to make a call
	 */
	DPRINT("dialout\n");
	dkmesg(dp, t_val, d_val, line, W_WINDOW(WS_2K,WS_2K,2));

	/*
	 *  if redial, wait for a dial tone (otherwise we might send
	 *  the dialstr to the previous other end and not the controller)
	 */
	if(type==Redial){
		if(streamread(dc, &dialtone, 1L) != 1L){
			lp->state = Lconnected;
			error(0, Ebadarg);
		}
	}

	/*
	 *  make the call
	 */
	DPRINT("dialstr %s\n", dialstr);
	streamwrite(dc, dialstr, (long)strlen(dialstr), 1);
	close(dc);
	poperror();

	/*
	 *  redial's never get a reply, assume it worked
	 */
	if(type == Redial) {
		lp->state = Lconnected;
		return;
	}

	/*
	 *  wait for a reply
	 */
	DPRINT("reply wait\n");
	sleep(&lp->r, calldone, lp);

	/*
	 *  if there was an error, translate it to a plan 9
	 *  errno and report it to the user.
	 */
	DPRINT("got reply %d\n", lp->state);
	if(lp->state != Lconnected) {
		if(lp->err >= DKERRS)
			error(0, dkerr[0]);
		else
			error(0, dkerr[lp->err]);
	}

	/*
	 *  linege state if serving
	 */
	if(type == D_SERV){
		lp->state = Llistening;
	}
	DPRINT("connected!\n");

	/*
	 *  decode the window size
	 */
	if (W_VALID(lp->window)){
		/*
		 *  a 1127 window negotiation
		 */
		lp->window = W_VALUE(W_DEST(lp->window));
	} else if(lp->window>2 && lp->window<31){
		/*
		 *  a generic window negotiation
		 */
		lp->window = 1<<lp->window;
	} else
		lp->window = 0;

	/*
	 *  tag the connection
	 */
	strncpy(lp->addr, addr, sizeof(lp->addr)-1);
	strncpy(lp->raddr, addr, sizeof(lp->raddr)-1);

	/*
	 *  reset the protocol
	 */
	dkwindow(c);
}

/*
 *  listen for a call, reflavor the 
 */
static int
dklisten(Chan *c)
{
	char dialstr[512];
	char *line[12];
	char *field[8];
	Line *lp;
	Dk *dp;
	int n, lineno, ts, window;
	Chan *dc;

	dp = &dk[c->dev];

	/*
	 *  open the data file
	 */
	dc = dkattach(dp->name);
	if(waserror()){
		close(dc);
		nexterror();
	}
	dc->qid = STREAMQID(STREAMID(c->qid), Sdataqid);
	dkopen(dc, ORDWR);

	/*
	 *  wait for a call in
	 */
	for(;;){
		/*
		 *  read the dialstring and null terminate it
		 */
		n = streamread(dc, dialstr, sizeof(dialstr)-1);
		DPRINT("returns %d\n", n);
		if(n <= 0)
			error(0, Eio);
		dialstr[n] = 0;
		DPRINT("dialstr = %s\n", dialstr);

		/*
		 *  break the dial string into lines
		 */
		n = getfields(dialstr, line, 12, '\n');
		if (n < 2) {
			DPRINT("bad dialstr from dk (1 line)\n");
			error(0, Eio);
		}

		/*
		 * line 0 is `line.tstamp.traffic[.urpparms.window]'
		 */
		window = 0;
		switch(getfields(line[0], field, 5, '.')){
		case 5:
			/*
			 *  generic way of passing window
			 */
			window = strtoul(field[4], 0, 0);
			if(window > 0 && window <31)
				window = 1<<window;
			else
				window = 0;
			/*
			 *  intentional fall through
			 */
		case 3:
			/*
			 *  1127 way of passing window
			 */
			if(window == 0){
				window = strtoul(field[2], 0, 0);
				if(W_VALID(window))
					window = W_VALUE(W_ORIG(window));
				else
					window = 0;
			}
			break;
		default:
			print("bad message from dk(bad first line)\n");
			continue;
		}
		lineno = strtoul(field[0], 0, 0);
		if(lineno >= dp->lines){
			print("dklisten: illegal line %d\n", lineno);
			continue;
		}
		lp = &dp->line[lineno];
		ts = strtoul(field[1], 0, 0);

		/*
		 *  this could be a duplicate request
		 */
		if(ts == lp->timestamp){
			print("dklisten: repeat timestamp %d\n", lineno);
			continue;
		}
	
		/*
		 *  take care of glare (datakit picked an inuse channel
		 *  for the call to come in on).
		 */
		if(!canqlock(lp)){
			print("DKbusy1\n");
			dkanswer(c, lineno, DKbusy);
			continue;
		} else {
			if(lp->state != Lclosed){
				qunlock(lp);
				print("DKbusy2 %ux\n", lp->state);
				dkanswer(c, lineno, DKbusy);
				continue;
			}
		}
		lp->window = window;

		/*
		 *  Line 1 is `my-dk-name.service[.more-things]'.
		 *  Special characters are escaped by '\'s.
		 */
		strncpy(lp->addr, line[1], sizeof(lp->addr)-1);
	
		/*
		 *  the rest is variable length
		 */
		switch(n) {
		case 2:
			/* no more lines */
			lp->ruser[0] = 0;
			lp->raddr[0] = 0;
			break;
		case 3:
			/* line 2 is `source.user.param1.param2' */
			getfields(line[2], field, 3, '.');
			strncpy(lp->raddr, field[0], sizeof(lp->raddr)-1);
			strncpy(lp->ruser, field[1], sizeof(lp->ruser)-1);
			break;
		case 4:
			/* line 2 is `user.param1.param2' */
			getfields(line[2], field, 2, '.');
			strncpy(lp->ruser, field[0], sizeof(lp->ruser)-1);
	
			/* line 3 is `source.node.mod.line' */
			strncpy(lp->raddr, line[3], sizeof(lp->raddr)-1);
			break;
		default:
			print("bad message from dk(>4 line)\n");
			qunlock(lp);
			error(0, Ebadarg);
		}

		sprint(lp->other, "w(%d)", W_TRAF(lp->window));
		DPRINT("src(%s)user(%s)dest(%s)other(%s)\n", lp->raddr, lp->ruser,
			lp->addr, lp->other);

		lp->timestamp = ts;
		lp->state = Lconnected;
		qunlock(lp);
		close(dc);
		poperror();
		DPRINT("dklisten returns %d\n", lineno);
		return lineno;
	}
}

/*
 *  answer a call
 */
static void
dkanswer(Chan *c, int line, int code)
{
	char reply[64];
	Dk *dp;
	Chan *dc;
	Line *lp;

	dp = &dk[c->dev];
	lp = &dp->line[line];

	/*
	 *  open the data file (c is a control file)
	 */
	dc = dkattach(dp->name);
	if(waserror()){
		close(dc);
		nexterror();
	}
	dc->qid = STREAMQID(STREAMID(c->qid), Sdataqid);
	dkopen(dc, ORDWR);

	/*
	 *  send the reply
	 */
	sprint(reply, "%ud.%ud.%ud", line, lp->timestamp, code);
	DPRINT("dkanswer %s\n", reply);
	streamwrite(dc, reply, strlen(reply), 1);
	close(dc);
	poperror();
}

/*
 *  set the window size and reset the protocol
 */
static void
dkwindow(Chan *c)
{
	char buf[64];
	long wins;
	Line *lp;

	lp = &dk[c->dev].line[STREAMID(c->qid)];
	if(lp->window == 0)
		lp->window = 64;
	sprint(buf, "init %d %d", lp->window, Streamhi);
	streamwrite(c, buf, strlen(buf), 1);
}

/*
 *  hangup a datakit connection
 */
static void
dkhangup(Line *lp)
{
	Block *bp;

	qlock(lp);
	if(lp->rq){
		bp = allocb(0);
		bp->type = M_HANGUP;
		PUTNEXT(lp->rq, bp);
	}
	qunlock(lp);
}

/*
 *  A process which listens to all input on a csc line
 */
static void
dkcsckproc(void *a)
{
	long n;
	Dk *dp;
	Dkmsg d;
	int line;
	int i;

	dp = (Dk *)a;

	/*
	 *  loop forever listening
	 */
	for(;;){
		n = streamread(dp->csc, (char *)&d, (long)sizeof(d));
		if(n != sizeof(d)){
			print("strange csc message %d\n", n);
			continue;
		}
		line = (d.param0h<<8) + d.param0l;
/*		print("t(%d)s(%d)l(%d)\n", d.type, d.srv, line); /**/
		switch (d.type) {

		case T_CHG:	/* controller wants to close a line */
			dkchgmesg(dp, &d, line);
			break;
		
		case T_REPLY:	/* reply to a dial request */
			dkreplymesg(dp, &d, line);
			break;
		
		case T_SRV:	/* ignore it, it's useless */
			print("dksrvmesg(%d)\n", line);
			break;
		
		case T_RESTART:	/* datakit reboot */
			print("dk restart\n");
			if(line >=0 && line<dp->lines){
				print("maxlines=%d\n", line+1);
				dp->lines=line+1;
			}
			break;
		
		default:
			DPRINT("unrecognized csc message %o(%o)\n", d.type, line);
			break;
		}
	}
}

/*
 *  datakit requests or confirms closing a line
 */
static void
dkchgmesg(Dk *dp, Dkmsg *dialp, int line)
{
	Line *lp;

	if (line <= 0 || line >= dp->lines) {
		/* tell controller this line is not in use */
		dkmesg(dp, T_CHG, D_CLOSE, line, 0);
		return;
	}
	lp = &dp->line[line];
	switch (dialp->srv) {

	case D_CLOSE:		/* remote shutdown */
		switch (lp->state) {

		case Ldialing:
			/* simulate a failed connection */
			dkreplymesg(dp, (Dkmsg *)0, line);
			lp->state = Lrclose;
			break;

		case Lrclose:
		case Lconnected:
		case Llistening:
		case Lackwait:
			dkhangup(lp);
			lp->state = Lrclose;
			break;

		case Lopened:
			dkmesg(dp, T_CHG, D_CLOSE, line, 0);
			break;

		case Llclose:
		case Lclosed:
			dkhangup(lp);
			dkmesg(dp, T_CHG, D_CLOSE, line, 0);
			lp->state = Lclosed;
			break;
		}
		break;
	
	case D_ISCLOSED:	/* acknowledging a local shutdown */
		switch (lp->state) {
		case Llclose:
		case Lclosed:
			lp->state = Lclosed;
			break;

		case Lrclose:
		case Lconnected:
		case Llistening:
		case Lackwait:
			break;
		}
		break;

	default:
		print("unrecognized T_CHG\n");
	}
}

/*
 *  datakit replies to a dialout.  capture reply code and traffic parameters
 */
static void
dkreplymesg(Dk *dp, Dkmsg *dialp, int line)
{
	Proc *p;
	Line *lp;

	DPRINT("dkreplymesg(%d)\n", line);

	if(line < 0 || line >= dp->lines)
		return;

	lp=&dp->line[line];
	if(lp->state != Ldialing)
		return;

	if(dialp){
		/*
		 *  a reply from the dk
		 */
		lp->state = (dialp->srv==D_OPEN) ? Lconnected : Lrclose;
		lp->err = (dialp->param1h<<8) + dialp->param1l;
		lp->window = lp->err;
		DPRINT("dkreplymesg: %d\n", lp->state);
	} else {
		/*
		 *  a local abort
		 */
		lp->state = Lrclose;
		lp->err = 0;
	}

	if(lp->state==Lrclose){
		dkhangup(lp);
	}
	wakeup(&lp->r);
}

/*
 *  15-second timer for all interfaces
 */
static Rendez dkt;
static int
fuckit(void *a)
{
	return 0;
}
static void
dktimer(void *a)
{
	int dki, i;
	Dk *dp;
	Line *lp;

	waserror();

	for(;;){
		/*
		 *  loop through the active dks
		 */
		for(dki=0; dki<Ndk; dki++){
			dp = &dk[dki];
			if(dp->csc==0)
				continue;

			/*
			 * send keep alive
			 */
			dkmesg(dp, T_ALIVE, D_CONTINUE, 0, 0);

			/*
			 *  remind controller of dead lines and
			 *  timeout calls that take to long
			 */
			for (i=0; i<dp->lines; i++){
				lp = &dp->line[i];
				switch(lp->state){
				case Llclose:
					dkmesg(dp, T_CHG, D_CLOSE, i, 0);
					break;

				case Ldialing:
					if(lp->calltolive==0 || --lp->calltolive!=0)
						break;
					dkreplymesg(dp, (Dkmsg *)0, i);
					break;
				}
			}
		}
		tsleep(&dkt, fuckit, 0, 7500);
	}
}

A gnot/devincon.c => gnot/devincon.c +764 -0
@@ 0,0 1,764 @@
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"errno.h"
#include	"devtab.h"

#include	"io.h"

typedef struct Incon	Incon;
typedef struct Device	Device;

#define NOW (MACHP(0)->ticks*MS2HZ)

#define DPRINT if(0)

enum {
	Minstation=	2,	/* lowest station # to poll */
	Maxstation=	15,	/* highest station # to poll */
	Nincon=		1,	/* number of incons */
	Nraw=		1024,	/* size of raw input buffer */
};

/*
 *  incon datakit board
 */
struct Device {
	uchar	cdata;
#define	cpolln	cdata
	uchar	u0;
	uchar	cstatus;
	uchar	u1;
	uchar	creset;
	uchar	u2;
	uchar	csend;
	uchar	u3;
	ushort	data_cntl;	/* data is high byte, cntl is low byte */
	uchar	status;
#define cmd	status
	uchar	u5;
	uchar	reset;
	uchar	u6;
	uchar	send;
	uchar	u7;
};
#define	INCON	((Device *)0x40700000)

struct Incon {
	QLock;

	QLock	xmit;		/* transmit lock */
	QLock	reslock;	/* reset lock */
	Device	*dev;
	int	station;	/* station number */
	int	state;		/* chip state */
	Rendez	r;		/* output process */
	Rendez	kr;		/* input kernel process */
	ushort	chan;		/* current input channel */
	Queue	*rq;		/* read queue */
	uchar	buf[1024];	/* bytes being collected */
	uchar	*wptr;		/* pointer into buf */
	int	kstarted;	/* true if kernel process started */

	ushort	raw[Nraw];
	ushort	*rp;
	ushort	*wp;

	/* statistics */

	ulong	overflow;	/* overflow errors */
	ulong	pack0;		/* channel 0 */
	ulong	crc;		/* crc errors */
	ulong	in;		/* bytes in */
	ulong	out;		/* bytes out */
};

Incon incon[Nincon];

/*
 *  chip state
 */
enum {
	Selecting,
	Selected,
	Dead,
};

/*
 *  internal chip registers
 */
#define	sel_polln	0
#define	sel_station	1
#define	sel_poll0	2
#define sel_rcv_cnt	3
#define sel_rcv_tim	4
#define sel_tx_cnt	5

/*
 *  CSR bits
 */
#define INCON_RUN	0x80
#define INCON_STOP	0x00
#define ENABLE_IRQ	0x40
#define ENABLE_TX_IRQ	0x20
#define INCON_ALIVE	0x80
#define TX_FULL		0x10
#define TX_EMPTY	0x08
#define RCV_EMPTY	0x04
#define OVERFLOW	0x02
#define CRC_ERROR	0x01

/*
 *  polling constants
 */
#define HT_GNOT	0x30
#define ST_UNIX 0x04
#define NCHAN 16

static void inconkproc(void*);

/*
 *  incon stream module definition
 */
static void inconoput(Queue*, Block*);
static void inconstopen(Queue*, Stream*);
static void inconstclose(Queue*);
Qinfo inconinfo = { nullput, inconoput, inconstopen, inconstclose, "incon" };

/*
 *  set the incon parameters
 */
void
inconset(Incon *ip, int cnt, int delay)
{
	Device *dev;

	if (cnt<1 || cnt>14 || delay<1 || delay>15)
		error(0, Ebadarg);

	dev = ip->dev;
	dev->cmd = sel_rcv_cnt | INCON_RUN;
	*(uchar *)&dev->data_cntl = cnt;
	dev->cmd = sel_rcv_tim | INCON_RUN;
	*(uchar *)&dev->data_cntl = delay;
	dev->cmd = INCON_RUN | ENABLE_IRQ;
}

static void
nop(void)
{
}

/*
 *  poll for a station number
 */
void 
inconpoll(Incon *ip, int station)
{
	ulong timer;
	Device *dev;

	dev = ip->dev;

	/*
	 *  get us to a known state
	 */
	ip->state = Dead;
	dev->cmd = INCON_STOP;

	/*
	 * try a station number
	 */
	dev->cmd = sel_station;
	*(uchar *)&dev->data_cntl = station;
	dev->cmd = sel_poll0;
	*(uchar *)&dev->data_cntl = HT_GNOT;
	dev->cmd = sel_rcv_cnt;
	*(uchar *)&dev->data_cntl = 3;
	dev->cmd = sel_rcv_tim;
	*(uchar *)&dev->data_cntl = 15;
	dev->cmd = sel_tx_cnt;
	*(uchar *)&dev->data_cntl = 1;
	dev->cmd = sel_polln;
	*(uchar *)&dev->data_cntl = 0x00;
	*(uchar *)&dev->data_cntl = ST_UNIX;
	*(uchar *)&dev->data_cntl = NCHAN;
	*(uchar *)&dev->data_cntl = 'g';
	*(uchar *)&dev->data_cntl = 'n';
	*(uchar *)&dev->data_cntl = 'o';
	*(uchar *)&dev->data_cntl = 't';
	dev->cpolln = 0;

	/*
	 *  poll and wait for ready (or 1/4 second)
	 */
	ip->state = Selecting;
	dev->cmd = INCON_RUN | ENABLE_IRQ;
	timer = NOW + 250;
	while (NOW < timer) {
		nop();
		if(dev->status & INCON_ALIVE){
			ip->station = station;
			ip->state = Selected;
			break;
		}
	}
}

/*
 *  reset the chip and find a new staion number
 */
void
inconrestart(Incon *ip)
{
	Device *dev;
	int i;

	if(!canqlock(&ip->reslock))
		return;

	/*
	 *  poll for incon station numbers
	 */
	for(i = Minstation; i <= Maxstation; i++){
		inconpoll(ip, i);
		if(ip->state == Selected)
			break;
	}
	switch(ip->state) {
	case Selecting:
		print("incon[%d] not polled\n", ip-incon);
		break;
	case Selected:
		print("incon[%d] station %d\n", ip-incon, ip->station);
		inconset(ip, 8, 9);
		break;
	default:
		print("incon[%d] bollixed\n", ip-incon);
		break;
	}
	qunlock(&ip->reslock);
}

/*
 *  reset all incon chips.
 */
void
inconreset(void)
{
	int i;
	Incon *ip;

	incon[0].dev = INCON;
	incon[0].state = Selected;
	incon[0].rp = incon[0].wp = incon[0].raw;
	for(i=1; i<Nincon; i++){
		incon[i].dev = INCON+i;
		incon[i].state = Dead;
		incon[i].dev->cmd = INCON_STOP;
		incon[i].rp = incon[i].wp = incon[i].raw;
	}
}

void
inconinit(void)
{
}

/*
 *  enable the device for interrupts, spec is the device number
 */
Chan*
inconattach(char *spec)
{
	Incon *ip;
	int i;
	Chan *c;

	i = strtoul(spec, 0, 0);
	if(i >= Nincon)
		error(0, Ebadarg);
	ip = &incon[i];
	if(ip->state != Selected)
		inconrestart(ip);

	c = devattach('i', spec);
	c->dev = i;
	c->qid = CHDIR;
	return c;
}

Chan*
inconclone(Chan *c, Chan *nc)
{
	return devclone(c, nc);
}

int	 
inconwalk(Chan *c, char *name)
{
	return devwalk(c, name, 0, 0, streamgen);
}

void	 
inconstat(Chan *c, char *dp)
{
	devstat(c, dp, 0, 0, streamgen);
}

Chan*
inconopen(Chan *c, int omode)
{
	if(c->qid == CHDIR){
		if(omode != OREAD)
			error(0, Eperm);
	}else
		streamopen(c, &inconinfo);
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

void	 
inconcreate(Chan *c, char *name, int omode, ulong perm)
{
	error(0, Eperm);
}

void	 
inconclose(Chan *c)
{
	if(c->qid != CHDIR)
		streamclose(c);
}

long	 
inconread(Chan *c, void *buf, long n)
{
	return streamread(c, buf, n);
}

long	 
inconwrite(Chan *c, void *buf, long n)
{
	return streamwrite(c, buf, n, 0);
}

void	 
inconremove(Chan *c)
{
	error(0, Eperm);
}

void	 
inconwstat(Chan *c, char *dp)
{
	error(0, Eperm);
}

void
inconuserstr(Error *e, char *buf)
{
	consuserstr(e, buf);
}

void	 
inconerrstr(Error *e, char *buf)
{
	rooterrstr(e, buf);
}

/*
 *	the stream routines
 */

/*
 *  create the kernel process for input
 */
static void
inconstopen(Queue *q, Stream *s)
{
	Incon *ip;
	char name[32];

	ip = &incon[s->dev];
	sprint(name, "**incon%d**", s->dev);
	q->ptr = q->other->ptr = ip;
	ip->rq = q;
	kproc(name, inconkproc, ip);
}

/*
 *  kill off the kernel process
 */
static int
kDead(void *arg)
{
	Incon *ip;

	ip = (Incon *)arg;
	return ip->kstarted == 0;
}
static void
inconstclose(Queue * q)
{
	Incon *ip;

	ip = (Incon *)q->ptr;
	qlock(ip);
	ip->rq = 0;
	qunlock(ip);
	wakeup(&ip->kr);
	sleep(&ip->r, kDead, ip);
}

/*
 *  free all blocks of a message in `q', `bp' is the first block
 *  of the message
 */
static void
freemsg(Queue *q, Block *bp)
{
	for(; bp; bp = getq(q)){
		if(bp->flags & S_DELIM){
			freeb(bp);
			return;
		}
		freeb(bp);
	}
}

/*
 *  output a block
 *
 *  the first 2 bytes of every message are the channel number,
 *  low order byte first.  the third is a possible trailing control
 *  character.
 */
void
inconoput(Queue *q, Block *bp)
{
	Device *dev;
	Incon *ip;
	ulong end;
	int chan;
	int ctl;
	int n, size;

	if(bp->type != M_DATA){
		freeb(bp);
		return;
	}

	/*
	 *  get a whole message before handing bytes to the device
	 */
	if(!putq(q, bp))
		return;

	/*
	 *  one transmitter at a time
	 */
	ip = (Incon *)q->ptr;
	qlock(&ip->xmit);
	dev = ip->dev;

	/*
	 *  parse message
	 */
	bp = getq(q);
	if(bp->wptr - bp->rptr < 3){
		freemsg(q, bp);
		qunlock(&ip->xmit);
		return;
	}
	chan = bp->rptr[0] | (bp->rptr[1]<<8);
	ctl = bp->rptr[2];
	bp->rptr += 3;

	/*
	 *  make sure there's an incon out there
	 */
	if(!(dev->status&INCON_ALIVE) || ip->state==Dead){
		inconrestart(ip);
		freemsg(q, bp);
		qunlock(&ip->xmit);
		return;
	}

	/*
	 *  send the 8 bit data
	 */
	for(;;){
		/*
		 *  spin till there is room
		 */
		for(end = NOW+1000; dev->status & TX_FULL;){
			nop();	/* make sure we don't optimize too much */
			if(NOW > end){
				print("incon output stuck\n");
				freemsg(q, bp);
				qunlock(&ip->xmit);
				return;
			}
		}

		/*
		 *  put in next packet
		 */
		n = bp->wptr - bp->rptr;
		if(n > 16)
			n = 16;
		size = n;
		dev->cdata = chan;
		DPRINT("CH|%uo->\n", chan);
		while(n--){
			DPRINT("->%uo\n", *bp->rptr);
			*(uchar *)&dev->data_cntl = *bp->rptr++;
		}

		/*
		 *  get next block 
		 */
		if(bp->rptr >= bp->wptr){
			if(bp->flags & S_DELIM){
				freeb(bp);
				break;
			}
			freeb(bp);
			bp = getq(q);
			if(bp==0)
				break;
		}

		/*
		 *  end packet
		 */
		dev->cdata = 0;
	}

	/*
	 *  send the control byte if there is one
	 */
	if(ctl){
		if(size >= 16){
			dev->cdata = 0;
			DPRINT("CH|%uo->\n", chan);
			dev->cdata = chan;
		}
		DPRINT("CTL|%uo->\n", ctl);
		dev->cdata = ctl;
	}
	dev->cdata = 0;

	qunlock(&ip->xmit);
	return;
}

/*
 *  return true if the raw fifo is non-empty
 */
static int
notempty(void *arg)
{
	Incon *ip;

	ip = (Incon *)arg;
	return ip->wp!=ip->rp;
}

/*
 *  fill a block with what is currently buffered and send it upstream
 */
static void
upstream(Incon *ip, unsigned int ctl)
{
	int n;
	Block *bp;

	n = ip->wptr - ip->buf;
	bp = allocb(3 + n);
	bp->wptr[0] = ip->chan;
	bp->wptr[1] = ip->chan>>8;
	bp->wptr[2] = ctl;
	if(n)
		memcpy(&bp->wptr[3], ip->buf, n);
	bp->wptr += 3 + n;
	bp->flags |= S_DELIM;
	PUTNEXT(ip->rq, bp);
	ip->wptr = ip->buf;
}

/*
 *  Read bytes from the raw input circular buffer.
 */
static void
inconkproc(void *arg)
{
	Incon *ip;
	unsigned int c;
	uchar *lim;
	ushort *p, *e;

	ip = (Incon *)arg;
	ip->kstarted = 1;
	ip->wptr = ip->buf;

	e = &ip->raw[Nraw];
	for(;;){
		/*
		 *  die if the device is closed
		 */
		qlock(ip);
		if(ip->rq == 0){
			qunlock(ip);
			ip->kstarted = 0;
			wakeup(&ip->r);
			return;
		}

		/*
		 *  sleep if input fifo empty
		 */
		while(!notempty(ip))
			sleep(&ip->kr, notempty, ip);
		p = ip->rp;

		/*
		 *  get channel number
		 */
		c = (*p++)>>8;
		if(p == e)
			p = ip->raw;
		DPRINT("<-CH|%uo\n", c);
		if(ip->chan != c){
			if(ip->wptr - ip->buf != 0)
				upstream(ip, 0);
			ip->chan = c;
		}

		/*
		 *  null byte marks end of packet
		 */
		for(lim = &ip->buf[sizeof ip->buf];;){
			if((c=*p++)&1) {
				/*
				 *  data byte, put in local buffer
				 */
				c = *ip->wptr++ = c>>8;
				DPRINT("<-%uo\n", c);
				if(ip->wptr >= lim)
					upstream(ip, 0);
			} else if (c>>=8) {
				/*
				 *  control byte ends block
				 */
				DPRINT("<-CTL|%uo\n", c);
				upstream(ip, c);
			} else {
				/* end of packet */
				break;
			}
			if(p == e)
				p = ip->raw;
		}
		ip->rp = p;
		qunlock(ip);
	}
}

/*
 *  read the packets from the device into the raw input buffer.
 *  we have to do this at interrupt tevel to turn off the interrupts.
 */
static
rdpackets(Incon *ip)
{
	Device *dev;
	unsigned int c;
	ushort *p, *e;
	int n;

	dev = ip->dev;
	while(!(dev->status & RCV_EMPTY)){
		n = ip->rp - ip->wp;
		if(n <= 0)
			n += Nraw;
		if(n < 19){
			/*
			 *  no room in the raw queue, throw it away
			 */
			c = (dev->data_cntl)>>8;
			for(c=0;c<18;c++){
				if(dev->data_cntl == 0)
					break;
			}
		} else {
			/*
			 *  put packet in the raw queue
			 */
			p = ip->wp;
			e = &ip->raw[Nraw];
			*p++ = dev->data_cntl;
			if(p == e)
				p = ip->raw;
			do {
				*p++ = c = dev->data_cntl;
				if(p == e)
					p = ip->raw;
			} while(c);
			ip->wp = p;
		}
	}
	wakeup(&ip->kr);
}

/*
 *  Receive an incon interrupt.  One entry point
 *  for all types of interrupt.  Until we figure out
 *  how to use more than one incon, this routine only
 *  is for incon[0].
 */
inconintr(Ureg *ur)
{
	uchar status;
	Incon *ip;

	ip = &incon[0];

	status = ip->dev->status;
	if(!(status & RCV_EMPTY))
		rdpackets(ip);

	/* check for exceptional conditions */
	if(status&(OVERFLOW|CRC_ERROR)){
		if(status&OVERFLOW){
			print("incon overflow\n");
			ip->overflow++;
		}
		if(status&CRC_ERROR){
			print("incon crc error\n");
			ip->crc++;
		}
	}

	/* see if it died underneath us */
	if(!(status&INCON_ALIVE)){
		switch(ip->state){
		case Selected:
			ip->dev->cmd = INCON_STOP;
			print("Incon died\n");
			break;
		case Selecting:
			print("rejected\n");
			break;
		default:
			ip->dev->cmd = INCON_STOP;
			break;
		}
		ip->state = Dead;
	}
}

M gnot/devmnt.c => gnot/devmnt.c +2 -0
@@ 382,6 382,7 @@ mntclose(Chan *c)
	if(c == m->mntpt)
		m->mntpt = 0;
	if(decref(m) == 0){		/* BUG: need to hang up all pending i/o */
print("close mount table %d\n", m->mntid);
		qlock(m);
		close(m->msg);
		m->msg = 0;


@@ 467,6 468,7 @@ mntremove(Chan *c)
		mhfree(mh);
		nexterror();
	}
	decref(m);
	mh->thdr.type = Tremove;
	mh->thdr.fid = c->fid;
	mntxmit(m, mh);

M gnot/devpipe.c => gnot/devpipe.c +1 -1
@@ 129,7 129,7 @@ pipewrite(Chan *c, void *va, long n)
		postnote(u->p, 1, "sys: write on closed pipe", NExit);
		error(0, Egreg);
	}
	return streamwrite(c, va, n);
	return streamwrite(c, va, n, 0);
}

void

M gnot/errno.h => gnot/errno.h +6 -1
@@ 36,10 36,15 @@ enum{
	Etoosmall,	/* read or write too small */
	Ehungup,	/* write to hungup stream */
	Ebadnet,	/* illegal network address */
	Enoifc,		/* no free nonet interface slots */
	Enoifc,		/* no free interface slots */
	Enodev,		/* no free devices */
	Ebadctl,	/* bad process control request */
	Enonote,	/* note overflow */
	Eintr,		/* interrupted */
	Edestbusy,	/* datakit destination busy */
	Enetbusy,	/* datakit controller busy */
	Edestotl,	/* datakit destination out to lunch */
	Enetotl,	/* datakit controller out to lunch */
	Erejected,	/* datakit destination rejected call */
	Egreg,		/* it's all greg's fault */
};

M gnot/fault.c => gnot/fault.c +3 -1
@@ 83,6 83,7 @@ fault(Ureg *ur, FFrame *f)
		read = (f->ssw&READ) && !(f->ssw&RM);
	else
		read = f->ssw&(FB|FC);
/* print("fault pc=%lux addr=%lux read %d\n", ur->pc, badvaddr, read); /**/

	s = seg(u->p, addr);
	if(s == 0){


@@ 272,9 273,10 @@ validaddr(ulong addr, ulong len, int write)
	Seg *s;

	if((long)len < 0)
		panic("validaddr len %lux\n", len);
		goto Err;
	s = seg(u->p, addr);
	if(s==0 || addr+len>s->maxva || (write && (s->o->flag&OWRPERM)==0)){
    Err:
		pprint("invalid address in sys call pc %lux sp %lux\n", ((Ureg*)UREGADDR)->pc, ((Ureg*)UREGADDR)->sp);
		postnote(u->p, 1, "bad address", NDebug);
		error(0, Ebadarg);

M gnot/fns.h => gnot/fns.h +9 -2
@@ 4,6 4,7 @@ Block	*allocb(ulong);
void	append(List**, List*);
void	cancel(Alarm*);
int	canlock(Lock*);
int	canqlock(QLock*);
void	chaninit(void);
void	chandevreset(void);
void	chandevinit(void);


@@ 47,7 48,9 @@ void	freepage(Orig*);
void	freepte(Orig*);
void	freesegs(int);
void	freealarm(Alarm*);
Block	*getq(Queue *);
Block	*getb(Blist*);
int	getfields(char*, char**, int, char);
Block	*getq(Queue*);
void	gotolabel(Label*);
void	growpte(Orig*, ulong);
void	*ialloc(ulong, int);


@@ 55,6 58,7 @@ int	incref(Ref*);
void	insert(List**, List*, List*);
void	isdir(Chan*);
void	kbdchar(int);
void	kproc(char*, void(*)(void*), void*);
void	lock(Lock*);
void	lockinit(void);
Orig	*lookorig(ulong, ulong, int, Chan*);


@@ 86,6 90,8 @@ void	printinit(void);
void	printslave(void);
void	procinit0(void);
Proc	*proctab(int);
Queue*	pushq(Stream*, Qinfo*);
void	putbq(Blist*, Block*);
void	putkmmu(ulong, ulong);
void	putmmu(ulong, ulong);
int	putq(Queue*, Block*);


@@ 118,10 124,11 @@ Devgen	streamgen;
void	streamclose(Chan*);
void	streaminit(void);
long	streamread(Chan*, void*, long);
long	streamwrite(Chan*, void*, long);
long	streamwrite(Chan*, void*, long, int);
Stream*	streamnew(Chan*, Qinfo*);
void	streamopen(Chan*, Qinfo*);
int	streamparse(char*, Block*);
long	stringread(Chan*, void*, long, char*);
long	syscall(Ureg*);
int	tas(char*);
void	touser(void);

M gnot/lock.c => gnot/lock.c +6 -0
@@ 66,6 66,12 @@ qlock(QLock *q)
	sched();
}

int
canqlock(QLock *q)
{
	return canlock(&q->use);
}

void
qunlock(QLock *q)
{

M gnot/main.c => gnot/main.c +24 -2
@@ 7,10 7,24 @@
#include	"ureg.h"
#include	"init.h"

typedef struct Boot{
	long station;
	long traffic;
	char user[NAMELEN];
	char server[64];
	char line[64];
}Boot;
#define BOOT ((Boot*)0)

char protouser[NAMELEN];

void unloadboot(void);

void
main(void)
{
	Lock l;
	unloadboot();
	machinit();
	mmuinit();
	confinit();


@@ 28,6 42,12 @@ main(void)
}

void
unloadboot(void)
{
	strncpy(protouser, BOOT->user, NAMELEN);
}

void
machinit(void)
{
	int n;


@@ 87,7 107,7 @@ userinit(void)
	p = newproc();
	p->pgrp = newpgrp();
	strcpy(p->text, "*init*");
	strcpy(p->pgrp->user, "bootes");
	strcpy(p->pgrp->user, protouser);
/*	savefpregs(&initfp);	/**/
/*	p->fpstate = FPinit;	/**/



@@ 219,7 239,7 @@ confinit(void)
	conf.npage = (2*1024*1024)/BY2PG;
	conf.npte = 500;
	conf.nmod = 50;
	conf.nalarm = 50;
	conf.nalarm = 1000;
	conf.norig = 50;
	conf.nchan = 100;
	conf.nenv = 50;


@@ 228,6 248,8 @@ confinit(void)
	conf.nmtab = 50;
	conf.nmount = 100;
	conf.nmntdev = 5;
	conf.nmntbuf = 10;
	conf.nmnthdr = 10;
	conf.nstream = 64;
	conf.nqueue = 5 * conf.nstream;
	conf.nblock = 16 * conf.nstream;

M gnot/proc.c => gnot/proc.c +59 -0
@@ 172,6 172,8 @@ loop:
		unlock(&pidalloc);
		if(p->pid == 0)
			panic("pidalloc");
print("allocate process %d\n", p->pid);
DEBUG();
		return p;
	}
	unlock(&procalloc);


@@ 523,3 525,60 @@ DEBUG()
				p->time[0], p->time[1]);
	}
}

void
kproc(char *name, void (*func)(void *), void *arg)
{
	Proc *p;
	int n;
	ulong upa;
	int lastvar;	/* used to compute stack address */
	User *up;

	/*
	 * Kernel stack
	 */
	p = newproc();
	p->upage = newpage(1, 0, USERADDR|(p->pid&0xFFFF));
	upa = p->upage->pa|KZERO;
	up = (User *)upa;

	/*
	 * Save time: only copy u-> data and useful stack
	 */
	memcpy((void*)upa, u, sizeof(User));
	n = USERADDR+BY2PG - (ulong)&lastvar;
	n = (n+32) & ~(BY2WD-1);	/* be safe & word align */
	memcpy((void*)(upa+BY2PG-n), (void*)((u->p->upage->pa|KZERO)+BY2PG-n), n);
	((User *)upa)->p = p;

	/*
	 * Refs
	 */
	incref(up->dot);
	for(n=0; n<=up->maxfd; n++)
		up->fd[n] = 0;
	up->maxfd = 0;

	/*
	 * Sched
	 */
	if(setlabel(&p->sched)){
		u->p = p;
		p->state = Running;
		p->mach = m;
		m->proc = p;
		spllo();
		strncpy(p->text, name, sizeof p->text);
		(*func)(arg);
		pexit(0, 1);
	}
	p->pgrp = u->p->pgrp;
	incref(p->pgrp);
	p->nchild = 0;
	p->parent = 0;
	memset(p->time, 0, sizeof(p->time));
	p->time[TReal] = MACHP(0)->ticks;
	ready(p);
	flushmmu();
}

M gnot/stream.c => gnot/stream.c +154 -26
@@ 7,12 7,23 @@
#include	"errno.h"
#include	"devtab.h"

/*
 *  process end line discipline
 */
static void stputq(Queue*, Block*);
Qinfo procinfo = { stputq, nullput, 0, 0, "process" } ;
/*extern Qinfo noetherinfo;	*/
Qinfo procinfo = { stputq, nullput, 0, 0, "process" };

/*
 *  line disciplines that can be pushed
 *
 *  WARNING: this table should be the result of configuration
 */
extern Qinfo noetherinfo;
extern Qinfo dkmuxinfo;
extern Qinfo urpinfo;
static Qinfo *lds[] = {
/*	&noetherinfo,	*/
	&dkmuxinfo,
	&urpinfo,
	0
};



@@ 34,7 45,9 @@ static Lock garbagelock;
 */
typedef struct {
	int	size;
	Queue;
	Blist;
	QLock;		/* qlock for sleepers on r */
	Rendez	r;	/* sleep here waiting for blocks */
} Bclass;
Bclass bclass[Nclass]={
	{ 0 },


@@ 108,7 121,9 @@ allocb(ulong size)
	while(bcp->first == 0){
		unlock(bcp);
		print("waiting for blocks\n");
		qlock(bcp);
		sleep(&bcp->r, isblock, (void *)bcp);
		qunlock(bcp);
		lock(bcp);
	}
	bp = bcp->first;


@@ 128,25 143,33 @@ allocb(ulong size)
}

/*
 *  Free a block.  Poison its pointers so that someone trying to access
 *  it after freeing will cause a dump.
 *  Free a block (or list of blocks).  Poison its pointers so that
 *  someone trying to access it after freeing will cause a dump.
 */
void
freeb(Block *bp)
{
	Bclass *bcp;
	int tries;

	bcp = &bclass[bp->flags & S_CLASS];
	bp->rptr = bp->wptr = 0;
	lock(bcp);
	bp->rptr = bp->wptr = 0;
	if(bcp->first)
		bcp->last->next = bp;
	else
		bcp->first = bp;
	tries = 0;
	while(bp->next){
		if(++tries > 10){
			dumpstack();
			panic("freeb");
		}
		bp = bp->next;
	}
	bcp->last = bp;
	bp->next = 0;
	wakeup(&bcp->r);
	unlock(bcp);
	wakeup(&bcp->r);
}

/*


@@ 269,11 292,11 @@ putq(Queue *q, Block *bp)
		q->last->next = bp;
	else
		q->first = bp;
	q->len += bp->wptr - bp->rptr;
	q->len += BLEN(bp);
	delim = bp->flags & S_DELIM;
	while(bp->next) {
		bp = bp->next;
		q->len += bp->wptr - bp->rptr;
		q->len += BLEN(bp);
		delim |= bp->flags & S_DELIM;
	}
	q->last = bp;


@@ 292,22 315,21 @@ putb(Blist *q, Block *bp)
		q->last->next = bp;
	else
		q->first = bp;
	q->len += bp->wptr - bp->rptr;
	q->len += BLEN(bp);
	delim = bp->flags & S_DELIM;
	while(bp->next) {
		bp = bp->next;
		q->len += bp->wptr - bp->rptr;
		q->len += BLEN(bp);
		delim |= bp->flags & S_DELIM;
	}
	q->last = bp;
	bp->next = 0;
	return delim;
}

/*
 *  add a block to the start of a queue 
 */
static void
void
putbq(Blist *q, Block *bp)
{
	lock(q);


@@ 316,12 338,12 @@ putbq(Blist *q, Block *bp)
	else
		q->last = bp;
	q->first = bp;
	q->len += bp->wptr - bp->rptr;
	q->len += BLEN(bp);
	unlock(q);
}

/*
 *  remove the first block from a queue 
 *  remove the first block from a queue
 */
Block *
getq(Queue *q)


@@ 334,7 356,7 @@ getq(Queue *q)
		q->first = bp->next;
		if(q->first == 0)
			q->last = 0;
		q->len -= bp->wptr - bp->rptr;
		q->len -= BLEN(bp);
		if((q->flag&QHIWAT) && q->len < Streamhi/2){
			wakeup(&q->other->next->other->r);
			q->flag &= ~QHIWAT;


@@ 344,6 366,10 @@ getq(Queue *q)
	unlock(q);
	return bp;
}

/*
 *  remove the first block from a list of blocks
 */
Block *
getb(Blist *q)
{


@@ 354,13 380,87 @@ getb(Blist *q)
		q->first = bp->next;
		if(q->first == 0)
			q->last = 0;
		q->len -= bp->wptr - bp->rptr;
		q->len -= BLEN(bp);
		bp->next = 0;
	}
	return bp;
}

/*
 *  make sure the first block has n bytes
 */
Block *
pullup(Block *bp, int n)
{
	Block *nbp;
	int i;

	/*
	 *  this should almost always be true, the rest it
	 *  just for to avoid every caller checking.
	 */
	if(BLEN(bp) >= n)
		return bp;

	/*
	 *  if not enough room in the first block,
	 *  add another to the front of the list.
	if(bp->lim - bp->rptr < n){
		nbp = allocb(n);
		nbp->next = bp;
		bp = nbp;
	}

	/*
	 *  copy bytes from the trailing blocks into the first
	 */
	n -= BLEN(bp);
	while(nbp = bp->next){
		i = BLEN(nbp);
		if(i > n) {
			memcpy(bp->wptr, nbp->rptr, n);
			bp->wptr += n;
			nbp->rptr += n;
			return bp;
		} else {
			memcpy(bp->wptr, nbp->rptr, i);
			bp->wptr += i;
			bp->next = nbp->next;
			nbp->next = 0;
			freeb(nbp);
		}
	}
	freeb(bp);
	return 0;
}

/*
 *  grow the front of a list of blocks by n bytes
 */
Block *
prepend(Block *bp, int n)
{
	Block *nbp;

	if(bp->base && (bp->rptr - bp->base)>=n){
		/*
		 *  room for channel number in first block of message
		 */
		bp->rptr -= n;
		return bp;
	} else {
		/*
		 *  make new block, put message number at end
		 */
		nbp = allocb(2);
		nbp->next = bp;
		nbp->wptr = nbp->lim;
		nbp->rptr = nbp->wptr - n;
		return nbp;
	}
}

/*
 *  put a block into the bit bucket
 */
void


@@ 415,7 515,7 @@ streamparse(char *name, Block *bp)
	int len;

	len = strlen(name);
	if(bp->wptr - bp->rptr < len)
	if(BLEN(bp) < len)
		return 0;
	if(strncmp(name, (char *)bp->rptr, len)==0){
		if(bp->rptr[len] == ' ')


@@ 622,14 722,19 @@ stputq(Queue *q, Block *bp)
		freeb(bp);
		q->flag |= QHUNGUP;
		q->other->flag |= QHUNGUP;
		wakeup(&q->other->r);
	} else {
		lock(q);
		if(q->first)
			q->last->next = bp;
		else
			q->first = bp;
		q->len += BLEN(bp);
		while(bp->next) {
			bp = bp->next;
			q->len += BLEN(bp);
		}
		q->last = bp;
		q->len += bp->wptr - bp->rptr;
		if(q->len >= Streamhi)
			q->flag |= QHIWAT;
		unlock(q);


@@ 651,8 756,7 @@ stringread(Chan *c, uchar *buf, long n, char *str)
		n = i;
	if(n<0)
		return 0;
	memcpy(buf + c->offset, str, n);
	c->offset += n;
	memcpy(buf, str + c->offset, n);
	return n;
}



@@ 716,7 820,7 @@ streamread(Chan *c, void *vbuf, long n)
			continue;
		}

		i = bp->wptr - bp->rptr;
		i = BLEN(bp);
		if(i <= left){
			memcpy(buf, bp->rptr, i);
			left -= i;


@@ 806,7 910,7 @@ flowctl(Queue *q)
 *  send the request as a single delimited block
 */
long
streamwrite(Chan *c, void *a, long n)
streamwrite(Chan *c, void *a, long n, int docopy)
{
	Stream *s;
	Block *bp;


@@ 846,7 950,7 @@ streamwrite(Chan *c, void *a, long n)
	if(q->other->flag & QHUNGUP)
		error(0, Ehungup);

	if(GLOBAL(a) || n==0){
	if((GLOBAL(a) && !docopy) || n==0){
		/*
		 *  `a' is global to the whole system, just create a
		 *  pointer to it and pass it on.


@@ 887,3 991,27 @@ streamwrite(Chan *c, void *a, long n)
	poperror();
	return n;
}

/*
 *  like andrew's getmfields but no hidden state
 */
int
getfields(char *lp,	/* to be parsed */
	char **fields,	/* where to put pointers */
	int n,		/* number of pointers */
	char sep	/* separator */
)
{
	int i;

	for(i=0; lp && *lp && i<n; i++){
		while(*lp == sep)
			*lp++=0;
		if(*lp == 0)
			break;
		fields[i]=lp;
		while(*lp && *lp != sep)
			lp++;
	}
	return i;
}

A gnot/sturp.c => gnot/sturp.c +845 -0
@@ 0,0 1,845 @@
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"errno.h"

enum {
	Nurp=		32,
	MSrexmit=	1000,
	Nmask=		0x7,
};

#define DPRINT print

typedef struct Urp	Urp;

#define NOW (MACHP(0)->ticks*MS2HZ)

/*
 * URP status
 */
struct urpstat {
	ulong	input;		/* bytes read from urp */
	ulong	output;		/* bytes output to urp */
	ulong	rxmit;		/* retransmit rejected urp msg */
	ulong	rjtrs;		/* reject, trailer size */
	ulong	rjpks;		/* reject, packet size */
	ulong	rjseq;		/* reject, sequence number */
	ulong	levelb;		/* unknown level b */
	ulong	enqsx;		/* enqs sent */
	ulong	enqsr;		/* enqs rcved */
} urpstat;

struct Urp {
	QLock;
	short	state;		/* flags */
	Rendez	r;		/* process waiting for close */

	/* input */

	Queue	*rq;		/* input queue */
	uchar	iseq;		/* last good input sequence number */
	uchar	lastecho;	/* last echo/rej sent */
	uchar	trbuf[3];	/* trailer being collected */
	short	trx;		/* # bytes in trailer being collected */

	/* output */

	QLock	xmit;		/* output lock, only one process at a time */
	Queue	*wq;		/* output queue */
	int	maxout;		/* maximum outstanding unacked blocks */
	int	maxblock;	/* max block size */
	int	next;		/* next block to send */
	int	unechoed;	/* first unechoed block */
	int	unacked;	/* first unacked block */
	int	nxb;		/* next xb to use */
	Block	*xb[8];		/* the xmit window buffer */
	QLock	xl[8];
	ulong	timer;		/* timeout for xmit */

	int	kstarted;
};
#define WINDOW(u) ((u->unechoed + u->maxout - u->next)%8)
#define IN(x, f, n) (f<=n ? x>=f && x<n : x<n || x>=f)
#define NEXT(x) (((x)+1)&Nmask)

/*
 *  Protocol control bytes
 */
#define	SEQ	0010		/* sequence number, ends trailers */
#undef	ECHO
#define	ECHO	0020		/* echos, data given to next queue */
#define	REJ	0030		/* rejections, transmission error */
#define	ACK	0040		/* acknowledgments */
#define	BOT	0050		/* beginning of trailer */
#define	BOTM	0051		/* beginning of trailer, more data follows */
#define	BOTS	0052		/* seq update algorithm on this trailer */
#define	SOU	0053		/* start of unsequenced trailer */
#define	EOU	0054		/* end of unsequenced trailer */
#define	ENQ	0055		/* xmitter requests flow/error status */
#define	CHECK	0056		/* xmitter requests error status */
#define	INITREQ 0057		/* request initialization */
#define	INIT0	0060		/* disable trailer processing */
#define	INIT1	0061		/* enable trailer procesing */
#define	AINIT	0062		/* response to INIT0/INIT1 */
#undef	DELAY
#define	DELAY	0100		/* real-time printing delay */
#define	BREAK	0110		/* Send/receive break (new style) */

#define	REJECTING	0x1
#define	INITING 	0x2
#define HUNGUP		0x4
#define	OPEN		0x8
#define CLOSING		0x10

Urp	urp[Nurp];

/*
 *  predeclared
 */
static void	urpciput(Queue*, Block*);
static void	urpiput(Queue*, Block*);
static void	urpoput(Queue*, Block*);
static void	urpopen(Queue*, Stream*);
static void	urpclose(Queue *);
static void	output(Urp*);
static void	sendblock(Urp*, int);
static void	rcvack(Urp*, int);
static void	flushinput(Urp*);
static void	sendctl(Urp*, int);
static void	initoutput(Urp*, int);
static void	initinput(Urp*, int);
static void	urpkproc(void *arg);

Qinfo urpinfo = { urpciput, urpoput, urpopen, urpclose, "urp" };

static void
urpopen(Queue *q, Stream *s)
{
	Urp *up;
	int i;
	char name[128];

	DPRINT("urpopen\n");

	/*
	 *  find a free urp structure
	 */
	for(up = urp; up < &urp[Nurp]; up++){
		qlock(up);
		if(up->state == 0)
			break;
		qunlock(up);
	}
	if(up == &urp[Nurp])
		error(0, Egreg);

	q->ptr = q->other->ptr = up;
	up->rq = q;
	up->wq = q->other;
	up->state = OPEN;
	qunlock(up);
	initinput(up, 0);
	initoutput(up, 0);

	/*
	 *  start the ack/(re)xmit process
	 */
	if(up->kstarted == 0){
		up->kstarted = 1;
		sprint(name, "**urp%d**", up - urp);
		kproc(name, urpkproc, up);
	}
}

/*
 *  Shut down the connection and kill off the kernel process
 */
static int
isflushed(void *a)
{
	Urp *up;

	up = (Urp *)a;
	return (up->state&HUNGUP) || (up->unechoed==up->next && up->wq->len==0);
}
static int
isdead(void *a)
{
	Urp *up;

	up = (Urp *)a;
	return up->kstarted == 0;
}
static void
urpclose(Queue *q)
{
	Block *bp;
	Urp *up;
	int i;

	up = (Urp *)q->ptr;

	/*
	 *  wait for all outstanding messages to drain, tell kernel
	 *  process we're closing.
	 */
	up->state |= CLOSING;
	sleep(&up->r, isflushed, up);

	/*
	 *  ack all outstanding messages
	 */
	qlock(&up->xmit);
	up->state |= HUNGUP;
	i = up->next - 1;
	if(i < 0)
		i = 7;
	rcvack(up, ECHO+i);
	qunlock(&up->xmit);
	DPRINT("urpclose(%ux)\n", up);

	/*
	 *  kill off the kernel process
	 */
	wakeup(&up->rq->r);
	DPRINT("urpclosed(%ux)\n", up);
}

/*
 *  upstream control messages
 */
static void
urpctliput(Urp *up, Queue *q, Block *bp)
{
	switch(bp->type){
	case M_HANGUP:
		up->state |= HUNGUP;
		wakeup(&up->r);
		wakeup(&up->rq->r);
		break;
	}
	PUTNEXT(q, bp);
}

/*
 *  character mode input.
 *
 *  the first byte in every message is a ctl byte (which belongs at the end).
 */
void
urpciput(Queue *q, Block *bp)
{
	Urp *up;
	int i;
	int ctl;

	up = (Urp *)q->ptr;
	if(bp->type != M_DATA){
		urpctliput(up, q, bp);
		return;
	}

	/*
	 *  get the control character
	 */
	ctl = *bp->rptr++;
	if(ctl < 0)
		return;

	/*
	 *  take care of any data
	 */
	if(BLEN(bp)>0 && q->next->len<Streamhi)
		PUTNEXT(q, bp);
	else
		freeb(bp);

	/*
	 *  handle the control character
	 */
	switch(ctl){
	case 0:
		break;
	case ENQ:
		urpstat.enqsr++;
		sendctl(up, up->lastecho);
		sendctl(up, ACK+up->iseq);
		break;

	case CHECK:
		sendctl(up, ACK+up->iseq);
		break;

	case AINIT:
		up->state &= ~INITING;
		flushinput(up);
		wakeup(&up->rq->r);
		break;

	case INIT0:
	case INIT1:
		sendctl(up, AINIT);
		if(ctl == INIT1)
			q->put = urpiput;
		initinput(up, 0);
		break;

	case INITREQ:
		initoutput(up, 0);
		break;

	case BREAK:
		break;

	case REJ+0: case REJ+1: case REJ+2: case REJ+3:
	case REJ+4: case REJ+5: case REJ+6: case REJ+7:
		rcvack(up, ctl);
		break;
	
	case ACK+0: case ACK+1: case ACK+2: case ACK+3:
	case ACK+4: case ACK+5: case ACK+6: case ACK+7:
	case ECHO+0: case ECHO+1: case ECHO+2: case ECHO+3:
	case ECHO+4: case ECHO+5: case ECHO+6: case ECHO+7:
		rcvack(up, ctl);
		break;

	case SEQ+0: case SEQ+1: case SEQ+2: case SEQ+3:
	case SEQ+4: case SEQ+5: case SEQ+6: case SEQ+7:
		i = ctl & Nmask;
		if(q->next->len < Streamhi)
			sendctl(up, up->lastecho = ECHO+i);
		up->iseq = i;
		break;
	}
}

/*
 *  block mode input.
 *
 *  the first byte in every message is a ctl byte (which belongs at the end).
 *
 *  Simplifying assumption:  one put == one message && the control byte
 *	is in the first block.  If this isn't true, strange bytes will be
 *	used as control bytes.
 */
void
urpiput(Queue *q, Block *bp)
{
	Urp *up;
	int i;
	int ctl;

	up = (Urp *)q->ptr;
	if(bp->type != M_DATA){
		urpctliput(up, q, bp);
		return;
	}

	/*
	 *  get the control character
	 */
	ctl = *bp->rptr++;

	/*
	 *  take care of any block count(trx)
	 */
	while(up->trx){
		if(BLEN(bp)<=0)
			break;
		switch (up->trx) {
		case 1:
		case 2:
			up->trbuf[up->trx++] = *bp->rptr++;
			continue;
		default:
			up->trx = 0;
			break;
		}
	}

	/*
	 *  queue the block(s)
	 */
	if(BLEN(bp) > 0){
		putq(q, bp);
		q->last->flags &= ~S_DELIM;
		if(q->len > 4*1024){
			flushinput(up);
			return;
		}
	} else
		freeb(bp);

	/*
	 *  handle the control character
	 */
	switch(ctl){
	case 0:
		break;
	case ENQ:
		print("rENQ %uo %uo\n", up->lastecho, ACK+up->iseq);
		urpstat.enqsr++;
		sendctl(up, up->lastecho);
		sendctl(up, ACK+up->iseq);
		flushinput(up);
		break;

	case CHECK:
		sendctl(up, ACK+up->iseq);
		break;

	case AINIT:
		up->state &= ~INITING;
		flushinput(up);
		wakeup(&up->rq->r);
		break;

	case INIT0:
	case INIT1:
		sendctl(up, AINIT);
		if(ctl == INIT0)
			q->put = urpciput;
		initinput(up, 0);
		break;

	case INITREQ:
		initoutput(up, 0);
		break;

	case BREAK:
		break;

	case BOT:
	case BOTS:
	case BOTM:
		up->trx = 1;
		up->trbuf[0] = ctl;
		break;

	case REJ+0: case REJ+1: case REJ+2: case REJ+3:
	case REJ+4: case REJ+5: case REJ+6: case REJ+7:
		print("rREJ\n");
		rcvack(up, ctl);
		break;
	
	case ACK+0: case ACK+1: case ACK+2: case ACK+3:
	case ACK+4: case ACK+5: case ACK+6: case ACK+7:
	case ECHO+0: case ECHO+1: case ECHO+2: case ECHO+3:
	case ECHO+4: case ECHO+5: case ECHO+6: case ECHO+7:
		rcvack(up, ctl);
		break;

	/*
	 *  if the seuence number is the next expected
	 *	and te trailer length == 3
	 *	and the block count matches the bytes received
	 *  then send the bytes upstream.
	 */
	case SEQ+0: case SEQ+1: case SEQ+2: case SEQ+3:
	case SEQ+4: case SEQ+5: case SEQ+6: case SEQ+7:
		i = ctl & Nmask;
		if(up->trx != 3){
			urpstat.rjtrs++;
			flushinput(up);
			print("sREJ1 %d\n", up->iseq);
			sendctl(up, up->lastecho = REJ+up->iseq);
			break;
		} else if(q->len != up->trbuf[1] + (up->trbuf[2]<<8)){
			urpstat.rjpks++;
			flushinput(up);
			print("sREJ2 %d\n", up->iseq);
			sendctl(up, up->lastecho = REJ+up->iseq);
			break;
		} else if(i != ((up->iseq+1)&Nmask)) {
			urpstat.rjseq++;
			flushinput(up);
			print("sREJ3 %d %d\n", i, up->iseq);
			sendctl(up, up->lastecho = REJ+up->iseq);
			break;
		}

		/*
		 *  send data upstream
		 */
		if(q->first) {
			if(up->trbuf[0] != BOTM)
				q->last->flags |= S_DELIM;
			while(bp = getq(q))
				PUTNEXT(q, bp);
		} else {
			bp = allocb(0);
			bp->flags |= S_DELIM;
			PUTNEXT(q, bp);
		}
		up->trx = 0;

		/*
		 *  acknowledge receipt
		 */
		if(q->next->len < Streamhi){
			sendctl(up, ECHO+i);
			up->lastecho = ECHO+i;
			wakeup(&up->rq->r);
		}
		up->iseq = i;
		break;
	}
}

/*
 *  downstream control
 */
static void
urpctloput(Urp *up, Queue *q, Block *bp)
{
	char *fields[2];
	int n;
	int inwin=0, outwin=0;

	switch(bp->type){
	case M_CTL:
		if(streamparse("init", bp)){
			switch(getfields((char *)bp->rptr, fields, 2, ' ')){
			case 2:
				inwin = strtoul(fields[1], 0, 0);
			case 1:
				outwin = strtoul(fields[0], 0, 0);
			}
/*			initinput(up, inwin); */
			DPRINT("initoutput %d\n", outwin);
			initoutput(up, outwin);
			freeb(bp);
			return;
		}
	}
	PUTNEXT(q, bp);
}

/*
 *  accept data from a writer
 */
static void
urpoput(Queue *q, Block *bp)
{
	Urp *up;

	up = (Urp *)q->ptr;

	if(bp->type != M_DATA){
		urpctloput(up, q, bp);
		return;
	}

	urpstat.output += BLEN(bp);
	putq(q, bp);
	output(up);
}

/*
 *  start output
 */
static void
output(Urp *up)
{
	Block *bp, *nbp;
	ulong now;
	Queue *q;
	int n;

	if(!canqlock(&up->xmit))
		return;

	if(waserror()){
		print("urp output error\n");
		qunlock(&up->xmit);
		nexterror();
	}

	/*
	 *  if still initing and it's time to rexmit, send an INIT1
	 */
	now = NOW;
	if(up->state & INITING){
		if(now > up->timer){
			sendctl(up, INIT1);
			up->timer = now + MSrexmit;
		}
		qunlock(&up->xmit);
		poperror();
		return;
	}

	/*
	 *  fill the transmit buffers
	 */
	q = up->wq;
	for(bp = getq(q); bp && up->xb[up->nxb]==0; up->nxb = NEXT(up->nxb)){
		if(BLEN(bp) > up->maxblock){
			nbp = up->xb[up->nxb] = allocb(0);
			nbp->rptr = bp->rptr;
			nbp->wptr = bp->rptr = bp->rptr + up->maxblock;
		} else {
			up->xb[up->nxb] = bp;
			bp = getq(q);
		}
	}
	if(bp)
		putbq(q, bp);
/*	print("output w(%d) up->xb[%d](%ux) up->nxb(%d) up->state(%ux)\n",
		WINDOW(up), up->next, up->xb[up->next], up->nxb, up->state);
/**/
	/*
	 *  if a retransmit time has elapsed since a transmit, send an ENQ
	 */
	if(up->unechoed != up->next && NOW > up->timer){
		print("sENQ\n");
		up->timer = NOW + MSrexmit;
		up->state &= ~REJECTING;
		sendctl(up, ENQ);
		qunlock(&up->xmit);
		poperror();
		return;
	}

	/*
	 *  if there's a window open, push some blocks out
	 */
	while(WINDOW(up)>0 && up->xb[up->next]!=0 && canqlock(&up->xl[up->next])){
		if(up->xb[up->next])
			sendblock(up, up->next);
		qunlock(&up->xl[up->next]);
		up->next = NEXT(up->next);
	}
	qunlock(&up->xmit);
	poperror();
}

/*
 *  send a control byte, put the byte at the end of the allocated
 *  space in case a lower layer needs header room.
 */
static void
sendctl(Urp *up, int ctl)
{
	Block *bp;

	if(up->wq->next->len > Streamhi)
		return;
	bp = allocb(1);
	bp->wptr = bp->lim;
	bp->rptr = bp->lim-1;
	*bp->rptr = ctl;
	bp->flags |= S_DELIM;
	PUTNEXT(up->wq, bp);
}

/*
 *  send a block.
 */
static void
sendblock(Urp *up, int bn)
{
	Block *bp, *m, *nbp;
	int n;

	up->timer = NOW + MSrexmit;
	if(up->wq->next->len > Streamhi)
		return;

	/*
	 *  message 1, the BOT and the data
	 */
	bp = up->xb[bn];
	m = allocb(1);
	m->rptr = m->lim - 1;
	m->wptr = m->lim;
	*m->rptr = (bp->flags & S_DELIM) ? BOT : BOTM;
	nbp = m->next = allocb(0);
	nbp->rptr = bp->rptr;
	nbp->wptr = bp->wptr;
	nbp->flags |= S_DELIM;
	PUTNEXT(up->wq, m);

	/*
	 *  message 2, the block length and the SEQ
	 */
	m = allocb(3);
	m->rptr = m->lim - 3;
	m->wptr = m->lim;
	n = BLEN(bp);
	m->rptr[0] = SEQ | bn;
	m->rptr[1] = n;
	m->rptr[2] = n<<8;
	m->flags |= S_DELIM;
	PUTNEXT(up->wq, m);
}

/*
 *  receive an acknowledgement
 */
static void
rcvack(Urp *up, int msg)
{
	int seqno;
	int next;

	seqno = msg&Nmask;
	next = NEXT(seqno);

	/*
	 *  release any acknowledged blocks
	 */
	if(IN(seqno, up->unacked, up->next)){
		for(; up->unacked != next; up->unacked = NEXT(up->unacked)){
			qlock(&up->xl[up->unacked]);
			if(up->xb[up->unacked])
				freeb(up->xb[up->unacked]);
			up->xb[up->unacked] = 0;
			qunlock(&up->xl[up->unacked]);
		}
	}

	switch(msg & 0370){
	case ECHO:
		if(IN(seqno, up->unechoed, up->next)) {
			up->unechoed = next;
		}
		/*
		 *  the next reject at the start of a window starts a 
		 *  retransmission.
		 */
		up->state &= ~REJECTING;
		break;
	case REJ:
		if(IN(seqno, up->unechoed, up->next))
			up->unechoed = next;
		/*
		 *  ... FALL THROUGH ...
		 */
	case ACK:
		/*
		 *  start a retransmission if we aren't retransmitting
		 *  and this is the start of a window.
		 */
		if(up->unechoed==next && !(up->state & REJECTING)){
			up->state |= REJECTING;
			up->next = next;
		}
		break;
	}

	wakeup(&up->rq->r);
}

/*
 * throw away any partially collected input
 */
static void
flushinput(Urp *up)
{
	Block *bp;

	while (bp = getq(up->rq))
		freeb(bp);
	up->trx = 0;
}

/*
 *  initialize output
 */
static void
initoutput(Urp *up, int window)
{
	int i;

	/*
	 *  set output window
	 */
	up->maxblock = window/4;
	if(up->maxblock < 64)
		up->maxblock = 64;
	if(up->maxblock > Streamhi/4)
		up->maxblock = Streamhi/4;
	up->maxblock -= 4;
	up->maxout = 3;

	/*
	 *  set sequence varialbles
	 */
	up->unechoed = 1;
	up->unacked = 1;
	up->next = 1;
	up->nxb = 1;

	/*
	 *  free any outstanding blocks
	 */
	for(i = 0; i < 8; i++){
		qlock(&up->xl[i]);
		if(up->xb[i])
			freeb(up->xb[i]);
		qunlock(&up->xl[i]);
	}

	/*
	 *  tell the other side we've inited
	 */
	up->state |= INITING;
	up->timer = NOW + MSrexmit;
	sendctl(up, INIT1);
}

/*
 *  initialize input
 */
static void
initinput(Urp *up, int window)
{
	/*
	 *  restart all sequence parameters
	 */
	up->trx = 0;
	up->iseq = 0;
	up->lastecho = ECHO+0;
	flushinput(up);
}

/*
 *  do retransmissions etc
 */
static int
todo(void *arg)
{
	Urp *up;

	up = (Urp *)arg;
	return (WINDOW(up)>0 && up->wq->len>0 && !(up->state&INITING));
}
static void
urpkproc(void *arg)
{
	Urp *up;

	up = (Urp *)arg;
	print("urpkproc started\n");

	for(;;){
		if(up->state & (HUNGUP|CLOSING)){
			if(isflushed(up))
				wakeup(&up->r);
			if(up->state & HUNGUP)
				break;
		}
		if((up->lastecho&Nmask)!=up->iseq && up->rq->next->len<Streamhi)
			sendctl(up, up->lastecho = ECHO+up->iseq);
		output(up);
		tsleep(&up->rq->r, todo, up, MSrexmit/2);
	}
	DPRINT("urpkproc exiting %ux\n", up);
	up->kstarted = 0;
	up->state = 0;
}

M gnot/sysproc.c => gnot/sysproc.c +11 -5
@@ 51,7 51,7 @@ sysfork(ulong *arg)
	s = &p->seg[SSEG];
	s->proc = p;
	on = (s->maxva-s->minva)>>PGSHIFT;
	usp = ((Ureg*)UREGADDR)->sp;
	usp = ((Ureg*)UREGADDR)->usp;
	if(usp >= USTKTOP)
		panic("fork bad usp %lux", usp);
	if(usp < u->p->seg[SSEG].minva)


@@ 174,21 174,27 @@ sysexec(ulong *arg)
	}
	if(!indir)
		strcpy(elem, u->elem);
print("offset %lux\n", tc->offset);
	n = (*devtab[tc->type].read)(tc, &exec, sizeof(Exec));
	if(n < 2)
	if(n < 2){
		print("short read\n");
    Err:
		error(0, Ebadexec);
	}
	if(n==sizeof(Exec) && exec.magic==A_MAGIC){
		if((exec.text&KZERO)
		|| (ulong)exec.entry < UTZERO+sizeof(Exec)
		|| (ulong)exec.entry >= UTZERO+sizeof(Exec)+exec.text)
		|| (ulong)exec.entry >= UTZERO+sizeof(Exec)+exec.text){
			print("bad header sizes\n");
			goto Err;
		}
		goto Binary;
	}

	/*
	 * Process #! /bin/sh args ...
	 */
print("do #! magic=%lux size=%d\n", exec.magic, n);
	memcpy(line, &exec, sizeof(Exec));
	if(indir || line[0]!='#' || line[1]!='!')
		goto Err;


@@ 347,10 353,10 @@ sysexec(ulong *arg)
	unlock(o);

	flushmmu();
	((Ureg*)UREGADDR)->pc = exec.entry - 4;
	((Ureg*)UREGADDR)->pc = exec.entry;
	sp = (ulong*)(USTKTOP - ssize);
	*--sp = nargs;
	((Ureg*)UREGADDR)->sp = (ulong)sp;
	((Ureg*)UREGADDR)->usp = (ulong)sp;
	lock(&p->debug);
	u->nnote = 0;
	u->notify = 0;

M gnot/trap.c => gnot/trap.c +1 -0
@@ 244,6 244,7 @@ syscall(Ureg *aur)

	u->p->insyscall = 1;
	ur = aur;
	u->p->pc = ur->pc;
	if(ur->sr & SUPER)
		panic("recursive system call");
#ifdef asdf

M port/devcons.c => port/devcons.c +7 -3
@@ 323,7 323,8 @@ readnum(ulong off, char *buf, ulong n, ulong val, int size)

	numbconv(&op, 10);
	tmp[size-1] = ' ';
	off %= size;
	if(off >= size)
		return 0;
	if(off+n > size)
		n = size-off;
	memcpy(buf, tmp+off, n);


@@ 336,7 337,8 @@ readstr(ulong off, char *buf, ulong n, char *str)
	int size;

	size = strlen(str);
	off %= size;
	if(off >= size)
		return 0;
	if(off+n > size)
		n = size-off;
	memcpy(buf, str+off, n);


@@ 455,7 457,9 @@ consread(Chan *c, void *buf, long n)
		return i;

	case Qcputime:
		k = c->offset % sizeof tmp;
		k = c->offset;
		if(k >= sizeof tmp)
			return 0;
		if(k+n > sizeof tmp)
			n = sizeof tmp - k;
		/* easiest to format in a separate buffer and copy out */

A port/devdk.c => port/devdk.c +1459 -0
@@ 0,0 1,1459 @@
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"errno.h"

#define NOW (MACHP(0)->ticks)
#define DPRINT if(0)

enum {
	/*
	 *  configuration parameters
	 */
	Ndk = 2,		/* max dks */

	/*
	 *  relative or immutable
	 */
	Nline = 256,		/* max lines per dk */
	Ndir = Nline + 1,	/* entries in the dk directory */
	Nsubdir = 5,		/* entries in the sub directory */
};

typedef struct Dkmsg	Dkmsg;
typedef struct Line	Line;
typedef struct Dk	Dk;

/*
 *  types of possible dkcalls
 */
enum {
	Dial,
	Announce,
	Redial
};

/*
 *  format of messages to/from the datakit controller on the common
 *  signalling line
 */
struct Dkmsg {
	uchar	type;
	uchar	srv;
	uchar	param0l;
	uchar	param0h;
	uchar	param1l;
	uchar	param1h;
	uchar	param2l;
	uchar	param2h;
	uchar	param3l;
	uchar	param3h;
	uchar	param4l;
	uchar	param4h;
};

/*
 *  message codes (T_xxx == dialin.type, D_xxx == dialin.srv)
 */
#define	T_SRV	1		/* service request */
#define   D_SERV	1		/* (host to dkmux) announce a service */
#define   D_DIAL	2		/* (host to dkmux) connect to a service */
#define   D_XINIT	7		/* (dkmux to host) line has been spliced */
#define	T_REPLY	2		/* reply to T_SRV/D_SERV or T_SRV/D_DIAL */
#define	  D_OK		1		/* not used */
#define	  D_OPEN	2		/* (dkmux to host) connection established */
#define	  D_FAIL	3		/* (dkmux to host) connection failed */
#define	T_CHG	3		/* linege the status of a connection */
#define	  D_CLOSE	1		/* close the connection */
#define	  D_ISCLOSED	2		/* (dkmux to host) confirm a close */
#define	  D_CLOSEALL	3		/* (dkmux to host) close all connections */
#define	  D_REDIAL	6		/* (host to dkmux) redial a call */
#define	T_ALIVE	4		/* (host to dkmux) keep alive message */
#define	  D_CONTINUE	0		/* host has not died since last msg */
#define	  D_RESTART	1		/* host has restarted */
#define   D_MAXCHAN	2		/* request maximum line number */
#define	T_RESTART 8		/* (dkmux to host) datakit restarted */

/*
 *  macros for cracking/forming the window negotiation parameter
 */
#define MIN(x,y)  (x < y ? x : y)
#define W_WINDOW(o,d,t)  ((o<<8) | (d<<4) | t | 0100000)
#define W_VALID(x)  ((x) & 0100000)
#define W_ORIG(x)  (((x)>>8) & 017)
#define W_DEST(x)  (((x)>>4) & 017)
#define W_TRAF(x)  ((x) & 017)
#define W_DESTMAX(x,y)  (W_WINDOW(W_ORIG(x),MIN(W_DEST(x),y),W_TRAF(x)))
#define W_LIMIT(x,y)  (W_WINDOW(MIN(W_ORIG(x),y),MIN(W_DEST(x),y),W_TRAF(x)))
#define	W_VALUE(x)	(1<<((x)+4))
#define WS_2K	7

/*
 *  one per datakit line
 */
struct Line {
	QLock;
	Rendez	r;		/* wait here for dial */
	int	state;		/* dial state */
	int	err;		/* dialing error (if non zero) */
	int	window;		/* negotiated window */
	int	timestamp;	/* timestamp of last call received on this line */
	int	calltolive;	/* multiple of 15 seconds for dialing state to last */
	Queue	*rq;
	char	addr[64];
	char	raddr[64];
	char	ruser[32];
	char	other[64];
	Dk *dp;			/* interface contianing this line */
};

/*
 *  a dkmux dk.  one exists for every stream that a 
 *  dkmux line disilpline is pushed onto.
 */
struct Dk {
	QLock;
	int	ref;
	char	name[64];	/* dk name */	
	Queue	*wq;		/* dk output queue */
	int	lines;		/* number of lines */
	int	ncsc;		/* csc line number */
	Chan	*csc;		/* common signalling line */
	Line	line[Nline];
};
static Dk dk[Ndk];

/*
 *  conversation states (for Line.state)
 */
typedef enum {
	Lclosed=0,
	Lopened,		/* opened but no call out */
	Lconnected,		/* opened and a call set up on htis line */
	Lrclose,		/* remote end has closed down */
	Llclose,		/* local end has closed down */
	Ldialing,		/* dialing a new call */
	Llistening,		/* this line listening for calls */
	Lackwait,		/* incoming call waiting for ack/nak */
	Laccepting,		/* waiting for user to accept or reject the call */
} Lstate;

/*
 *  datakit error to errno 
 */
enum {
	DKok,
	DKbusy,
	DKnetotl,
	DKdestotl,
	DKbadnet,
	DKnetbusy,
	DKinuse,
	DKreject,
};
int dkerr[]={
	[DKok]Egreg,
	[DKbusy]Einuse,		/* destination busy */
	[DKnetotl]Enetotl,	/* network not answering */
	[DKdestotl]Edestotl,	/* destination not answering */ 
	[DKbadnet]Ebadnet,	/* unassigned destination */
	[DKnetbusy]Enetbusy,	/* network overload */
	[DKinuse]Einuse,	/* server already exists */
	[DKreject]Erejected	/* call rejected by destination */
};
#define DKERRS sizeof(dkerr)/sizeof(int)

/*
 *  imported
 */
extern Qinfo urpinfo;

/*
 *  predeclared
 */
Chan*		dkattach(char*);
static void	dkmuxconfig(Dk*, Block*);
static int	dkmesg(Dk*, int, int, int, int);
static void	dkcsckproc(void*);
static int	dklisten(Chan*);
static void	dkanswer(Chan*, int, int);
static void	dkwindow(Chan*);
static void	dkcall(int, Chan*, char*, char*, char*);
static void	dktimer(void*);
static void	dkchgmesg(Dk*, Dkmsg*, int);
static void	dkreplymesg(Dk*, Dkmsg*, int);
Chan*		dkopen(Chan*, int);

/*
 *  the datakit multiplexor stream module definition
 */
static void dkmuxopen(Queue *, Stream *);
static void dkmuxclose(Queue *);
static void dkmuxoput(Queue *, Block *);
static void dkmuxiput(Queue *, Block *);
Qinfo dkmuxinfo = { dkmuxiput, dkmuxoput, dkmuxopen, dkmuxclose, "dkmux" };

/*
 *  a new dkmux.  find a free dk structure and assign it to this queue.
 */
static void
dkmuxopen(Queue *q, Stream *s)
{
	Dk *dp;
	int i;

	for(dp = dk; dp < &dk[Ndk]; dp++){
		if(dp->wq == 0){
			qlock(dp);
			if(dp->wq) {
				/* someone was faster than us */
				qunlock(dp);
				continue;
			}
			q->ptr = q->other->ptr = (void *)dp;
			dp->csc = 0;
			dp->ncsc = 4;
			dp->lines = 16;
			dp->name[0] = 0;
			dp->wq = WR(q);
			qunlock(dp);
			return;
		}
	}
	error(0, Enoifc);
}

/*
 *  close down a dkmux
 */
static void
dkmuxclose(Queue *q)
{
	Dk *dp;

	dp = (Dk *)q->ptr;
	qlock(dp);
	if(dp->csc)
		close(dp->csc);
	dp->wq = 0;
	qunlock(dp);
}

/*
 *  handle configuration
 */
static void
dkmuxoput(Queue *q, Block *bp)
{
	Dk *dp;

	dp = (Dk *)q->ptr;
	if(bp->type != M_DATA){
		if(streamparse("config", bp))
			dkmuxconfig(dp, bp);
		else
			PUTNEXT(q, bp);
		return;
	}
	PUTNEXT(q, bp);
}

/*
 *  gather a message and send it up the appropriate stream
 *
 *  The first two bytes of each message contains the channel
 *  number, low order byte first.
 *
 *  Simplifying assumption:  one put == one message && the channel number
 *	is in the first block.  If this isn't true, demultiplexing will not
  *	work.
 */
static void
dkmuxiput(Queue *q, Block *bp)
{
	Dk *dp;
	Line *lp;
	int line;

	dp = (Dk *)q->ptr;
	if(bp->type != M_DATA){
		PUTNEXT(q, bp);
		return;
	}

	line = *bp->rptr++ | (*bp->rptr++<<8);
	if(line<0 || line>=dp->lines){
		print("dkmuxiput bad line %d\n", line);
		freeb(bp);
		return;
	}

	lp = &dp->line[line];
	if(canqlock(lp)){
		if(lp->rq)
			PUTNEXT(lp->rq, bp);
		else{
			print("dkmuxiput unopened line %d\n", line);
			freeb(bp);
		}
		qunlock(lp);
	} else {
		print("dkmuxiput unopened line %d\n", line);
		freeb(bp);
	}
}

/*
 *  the datakit line stream module definition
 */
static void dkstopen(Queue *, Stream *);
static void dkstclose(Queue *);
static void dkoput(Queue *, Block *);
static void dkiput(Queue *, Block *);
Qinfo dkinfo = { dkiput, dkoput, dkstopen, dkstclose, "dk" };

/*
 *  open and save a pointer to the conversation
 */
static void
dkstopen(Queue *q, Stream *s)
{
	Dk *dp;
	Line *lp;

	dp = &dk[s->dev];
	q->other->ptr = q->ptr = lp = &dp->line[s->id];
	lp->dp = dp;
	lp->rq = q;
}

/*
 *  close down a datakit conversation
 */
static void
dkstclose(Queue *q)
{
	Dk *dp;
	Line *lp;

	lp = (Line *)q->ptr;
	dp = lp->dp;

	/*
	 *  shake hands with dk
	 */
	switch(lp->state){
	case Lclosed:
	case Llclose:
		break;

	case Lrclose:
		dkmesg(dp, T_CHG, D_CLOSE, lp - dp->line, 0);
		lp->state = Lclosed;
		break;

	case Lackwait:
		dkmesg(dp, T_CHG, D_CLOSE, lp - dp->line, 0);
		lp->state = Llclose;
		break;

	case Llistening:
		dkmesg(dp, T_CHG, D_CLOSE, lp - dp->line, 0);
		lp->state = Llclose;
		break;

	case Lconnected:
		dkmesg(dp, T_CHG, D_CLOSE, lp - dp->line, 0);
		lp->state = Llclose;
		break;

	case Lopened:
		lp->state = Lclosed;
	}

	qlock(lp);
	lp->rq = 0;
	qunlock(lp);
}

/*
 *  this is only called by hangup
 */
static void
dkiput(Queue *q, Block *bp)
{
	PUTNEXT(q, bp);
}

/*
 *  we assume that each put is a message.
 *
 *  add a 2 byte channel number to the start of each message
 */
static void
dkoput(Queue *q, Block *bp)
{
	Line *lp;
	Dk *dp;
	int line;

	if(bp->type != M_DATA){
		freeb(bp);
		error(0, Ebadarg);
	}

	lp = (Line *)q->ptr;
	dp = lp->dp;
	line = lp - dp->line;

	if(bp->base && bp->rptr - bp->base >= 2)
		bp->rptr -= 2;
	bp->rptr[0] = line;
	bp->rptr[1] = line>>8;

	PUTNEXT(dp->wq, bp);
}

/*
 *  configure a datakit multiplexor.  this takes 3 arguments separated
 *  by spaces:
 *	the line number of the common signalling channel (must be > 0)
 *	the number of lines in the device (optional)
 *	the name of the dk (optional)
 *
 *  we can configure only once
 */
static void
dkmuxconfig(Dk *dp, Block *bp)
{
	Chan *c;
	char *fields[3];
	int n;
	char buf[64];
	static int dktimeron;

	if(dp->csc != 0){
		freeb(bp);
		error(0, Ebadarg);
	}

	/*
	 *  parse
	 */
	n = getfields((char *)bp->rptr, fields, 3, ' ');
	switch(n){
	case 3:
		strncpy(dp->name, fields[2], sizeof(dp->name));
	case 2:
		dp->lines = strtoul(fields[1], 0, 0);
	case 1:
		dp->ncsc = strtoul(fields[0], 0, 0);
		break;
	default:
		freeb(bp);
		error(0, Ebadarg);
	}
	freeb(bp);
	if(dp->ncsc <= 0 || dp->lines <= dp->ncsc){
		dp->lines = 16;
		error(0, Ebadarg);
	}

	/*
	 *  open a stream for the csc and push urp onto it
	 */
	c = 0;
	if(waserror()){
		if(c)
			close(c);
		nexterror();
	}
	c = dkattach(dp->name);
	c->qid = STREAMQID(dp->ncsc, Sdataqid);
	dkopen(c, ORDWR);
	dp->csc = c;

	/*
	 *  start a process to deal with it
	 */
	sprint(buf, "**csckproc%d**", dp->ncsc);
	kproc(buf, dkcsckproc, dp);
	poperror();

	/*
	 *  start a keepalive process if one doesn't exist
	 */
	if(dktimeron == 0){
		dktimeron = 1;
		kproc("**dktimer**", dktimer, 0);
	}
}

/*
 *  qid's
 */
enum {
	/*
	 *  per line
	 */
	Daddrqid,
	Dlistenqid,
	Draddrqid,
	Duserqid,
	Dotherqid,
	Dlineqid,

	/*
	 *  per device
	 */
	Dcloneqid,
};

/*
 *  the dk directory
 */
Dirtab dkdir[Ndir];

/*
 *  the per stream directory structure
 */
Dirtab dksubdir[]={
	"addr",		Daddrqid,	0,	0600,
	"listen",	Dlistenqid,	0,	0600,
	"other",	Dotherqid,	0,	0600,
	"raddr",	Draddrqid,	0, 	0600,
	"ruser",	Duserqid,	0, 	0600,
};

/*
 *  dk file system.  most of the calls use dev.c to access the dk
 *  directory and stream.c to access the dk devices.
 */
void
dkreset(void)
{
}

/*
 *  create the dk directory.  the files are `clone' and stream
 *  directories '1' to '32' (or whatever Nline is in decimal)
 */
void
dkinit(void)
{
	int i;

	/*
	 *  create the directory.
	 */
	/*
	 *  the circuits
	 */
	for(i = 1; i < Nline; i++) {
		sprint(dkdir[i].name, "%d", i);
		dkdir[i].qid = CHDIR|STREAMQID(i, Dlineqid);
		dkdir[i].length = 0;
		dkdir[i].perm = 0600;
	}

	/*
	 *  the clone device
	 */
	strcpy(dkdir[0].name, "clone");
	dkdir[0].qid = Dcloneqid;
	dkdir[0].length = 0;
	dkdir[0].perm = 0600;
}

Chan*
dkattach(char *spec)
{
	Chan *c;
	Dk *dp;

	/*
	 *  find a multiplexor with the same name
	 */
	for(dp = dk; dp < &dk[Ndk]; dp++){
		qlock(dp);
		if(dp->wq && strcmp(spec, dp->name)==0) {
			dp->ref++;
			qunlock(dp);
			break;
		}
		qunlock(dp);
	}
	if(dp == &dk[Ndk])
		error(0, Enoifc);
	c = devattach('k', spec);
	c->dev = dp - dk;

	return c;
}

Chan*
dkclone(Chan *c, Chan *nc)
{
	Dk *dp;

	dp = &dk[c->dev];
	qlock(dp);
	dp->ref++;
	qunlock(dp);
	return devclone(c, nc);
}

int	 
dkwalk(Chan *c, char *name)
{
	if(c->qid == CHDIR)
		return devwalk(c, name, dkdir, dk[c->dev].lines, devgen);
	else
		return devwalk(c, name, dksubdir, Nsubdir, streamgen);
}

void	 
dkstat(Chan *c, char *dp)
{
	if(c->qid == CHDIR)
		devstat(c, dp, dkdir, dk[c->dev].lines, devgen);
	else
		devstat(c, dp, dksubdir, Nsubdir, streamgen);
}

/*
 *  opening a dk device allocates a Line.  Opening the `clone'
 *  device is a ``macro'' for finding a free Line and opening
 *  it's ctl file.
 *
 *  opening the `listen' sub device is a macro for listening for
 *  a new call.  Lile `clone' the ctl file of the new channel is
 *  returned.
 */
Chan*
dkopen(Chan *c, int omode)
{
	extern Qinfo dkinfo;
	Stream *s;
	Line *lp, *end;
	Dk *dp;
	int line;

	if(c->qid == Dcloneqid){
		/*
		 *  get an unused device and open it's control file
		 */
		dp = &dk[c->dev];
		end = &dp->line[dp->lines];
		for(lp = &dp->line[dp->ncsc+1]; lp < end; lp++){
			if(lp->state == Lclosed && canqlock(lp)){
				if(lp->state != Lclosed){
					qunlock(lp);
					continue;
				}
				c->qid = STREAMQID(lp-dp->line, Sctlqid);
				qunlock(lp);
				break;
			}
		}
		if(lp == end)
			error(0, Enodev);
		streamopen(c, &dkinfo);
		pushq(c->stream, &urpinfo);
	} else if(STREAMTYPE(c->qid) == Dlistenqid){
		/*
		 *  listen for a call and open the control file for the
		 *  channel on which the call arrived.
		 */
		line = dklisten(c);
		c->qid = STREAMQID(line, Sctlqid);
		streamopen(c, &dkinfo);
		pushq(c->stream, &urpinfo);
		dkwindow(c);
	} else if(c->qid != CHDIR){
		/*
		 *  open whatever c points to, make sure it has an urp
		 */
		streamopen(c, &dkinfo);
		if(strcmp(c->stream->procq->next->info->name, "urp")!=0)
			pushq(c->stream, &urpinfo);
	}

	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

void	 
dkcreate(Chan *c, char *name, int omode, ulong perm)
{
	error(0, Eperm);
}

void	 
dkclose(Chan *c)
{
	Dk *dp;

	/* real closing happens in lancestclose */
	if(c->qid != CHDIR)
		streamclose(c);

	dp = &dk[c->dev];
	qlock(dp);
	dp->ref--;
	qunlock(dp);
}

long	 
dkread(Chan *c, void *a, long n)
{
	int t;
	Line *lp;

	t = STREAMTYPE(c->qid);
	if(t>=Slowqid || t==Dlineqid)
		return streamread(c, a, n);
	if(c->qid == CHDIR)
		return devdirread(c, a, n, dkdir, dk[c->dev].lines, devgen);

	lp = &dk[c->dev].line[STREAMID(c->qid)];
	switch(t){
	case Daddrqid:
		return stringread(c, a, n, lp->addr);
	case Draddrqid:
		return stringread(c, a, n, lp->raddr);
	case Duserqid:
		return stringread(c, a, n, lp->ruser);
	}
	error(0, Eperm);
}

long	 
dkwrite(Chan *c, void *a, long n)
{
	int t;
	char buf[256];
	char *field[5];
	int m;

	t = STREAMTYPE(c->qid);

	/*
	 *  get data dispatched as quickly as possible
	 */
	if(t == Sdataqid)
		return streamwrite(c, a, n, 0);

	/*
	 *  easier to do here than in dkoput
	 */
	if(t == Sctlqid){
		strncpy(buf, a, sizeof buf);
		m = getfields(buf, field, 5, ' ');
		if(strcmp(field[0], "connect")==0){
			if(m < 2)
				error(0, Ebadarg);
			dkcall(Dial, c, field[1], 0, 0);
		} else if(strcmp(field[0], "announce")==0){
			if(m < 2)
				error(0, Ebadarg);
			dkcall(Announce, c, field[1], 0, 0);
		} else if(strcmp(field[0], "redial")==0){
			if(m < 4)
				error(0, Ebadarg);
			dkcall(Redial, c, field[1], field[2], field[3]);
		} else if(strcmp(field[0], "accept")==0){
			if(m < 2)
				error(0, Ebadarg);
			dkanswer(c, strtoul(field[1], 0, 0), 0);
		} else if(strcmp(field[0], "reject")==0){
			if(m < 3)
				error(0, Ebadarg);
			dkanswer(c, strtoul(field[1], 0, 0), strtoul(field[2], 0, 0));
		} else
			return streamwrite(c, a, n, 0);
		return n;
	}

	if(t >= Slowqid)
		return streamwrite(c, a, n, 0);

	error(0, Eperm);
}

void	 
dkremove(Chan *c)
{
	error(0, Eperm);
}

void	 
dkwstat(Chan *c, char *dp)
{
	error(0, Eperm);
}

void	 
dkerrstr(Error *e, char *buf)
{
	rooterrstr(e, buf);
}

void	 
dkuserstr(Error *e, char *buf)
{
	extern consuserstr(Error *, char *);

	consuserstr(e, buf);
}

/*
 *  send a message to the datakit on the common signaling line
 */
static int
dkmesg(Dk *dp, int type, int srv, int p0, int p1)
{
	Dkmsg d;
	Block *bp;

	if(dp->csc == 0)
		return -1;
	if(waserror())
		return -1;
	d.type = type;
	d.srv = srv;
	d.param0l = p0;
	d.param0h = p0>>8;
	d.param1l = p1;
	d.param1h = p1>>8;
	d.param2l = 0;
	d.param2h = 0;
	d.param3l = 0;
	d.param3h = 0;
	d.param4l = 0;
	d.param4h = 0;
	streamwrite(dp->csc, (char *)&d, sizeof(Dkmsg), 1);
	poperror();
	return 0;
}

/*
 *  call out on a datakit
 */
static int
calldone(void *a)
{
	Line *lp;

	lp = (Line *)a;
	return lp->state != Ldialing;
}
static void
dkcall(int type, Chan *c, char *addr, char *nuser, char *machine)
{
 	char dialstr[66];
	int line;
	char dialtone;
	int t_val, d_val;
	Dk *dp;
	Line *lp;
	Chan *dc;

	line = STREAMID(c->qid);
	dp = &dk[c->dev];
	lp = &dp->line[line];

	/*
	 *  only dial on virgin lines
	 */
	if(lp->state != Lclosed)
		error(0, Ebadarg);

	DPRINT("dkcall(line=%d, type=%d, dest=%s)\n", line, type, addr);

	/*
	 *  build dial string (guard against new lines)
	 */
	if(strchr(addr, '\n'))
		error(0, Ebadarg);
	if(strlen(addr)+strlen(u->p->pgrp->user)+2 >= sizeof(dialstr))
		error(0, Ebadarg);
	strcpy(dialstr, addr);
	switch(type){
	case Dial:
		t_val = T_SRV;
		d_val = D_DIAL;
		strcat(dialstr, "\n");
		strcat(dialstr, u->p->pgrp->user);
		strcat(dialstr, "\n");
		break;
	case Announce:
		t_val = T_SRV;
		d_val = D_SERV;
		break;
	case Redial:
		t_val = T_CHG;
		d_val = D_REDIAL;
		strcat(dialstr, "\n");
		strcat(dialstr, nuser);
		strcat(dialstr, "\n");
		strcat(dialstr, machine);
		strcat(dialstr, "\n");
		break;
	}

	/*
	 *  open the data file
	 */
	dc = dkattach(dp->name);
	if(waserror()){
		close(dc);
		nexterror();
	}
	dc->qid = STREAMQID(line, Sdataqid);
	dkopen(dc, ORDWR);

	lp->calltolive = 4;
	lp->state = Ldialing;

	/*
	 *  tell the controller we want to make a call
	 */
	DPRINT("dialout\n");
	dkmesg(dp, t_val, d_val, line, W_WINDOW(WS_2K,WS_2K,2));

	/*
	 *  if redial, wait for a dial tone (otherwise we might send
	 *  the dialstr to the previous other end and not the controller)
	 */
	if(type==Redial){
		if(streamread(dc, &dialtone, 1L) != 1L){
			lp->state = Lconnected;
			error(0, Ebadarg);
		}
	}

	/*
	 *  make the call
	 */
	DPRINT("dialstr %s\n", dialstr);
	streamwrite(dc, dialstr, (long)strlen(dialstr), 1);
	close(dc);
	poperror();

	/*
	 *  redial's never get a reply, assume it worked
	 */
	if(type == Redial) {
		lp->state = Lconnected;
		return;
	}

	/*
	 *  wait for a reply
	 */
	DPRINT("reply wait\n");
	sleep(&lp->r, calldone, lp);

	/*
	 *  if there was an error, translate it to a plan 9
	 *  errno and report it to the user.
	 */
	DPRINT("got reply %d\n", lp->state);
	if(lp->state != Lconnected) {
		if(lp->err >= DKERRS)
			error(0, dkerr[0]);
		else
			error(0, dkerr[lp->err]);
	}

	/*
	 *  linege state if serving
	 */
	if(type == D_SERV){
		lp->state = Llistening;
	}
	DPRINT("connected!\n");

	/*
	 *  decode the window size
	 */
	if (W_VALID(lp->window)){
		/*
		 *  a 1127 window negotiation
		 */
		lp->window = W_VALUE(W_DEST(lp->window));
	} else if(lp->window>2 && lp->window<31){
		/*
		 *  a generic window negotiation
		 */
		lp->window = 1<<lp->window;
	} else
		lp->window = 0;

	/*
	 *  tag the connection
	 */
	strncpy(lp->addr, addr, sizeof(lp->addr)-1);
	strncpy(lp->raddr, addr, sizeof(lp->raddr)-1);

	/*
	 *  reset the protocol
	 */
	dkwindow(c);
}

/*
 *  listen for a call, reflavor the 
 */
static int
dklisten(Chan *c)
{
	char dialstr[512];
	char *line[12];
	char *field[8];
	Line *lp;
	Dk *dp;
	int n, lineno, ts, window;
	Chan *dc;

	dp = &dk[c->dev];

	/*
	 *  open the data file
	 */
	dc = dkattach(dp->name);
	if(waserror()){
		close(dc);
		nexterror();
	}
	dc->qid = STREAMQID(STREAMID(c->qid), Sdataqid);
	dkopen(dc, ORDWR);

	/*
	 *  wait for a call in
	 */
	for(;;){
		/*
		 *  read the dialstring and null terminate it
		 */
		n = streamread(dc, dialstr, sizeof(dialstr)-1);
		DPRINT("returns %d\n", n);
		if(n <= 0)
			error(0, Eio);
		dialstr[n] = 0;
		DPRINT("dialstr = %s\n", dialstr);

		/*
		 *  break the dial string into lines
		 */
		n = getfields(dialstr, line, 12, '\n');
		if (n < 2) {
			DPRINT("bad dialstr from dk (1 line)\n");
			error(0, Eio);
		}

		/*
		 * line 0 is `line.tstamp.traffic[.urpparms.window]'
		 */
		window = 0;
		switch(getfields(line[0], field, 5, '.')){
		case 5:
			/*
			 *  generic way of passing window
			 */
			window = strtoul(field[4], 0, 0);
			if(window > 0 && window <31)
				window = 1<<window;
			else
				window = 0;
			/*
			 *  intentional fall through
			 */
		case 3:
			/*
			 *  1127 way of passing window
			 */
			if(window == 0){
				window = strtoul(field[2], 0, 0);
				if(W_VALID(window))
					window = W_VALUE(W_ORIG(window));
				else
					window = 0;
			}
			break;
		default:
			print("bad message from dk(bad first line)\n");
			continue;
		}
		lineno = strtoul(field[0], 0, 0);
		if(lineno >= dp->lines){
			print("dklisten: illegal line %d\n", lineno);
			continue;
		}
		lp = &dp->line[lineno];
		ts = strtoul(field[1], 0, 0);

		/*
		 *  this could be a duplicate request
		 */
		if(ts == lp->timestamp){
			print("dklisten: repeat timestamp %d\n", lineno);
			continue;
		}
	
		/*
		 *  take care of glare (datakit picked an inuse channel
		 *  for the call to come in on).
		 */
		if(!canqlock(lp)){
			print("DKbusy1\n");
			dkanswer(c, lineno, DKbusy);
			continue;
		} else {
			if(lp->state != Lclosed){
				qunlock(lp);
				print("DKbusy2 %ux\n", lp->state);
				dkanswer(c, lineno, DKbusy);
				continue;
			}
		}
		lp->window = window;

		/*
		 *  Line 1 is `my-dk-name.service[.more-things]'.
		 *  Special characters are escaped by '\'s.
		 */
		strncpy(lp->addr, line[1], sizeof(lp->addr)-1);
	
		/*
		 *  the rest is variable length
		 */
		switch(n) {
		case 2:
			/* no more lines */
			lp->ruser[0] = 0;
			lp->raddr[0] = 0;
			break;
		case 3:
			/* line 2 is `source.user.param1.param2' */
			getfields(line[2], field, 3, '.');
			strncpy(lp->raddr, field[0], sizeof(lp->raddr)-1);
			strncpy(lp->ruser, field[1], sizeof(lp->ruser)-1);
			break;
		case 4:
			/* line 2 is `user.param1.param2' */
			getfields(line[2], field, 2, '.');
			strncpy(lp->ruser, field[0], sizeof(lp->ruser)-1);
	
			/* line 3 is `source.node.mod.line' */
			strncpy(lp->raddr, line[3], sizeof(lp->raddr)-1);
			break;
		default:
			print("bad message from dk(>4 line)\n");
			qunlock(lp);
			error(0, Ebadarg);
		}

		sprint(lp->other, "w(%d)", W_TRAF(lp->window));
		DPRINT("src(%s)user(%s)dest(%s)other(%s)\n", lp->raddr, lp->ruser,
			lp->addr, lp->other);

		lp->timestamp = ts;
		lp->state = Lconnected;
		qunlock(lp);
		close(dc);
		poperror();
		DPRINT("dklisten returns %d\n", lineno);
		return lineno;
	}
}

/*
 *  answer a call
 */
static void
dkanswer(Chan *c, int line, int code)
{
	char reply[64];
	Dk *dp;
	Chan *dc;
	Line *lp;

	dp = &dk[c->dev];
	lp = &dp->line[line];

	/*
	 *  open the data file (c is a control file)
	 */
	dc = dkattach(dp->name);
	if(waserror()){
		close(dc);
		nexterror();
	}
	dc->qid = STREAMQID(STREAMID(c->qid), Sdataqid);
	dkopen(dc, ORDWR);

	/*
	 *  send the reply
	 */
	sprint(reply, "%ud.%ud.%ud", line, lp->timestamp, code);
	DPRINT("dkanswer %s\n", reply);
	streamwrite(dc, reply, strlen(reply), 1);
	close(dc);
	poperror();
}

/*
 *  set the window size and reset the protocol
 */
static void
dkwindow(Chan *c)
{
	char buf[64];
	long wins;
	Line *lp;

	lp = &dk[c->dev].line[STREAMID(c->qid)];
	if(lp->window == 0)
		lp->window = 64;
	sprint(buf, "init %d %d", lp->window, Streamhi);
	streamwrite(c, buf, strlen(buf), 1);
}

/*
 *  hangup a datakit connection
 */
static void
dkhangup(Line *lp)
{
	Block *bp;

	qlock(lp);
	if(lp->rq){
		bp = allocb(0);
		bp->type = M_HANGUP;
		PUTNEXT(lp->rq, bp);
	}
	qunlock(lp);
}

/*
 *  A process which listens to all input on a csc line
 */
static void
dkcsckproc(void *a)
{
	long n;
	Dk *dp;
	Dkmsg d;
	int line;
	int i;

	dp = (Dk *)a;

	/*
	 *  loop forever listening
	 */
	for(;;){
		n = streamread(dp->csc, (char *)&d, (long)sizeof(d));
		if(n != sizeof(d)){
			print("strange csc message %d\n", n);
			continue;
		}
		line = (d.param0h<<8) + d.param0l;
/*		print("t(%d)s(%d)l(%d)\n", d.type, d.srv, line); /**/
		switch (d.type) {

		case T_CHG:	/* controller wants to close a line */
			dkchgmesg(dp, &d, line);
			break;
		
		case T_REPLY:	/* reply to a dial request */
			dkreplymesg(dp, &d, line);
			break;
		
		case T_SRV:	/* ignore it, it's useless */
			print("dksrvmesg(%d)\n", line);
			break;
		
		case T_RESTART:	/* datakit reboot */
			print("dk restart\n");
			if(line >=0 && line<dp->lines){
				print("maxlines=%d\n", line+1);
				dp->lines=line+1;
			}
			break;
		
		default:
			DPRINT("unrecognized csc message %o(%o)\n", d.type, line);
			break;
		}
	}
}

/*
 *  datakit requests or confirms closing a line
 */
static void
dkchgmesg(Dk *dp, Dkmsg *dialp, int line)
{
	Line *lp;

	if (line <= 0 || line >= dp->lines) {
		/* tell controller this line is not in use */
		dkmesg(dp, T_CHG, D_CLOSE, line, 0);
		return;
	}
	lp = &dp->line[line];
	switch (dialp->srv) {

	case D_CLOSE:		/* remote shutdown */
		switch (lp->state) {

		case Ldialing:
			/* simulate a failed connection */
			dkreplymesg(dp, (Dkmsg *)0, line);
			lp->state = Lrclose;
			break;

		case Lrclose:
		case Lconnected:
		case Llistening:
		case Lackwait:
			dkhangup(lp);
			lp->state = Lrclose;
			break;

		case Lopened:
			dkmesg(dp, T_CHG, D_CLOSE, line, 0);
			break;

		case Llclose:
		case Lclosed:
			dkhangup(lp);
			dkmesg(dp, T_CHG, D_CLOSE, line, 0);
			lp->state = Lclosed;
			break;
		}
		break;
	
	case D_ISCLOSED:	/* acknowledging a local shutdown */
		switch (lp->state) {
		case Llclose:
		case Lclosed:
			lp->state = Lclosed;
			break;

		case Lrclose:
		case Lconnected:
		case Llistening:
		case Lackwait:
			break;
		}
		break;

	default:
		print("unrecognized T_CHG\n");
	}
}

/*
 *  datakit replies to a dialout.  capture reply code and traffic parameters
 */
static void
dkreplymesg(Dk *dp, Dkmsg *dialp, int line)
{
	Proc *p;
	Line *lp;

	DPRINT("dkreplymesg(%d)\n", line);

	if(line < 0 || line >= dp->lines)
		return;

	lp=&dp->line[line];
	if(lp->state != Ldialing)
		return;

	if(dialp){
		/*
		 *  a reply from the dk
		 */
		lp->state = (dialp->srv==D_OPEN) ? Lconnected : Lrclose;
		lp->err = (dialp->param1h<<8) + dialp->param1l;
		lp->window = lp->err;
		DPRINT("dkreplymesg: %d\n", lp->state);
	} else {
		/*
		 *  a local abort
		 */
		lp->state = Lrclose;
		lp->err = 0;
	}

	if(lp->state==Lrclose){
		dkhangup(lp);
	}
	wakeup(&lp->r);
}

/*
 *  15-second timer for all interfaces
 */
static Rendez dkt;
static int
fuckit(void *a)
{
	return 0;
}
static void
dktimer(void *a)
{
	int dki, i;
	Dk *dp;
	Line *lp;

	waserror();

	for(;;){
		/*
		 *  loop through the active dks
		 */
		for(dki=0; dki<Ndk; dki++){
			dp = &dk[dki];
			if(dp->csc==0)
				continue;

			/*
			 * send keep alive
			 */
			dkmesg(dp, T_ALIVE, D_CONTINUE, 0, 0);

			/*
			 *  remind controller of dead lines and
			 *  timeout calls that take to long
			 */
			for (i=0; i<dp->lines; i++){
				lp = &dp->line[i];
				switch(lp->state){
				case Llclose:
					dkmesg(dp, T_CHG, D_CLOSE, i, 0);
					break;

				case Ldialing:
					if(lp->calltolive==0 || --lp->calltolive!=0)
						break;
					dkreplymesg(dp, (Dkmsg *)0, i);
					break;
				}
			}
		}
		tsleep(&dkt, fuckit, 0, 7500);
	}
}

M port/devlance.c => port/devlance.c +2 -2
@@ 362,7 362,7 @@ lanceoput(Queue *q, Block *bp )
	 */
	len = 0;
	while(bp = getq(q)){
		if(sizeof(Pkt) - len >= (n = bp->wptr - bp->rptr)){
		if(sizeof(Pkt) - len >= (n = BLEN(bp))){
			memcpy(((uchar *)p)+len, bp->rptr, n);
			len += n;
		} else


@@ 704,7 704,7 @@ lanceread(Chan *c, void *a, long n)
long	 
lancewrite(Chan *c, void *a, long n)
{
	return streamwrite(c, a, n);
	return streamwrite(c, a, n, 0);
}

void	 

M port/devmnt.c => port/devmnt.c +1 -0
@@ 468,6 468,7 @@ mntremove(Chan *c)
		mhfree(mh);
		nexterror();
	}
	decref(m);
	mh->thdr.type = Tremove;
	mh->thdr.fid = c->fid;
	mntxmit(m, mh);

M port/devpipe.c => port/devpipe.c +1 -1
@@ 129,7 129,7 @@ pipewrite(Chan *c, void *va, long n)
		postnote(u->p, 1, "sys: write on closed pipe", NExit);
		error(0, Egreg);
	}
	return streamwrite(c, va, n);
	return streamwrite(c, va, n, 0);
}

void

M port/fault.c => port/fault.c +3 -1
@@ 138,6 138,7 @@ fault(Ureg *ur, int user, int code)
			 * Add to mod list
			 */
			pte = newmod();
o->nmod++;
			pte->proc = u->p;
			pte->page = opte->page;
			pte->page->ref++;


@@ 210,9 211,10 @@ validaddr(ulong addr, ulong len, int write)
	Seg *s;

	if((long)len < 0)
		panic("validaddr len %lux\n", len);
		goto Err;
	s = seg(u->p, addr);
	if(s==0 || addr+len>s->maxva || (write && (s->o->flag&OWRPERM)==0)){
    Err:
		pprint("invalid address in sys call pc %lux sp %lux\n", ((Ureg*)UREGADDR)->pc, ((Ureg*)UREGADDR)->sp);
		postnote(u->p, 1, "bad address", NDebug);
		error(0, Ebadarg);

M port/page.c => port/page.c +5 -0
@@ 287,6 287,7 @@ loop:
		o->nproc = 1;
		o->npage = 0;
		o->chan = c;
o->nmod = 0;
		if(c){
			o->type = c->type;
			o->qid = c->qid;


@@ 350,6 351,8 @@ loop:
	}
	unlock(&modalloc);
	print("no mods\n");
DEBUG();
panic("mods");
	if(u == 0)
		panic("newmod");
	u->p->state = Wakeme;


@@ 376,6 379,7 @@ forkmod(Seg *old, Seg *new, Proc *p)
if(pte->page==0) panic("forkmod zero page");
if(pte->proc != u->p) panic("forkmod wrong page");
		npte = newmod();
o->nmod++;
		npte->proc = p;
		npte->page = pte->page;
		pte->page->ref++;


@@ 435,6 439,7 @@ freesegs(int save)
				}
				pg->ref--;
				o->npage--;
o->nmod--;
				old = (PTEA*)pte;
				pte = pte->nextva;
				lock(&modalloc);

M port/proc.c => port/proc.c +9 -2
@@ 518,16 518,23 @@ proctab(int i)
#include <ureg.h>
DEBUG()
{
	int i;
	int i, j;
	Proc *p;
	Orig *o;

	print("DEBUG\n");
	for(i=0; i<conf.nproc; i++){
		p = procalloc.arena+i;
		if(p->state != Dead)
		if(p->state != Dead){
			print("%d:%s upc %lux %s ut %ld st %ld\n",
				p->pid, p->text, p->pc, statename[p->state],
				p->time[0], p->time[1]);
			for(j=0; j<NSEG; j++){
				o = p->seg[j].o;
				if(o)
					print("	s%d %d %d\n", j, o->nmod, o->npte);
			}
		}
	}
}


M port/stream.c => port/stream.c +154 -25
@@ 7,12 7,24 @@
#include	"errno.h"
#include	"devtab.h"

/*
 *  process end line discipline
 */
static void stputq(Queue*, Block*);
Qinfo procinfo = { stputq, nullput, 0, 0, "process" } ;
extern Qinfo noetherinfo;
Qinfo procinfo = { stputq, nullput, 0, 0, "process" };

/*
 *  line disciplines that can be pushed
 *
 *  WARNING: this table should be the result of configuration
 */
extern Qinfo noetherinfo;
extern Qinfo dkmuxinfo;
extern Qinfo urpinfo;
static Qinfo *lds[] = {
	&noetherinfo,
	&dkmuxinfo,
	&urpinfo,
	0
};



@@ 34,7 46,9 @@ static Lock garbagelock;
 */
typedef struct {
	int	size;
	Queue;
	Blist;
	QLock;		/* qlock for sleepers on r */
	Rendez	r;	/* sleep here waiting for blocks */
} Bclass;
Bclass bclass[Nclass]={
	{ 0 },


@@ 108,7 122,9 @@ allocb(ulong size)
	while(bcp->first == 0){
		unlock(bcp);
		print("waiting for blocks\n");
		qlock(bcp);
		sleep(&bcp->r, isblock, (void *)bcp);
		qunlock(bcp);
		lock(bcp);
	}
	bp = bcp->first;


@@ 128,25 144,33 @@ allocb(ulong size)
}

/*
 *  Free a block.  Poison its pointers so that someone trying to access
 *  it after freeing will cause a dump.
 *  Free a block (or list of blocks).  Poison its pointers so that
 *  someone trying to access it after freeing will cause a dump.
 */
void
freeb(Block *bp)
{
	Bclass *bcp;
	int tries;

	bcp = &bclass[bp->flags & S_CLASS];
	bp->rptr = bp->wptr = 0;
	lock(bcp);
	bp->rptr = bp->wptr = 0;
	if(bcp->first)
		bcp->last->next = bp;
	else
		bcp->first = bp;
	tries = 0;
	while(bp->next){
		if(++tries > 10){
			dumpstack();
			panic("freeb");
		}
		bp = bp->next;
	}
	bcp->last = bp;
	bp->next = 0;
	wakeup(&bcp->r);
	unlock(bcp);
	wakeup(&bcp->r);
}

/*


@@ 269,11 293,11 @@ putq(Queue *q, Block *bp)
		q->last->next = bp;
	else
		q->first = bp;
	q->len += bp->wptr - bp->rptr;
	q->len += BLEN(bp);
	delim = bp->flags & S_DELIM;
	while(bp->next) {
		bp = bp->next;
		q->len += bp->wptr - bp->rptr;
		q->len += BLEN(bp);
		delim |= bp->flags & S_DELIM;
	}
	q->last = bp;


@@ 292,22 316,21 @@ putb(Blist *q, Block *bp)
		q->last->next = bp;
	else
		q->first = bp;
	q->len += bp->wptr - bp->rptr;
	q->len += BLEN(bp);
	delim = bp->flags & S_DELIM;
	while(bp->next) {
		bp = bp->next;
		q->len += bp->wptr - bp->rptr;
		q->len += BLEN(bp);
		delim |= bp->flags & S_DELIM;
	}
	q->last = bp;
	bp->next = 0;
	return delim;
}

/*
 *  add a block to the start of a queue 
 */
static void
void
putbq(Blist *q, Block *bp)
{
	lock(q);


@@ 316,12 339,12 @@ putbq(Blist *q, Block *bp)
	else
		q->last = bp;
	q->first = bp;
	q->len += bp->wptr - bp->rptr;
	q->len += BLEN(bp);
	unlock(q);
}

/*
 *  remove the first block from a queue 
 *  remove the first block from a queue
 */
Block *
getq(Queue *q)


@@ 334,7 357,7 @@ getq(Queue *q)
		q->first = bp->next;
		if(q->first == 0)
			q->last = 0;
		q->len -= bp->wptr - bp->rptr;
		q->len -= BLEN(bp);
		if((q->flag&QHIWAT) && q->len < Streamhi/2){
			wakeup(&q->other->next->other->r);
			q->flag &= ~QHIWAT;


@@ 344,6 367,10 @@ getq(Queue *q)
	unlock(q);
	return bp;
}

/*
 *  remove the first block from a list of blocks
 */
Block *
getb(Blist *q)
{


@@ 354,13 381,87 @@ getb(Blist *q)
		q->first = bp->next;
		if(q->first == 0)
			q->last = 0;
		q->len -= bp->wptr - bp->rptr;
		q->len -= BLEN(bp);
		bp->next = 0;
	}
	return bp;
}

/*
 *  make sure the first block has n bytes
 */
Block *
pullup(Block *bp, int n)
{
	Block *nbp;
	int i;

	/*
	 *  this should almost always be true, the rest it
	 *  just for to avoid every caller checking.
	 */
	if(BLEN(bp) >= n)
		return bp;

	/*
	 *  if not enough room in the first block,
	 *  add another to the front of the list.
	if(bp->lim - bp->rptr < n){
		nbp = allocb(n);
		nbp->next = bp;
		bp = nbp;
	}

	/*
	 *  copy bytes from the trailing blocks into the first
	 */
	n -= BLEN(bp);
	while(nbp = bp->next){
		i = BLEN(nbp);
		if(i > n) {
			memcpy(bp->wptr, nbp->rptr, n);
			bp->wptr += n;
			nbp->rptr += n;
			return bp;
		} else {
			memcpy(bp->wptr, nbp->rptr, i);
			bp->wptr += i;
			bp->next = nbp->next;
			nbp->next = 0;
			freeb(nbp);
		}
	}
	freeb(bp);
	return 0;
}

/*
 *  grow the front of a list of blocks by n bytes
 */
Block *
prepend(Block *bp, int n)
{
	Block *nbp;

	if(bp->base && (bp->rptr - bp->base)>=n){
		/*
		 *  room for channel number in first block of message
		 */
		bp->rptr -= n;
		return bp;
	} else {
		/*
		 *  make new block, put message number at end
		 */
		nbp = allocb(2);
		nbp->next = bp;
		nbp->wptr = nbp->lim;
		nbp->rptr = nbp->wptr - n;
		return nbp;
	}
}

/*
 *  put a block into the bit bucket
 */
void


@@ 415,7 516,7 @@ streamparse(char *name, Block *bp)
	int len;

	len = strlen(name);
	if(bp->wptr - bp->rptr < len)
	if(BLEN(bp) < len)
		return 0;
	if(strncmp(name, (char *)bp->rptr, len)==0){
		if(bp->rptr[len] == ' ')


@@ 622,14 723,19 @@ stputq(Queue *q, Block *bp)
		freeb(bp);
		q->flag |= QHUNGUP;
		q->other->flag |= QHUNGUP;
		wakeup(&q->other->r);
	} else {
		lock(q);
		if(q->first)
			q->last->next = bp;
		else
			q->first = bp;
		q->len += BLEN(bp);
		while(bp->next) {
			bp = bp->next;
			q->len += BLEN(bp);
		}
		q->last = bp;
		q->len += bp->wptr - bp->rptr;
		if(q->len >= Streamhi)
			q->flag |= QHIWAT;
		unlock(q);


@@ 651,8 757,7 @@ stringread(Chan *c, uchar *buf, long n, char *str)
		n = i;
	if(n<0)
		return 0;
	memcpy(buf + c->offset, str, n);
	c->offset += n;
	memcpy(buf, str + c->offset, n);
	return n;
}



@@ 716,7 821,7 @@ streamread(Chan *c, void *vbuf, long n)
			continue;
		}

		i = bp->wptr - bp->rptr;
		i = BLEN(bp);
		if(i <= left){
			memcpy(buf, bp->rptr, i);
			left -= i;


@@ 806,7 911,7 @@ flowctl(Queue *q)
 *  send the request as a single delimited block
 */
long
streamwrite(Chan *c, void *a, long n)
streamwrite(Chan *c, void *a, long n, int docopy)
{
	Stream *s;
	Block *bp;


@@ 846,7 951,7 @@ streamwrite(Chan *c, void *a, long n)
	if(q->other->flag & QHUNGUP)
		error(0, Ehungup);

	if(GLOBAL(a) || n==0){
	if((GLOBAL(a) && !docopy) || n==0){
		/*
		 *  `a' is global to the whole system, just create a
		 *  pointer to it and pass it on.


@@ 887,3 992,27 @@ streamwrite(Chan *c, void *a, long n)
	poperror();
	return n;
}

/*
 *  like andrew's getmfields but no hidden state
 */
int
getfields(char *lp,	/* to be parsed */
	char **fields,	/* where to put pointers */
	int n,		/* number of pointers */
	char sep	/* separator */
)
{
	int i;

	for(i=0; lp && *lp && i<n; i++){
		while(*lp == sep)
			*lp++=0;
		if(*lp == 0)
			break;
		fields[i]=lp;
		while(*lp && *lp != sep)
			lp++;
	}
	return i;
}

M port/sturp.c => port/sturp.c +381 -347
@@ 1,23 1,23 @@
#include "syslibc.h"
#include "lock.h"
#include "chan.h"
#include "proc.h"
#include "user.h"
#include "errno.h"
#include "lint.h"
#include "mem.h"
#include "mempool.h"
#include "stream.h"
#include "dkparam.h"
#include "misc.h"
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"errno.h"

enum {
	Nurp=		32,
	MSrexmit=	1000,
	Nmask=		0x7,
};

#define DPRINT if(0)

typedef struct Urp	Urp;

#define NOW (MACHP(0)->ticks*MS2HZ)

/*
 * URP status
 */


@@ 34,12 34,13 @@ struct urpstat {
} urpstat;

struct Urp {
	Qlock;
	QLock;
	short	state;		/* flags */
	Rendez	r;		/* process waiting for close */

	/* input */

	ulong	iwindow;	/* input window */
	Queue	*rq;		/* input queue */
	uchar	iseq;		/* last good input sequence number */
	uchar	lastecho;	/* last echo/rej sent */
	uchar	trbuf[3];	/* trailer being collected */


@@ 47,22 48,23 @@ struct Urp {

	/* output */

	Queue	*rq;
	Queue	*wq;
	int	XW;		/* blocks per xmit window */
	QLock	xmit;		/* output lock, only one process at a time */
	Queue	*wq;		/* output queue */
	int	maxout;		/* maximum outstanding unacked blocks */
	int	maxblock;	/* max block size */
	int	timeout;	/* a timeout has occurred */
	int	WS;		/* start of current window */
	int	WACK;		/* first non-acknowledged message */
	int	WNX;		/* next message to be sent */
	Rendez	r;		/* process waiting in urpoput */
	Rendez	kr;		/* process waiting in urpoput */
	int	next;		/* next block to send */
	int	unechoed;	/* first unechoed block */
	int	unacked;	/* first unacked block */
	int	nxb;		/* next xb to use */
	Block	*xb[8];		/* the xmit window buffer */
	uchar	timer;		/* timeout for xmit */
	QLock	xl[8];
	ulong	timer;		/* timeout for xmit */

	int	kstarted;
};
#define WINDOW(u) ((u->WS + u->XW - u->WNX)%8)
#define BETWEEN(x, s, n) (s<=n ? x>=s && x<n : x<n || x>=s)
#define UNACKED(x, u) (BETWEEN(x, u->WACK, u->WNX)
#define WINDOW(u) ((u->unechoed + u->maxout - u->next)%8)
#define IN(x, f, n) (f<=n ? x>=f && x<n : x<n || x>=f)
#define NEXT(x) (((x)+1)&Nmask)

/*
 *  Protocol control bytes


@@ 87,33 89,39 @@ struct Urp {
#define	DELAY	0100		/* real-time printing delay */
#define	BREAK	0110		/* Send/receive break (new style) */

#define	REXMITING	0001
#define	REXMIT		0002
#define	INITING 	0004
#define	REJECTING	0x1
#define	INITING 	0x2
#define HUNGUP		0x4
#define	OPEN		0x8
#define CLOSING		0x10

Urp	urp[Nurp];

/*
 *  predeclared
 */
static void	urpiput(Queue*, Block*, Rendez*);
static void	urpoput(Queue*, Block*, Rendez*);
static void	urpciput(Queue*, Block*);
static void	urpiput(Queue*, Block*);
static void	urpoput(Queue*, Block*);
static void	urpopen(Queue*, Stream*);
static void	urpclose(Queue *);
static void	output(Urp*);
static void	sendblock(Urp*, int);
static void	rcvack(Urp*, int);
static void	flushinput(Urp*);
static void	sendctl(Queue*, int);
static void	queuectl(Urp*, int);
static void	sendctl(Urp*, int);
static void	initoutput(Urp*, int);
static void	initinput(Urp*, int);
static void	urpkproc(void *arg);

Qinfo urpinfo = { urpiput, urpoput, urpopen, urpclose, "urp" };
Qinfo urpinfo = { urpciput, urpoput, urpopen, urpclose, "urp" };

int
static void
urpopen(Queue *q, Stream *s)
{
	Urp *up;
	int i;
	char name[128];

	/*
	 *  find a free urp structure


@@ 127,26 135,44 @@ urpopen(Queue *q, Stream *s)
	if(up == &urp[Nurp])
		error(0, Egreg);

	q->ptr = = q->other->ptr = up;
	q->ptr = q->other->ptr = up;
	up->rq = q;
	up->wq = WR(q);
	q->put = urpciput;
	up->wq = q->other;
	up->state = OPEN;
	qunlock(up);
	initinput(up, 0);
	initoutput(up, 0);

	/*
	 *  start the ack/(re)xmit process
	 */
	if(up->kstarted == 0){
		up->kstarted = 1;
		sprint(name, "**urp%d**", up - urp);
		kproc(name, urpkproc, up);
	}
}

/*
 *  Shut it down.
 *  Shut down the connection and kill off the kernel process
 */
static int
allacked(void *a)
isflushed(void *a)
{
	Urp *up;

	up = (Urp *)a;
	return up->WACK == up->WNX;
	return (up->state&HUNGUP) || (up->unechoed==up->next && up->wq->len==0);
}
static int
isdead(void *a)
{
	Urp *up;

	up = (Urp *)a;
	return up->kstarted == 0;
}
static void
urpclose(Queue *q)
{
	Block *bp;


@@ 154,14 180,31 @@ urpclose(Queue *q)
	int i;

	up = (Urp *)q->ptr;
	up->state |= LCLOSE;

	/*
	 *  wait for output to get acked
	 *  wait for all outstanding messages to drain, tell kernel
	 *  process we're closing.
	 */
	while(!urpdone(up))
		sleep(&up->r, urpdone, up);
	up->state = 0;
	up->state |= CLOSING;
	sleep(&up->r, isflushed, up);

	/*
	 *  ack all outstanding messages
	 */
	qlock(&up->xmit);
	up->state |= HUNGUP;
	i = up->next - 1;
	if(i < 0)
		i = 7;
	rcvack(up, ECHO+i);
	qunlock(&up->xmit);
	DPRINT("urpclose(%ux)\n", up);

	/*
	 *  kill off the kernel process
	 */
	wakeup(&up->rq->r);
	DPRINT("urpclosed(%ux)\n", up);
}

/*


@@ 172,29 215,25 @@ urpctliput(Urp *up, Queue *q, Block *bp)
{
	switch(bp->type){
	case M_HANGUP:
		/*
		 *  ack all outstanding messages
		 */
		lock(up);
		up->state &= ~(INITING);
		up->state |= RCLOSE;
		unlock(up);
		if(up->WS<up->WNX)
			urprack(up, ECHO+((up->WNX-1)&07));
		up->state |= HUNGUP;
		wakeup(&up->r);
		wakeup(&up->rq->r);
		break;
	}
	PUTNEXT(q, bp);
}

/*
 *  character mode input.  the last character in EVERY block is
 *  a control character.
 *  character mode input.
 *
 *  the first byte in every message is a ctl byte (which belongs at the end).
 */
void
urpciput(Queue *q, Block *bp, Rendez *rp)
urpciput(Queue *q, Block *bp)
{
	Urp *up;
	int i, full;
	Block *nbp;
	int i;
	int ctl;

	up = (Urp *)q->ptr;
	if(bp->type != M_DATA){


@@ 205,15 244,14 @@ urpciput(Queue *q, Block *bp, Rendez *rp)
	/*
	 *  get the control character
	 */
	if(bp->wptr > bp->rptr)
		ctl = *(--bp->wptr);
	else
		ctl = 0;
	ctl = *bp->rptr++;
	if(ctl < 0)
		return;

	/*
	 *  send the block upstream
	 *  take care of any data
	 */
	if(bp->wptr > bp->rptr)
	if(BLEN(bp)>0 && q->next->len<Streamhi)
		PUTNEXT(q, bp);
	else
		freeb(bp);


@@ 224,29 262,27 @@ urpciput(Queue *q, Block *bp, Rendez *rp)
	switch(ctl){
	case 0:
		break;

	case ENQ:
		urpstat.enqsr++;
		queuectl(up, up->lastecho);
		queuectl(up, ACK+up->iseq);
		flushinput(up);
		sendctl(up, up->lastecho);
		sendctl(up, ACK+up->iseq);
		break;

	case CHECK:
		queuectl(up, ACK+up->iseq);
		sendctl(up, ACK+up->iseq);
		break;

	case AINIT:
		up->state &= ~INITING;
		flushinput(up);
		wakeup(&cp->kr);
		wakeup(&up->rq->r);
		break;

	case INIT0:
	case INIT1:
		queuectl(up, AINIT);
		if(*bp->rptr == INIT1)
			q->put = urpbiput;
		sendctl(up, AINIT);
		if(ctl == INIT1)
			q->put = urpiput;
		initinput(up, 0);
		break;



@@ 257,6 293,11 @@ urpciput(Queue *q, Block *bp, Rendez *rp)
	case BREAK:
		break;

	case REJ+0: case REJ+1: case REJ+2: case REJ+3:
	case REJ+4: case REJ+5: case REJ+6: case REJ+7:
		rcvack(up, ctl);
		break;
	
	case ACK+0: case ACK+1: case ACK+2: case ACK+3:
	case ACK+4: case ACK+5: case ACK+6: case ACK+7:
	case ECHO+0: case ECHO+1: case ECHO+2: case ECHO+3:


@@ 266,29 307,29 @@ urpciput(Queue *q, Block *bp, Rendez *rp)

	case SEQ+0: case SEQ+1: case SEQ+2: case SEQ+3:
	case SEQ+4: case SEQ+5: case SEQ+6: case SEQ+7:
		/*
		 *  acknowledge receipt
		 */
		ctl = ctl & 07;
		if(q->next->len < Streamhi){
			queuectl(up, ECHO+ctl);
			up->lastecho = ECHO+ctl;
			wakeup(&cp->kr);
		}
		up->iseq = ctl;
		i = ctl & Nmask;
		if(q->next->len < Streamhi)
			sendctl(up, up->lastecho = ECHO+i);
		up->iseq = i;
		break;
	}
}

/*
 *  block mode input.  the last character in EVERY block is a control character.
 *  block mode input.
 *
 *  the first byte in every message is a ctl byte (which belongs at the end).
 *
 *  Simplifying assumption:  one put == one message && the control byte
 *	is in the first block.  If this isn't true, strange bytes will be
 *	used as control bytes.
 */
void
urpbiput(Queue *q, Block *bp, Rendez *rp)
urpiput(Queue *q, Block *bp)
{
	Urp *up;
	int i, full;
	Block *nbp;
	int i;
	int ctl;

	up = (Urp *)q->ptr;
	if(bp->type != M_DATA){


@@ 299,15 340,14 @@ urpbiput(Queue *q, Block *bp, Rendez *rp)
	/*
	 *  get the control character
	 */
	if(bp->wptr > bp->rptr)
		ctl = *(--bp->wptr);
	else
		ctl = 0;
	ctl = *bp->rptr++;

	/*
	 *  take care of any block count(trx)
	 */
	while(bp->wptr > bp->rptr && up->trx){
	while(up->trx){
		if(BLEN(bp)<=0)
			break;
		switch (up->trx) {
		case 1:
		case 2:


@@ 315,21 355,20 @@ urpbiput(Queue *q, Block *bp, Rendez *rp)
			continue;
		default:
			up->trx = 0;
		case 0:
			break;
		}
	}

	/*
	 *  queue the block
	 *  queue the block(s)
	 */
	if(bp->wptr > bp->rptr){
		if(q->len > up->iwindow){
	if(BLEN(bp) > 0){
		putq(q, bp);
		q->last->flags &= ~S_DELIM;
		if(q->len > 4*1024){
			flushinput(up);
			freeb(bp);
			return;
		}
		putq(q, bp);
	} else
		freeb(bp);



@@ 340,26 379,27 @@ urpbiput(Queue *q, Block *bp, Rendez *rp)
	case 0:
		break;
	case ENQ:
		print("rENQ\n");
		urpstat.enqsr++;
		queuectl(up, up->lastecho);
		queuectl(up, ACK+up->iseq);
		sendctl(up, up->lastecho);
		sendctl(up, ACK+up->iseq);
		flushinput(up);
		break;

	case CHECK:
		queuectl(WR(q)->next, ACK+up->iseq);
		sendctl(up, ACK+up->iseq);
		break;

	case AINIT:
		up->state &= ~INITING;
		flushinput(up);
		wakeup(&cp->kr);
		wakeup(&up->rq->r);
		break;

	case INIT0:
	case INIT1:
		queuectl(up, AINIT);
		if(*bp->rptr == INIT0)
		sendctl(up, AINIT);
		if(ctl == INIT0)
			q->put = urpciput;
		initinput(up, 0);
		break;


@@ 380,6 420,7 @@ urpbiput(Queue *q, Block *bp, Rendez *rp)

	case REJ+0: case REJ+1: case REJ+2: case REJ+3:
	case REJ+4: case REJ+5: case REJ+6: case REJ+7:
		print("rREJ\n");
		rcvack(up, ctl);
		break;
	


@@ 391,22 432,31 @@ urpbiput(Queue *q, Block *bp, Rendez *rp)
		break;

	/*
	 *  this case is extremely ugliferous
	 *  if the seuence number is the next expected
	 *	and te trailer length == 3
	 *	and the block count matches the bytes received
	 *  then send the bytes upstream.
	 */
	case SEQ+0: case SEQ+1: case SEQ+2: case SEQ+3:
	case SEQ+4: case SEQ+5: case SEQ+6: case SEQ+7:
		i = ctl & 07;
		i = ctl & Nmask;
		if(up->trx != 3){
			urpstat.rjtrs++;
			flushinput(up);
			print("sREJ\n");
			sendctl(up, up->lastecho = REJ+up->iseq);
			break;
		} else if(q->len != up->trbuf[1] + (up->trbuf[2]<<8)){
			urpstat.rjpks++;
			flushinput(up);
			print("sREJ\n");
			sendctl(up, up->lastecho = REJ+up->iseq);
			break;
		} else if(i != ((up->iseq+1)&07))) {
		} else if(i != ((up->iseq+1)&Nmask)) {
			urpstat.rjseq++;
			flushinput(up);
			print("sREJ\n");
			sendctl(up, up->lastecho = REJ+up->iseq);
			break;
		}



@@ 414,26 464,26 @@ urpbiput(Queue *q, Block *bp, Rendez *rp)
		 *  send data upstream
		 */
		if(q->first) {
			q->first->flags |= S_DELIM;
			if(up->trbuf[0] != BOTM)
				q->last->flags |= S_DELIM;
			while(bp = getq(q))
				PUTNEXT(q, nbp);
				PUTNEXT(q, bp);
		} else {
			bp = allocb(0);
			bp->flag |= S_DELIM;
			bp->flags |= S_DELIM;
			PUTNEXT(q, bp);
		{
		}
		up->trx = 0;

		/*
		 *  acknowledge receipt
		 */
		ctl = ctl & 07;
		if(q->next->len < Streamhi){
			queuectl(up, ECHO+ctl);
			up->lastecho = ECHO+ctl;
			wakeup(&cp->kr);
			sendctl(up, ECHO+i);
			up->lastecho = ECHO+i;
			wakeup(&up->rq->r);
		}
		up->iseq = ctl;
		up->iseq = i;
		break;
	}
}


@@ 444,20 494,21 @@ urpbiput(Queue *q, Block *bp, Rendez *rp)
static void
urpctloput(Urp *up, Queue *q, Block *bp)
{
	int fields[2];
	char *fields[2];
	int n;
	int inwin=0, outwin=0;

	switch(bp->type){
	case M_CTL:
		if(streamparse("init", bp)){
			switch(getfields(bp->rptr, fields, 2, ' ')){
			switch(getfields((char *)bp->rptr, fields, 2, ' ')){
			case 2:
				inwin = strtoul(fields[1], 0, 0);
			case 1:
				outwin = strtoul(fields[0], 0, 0);
			}
			initinput(up, inwin);
/*			initinput(up, inwin); */
			DPRINT("initoutput %d\n", outwin);
			initoutput(up, outwin);
			freeb(bp);
			return;


@@ 467,8 518,9 @@ urpctloput(Urp *up, Queue *q, Block *bp)
}

/*
 * accept data from writer
 *  accept data from a writer
 */
static void
urpoput(Queue *q, Block *bp)
{
	Urp *up;


@@ 480,192 532,148 @@ urpoput(Queue *q, Block *bp)
		return;
	}

	urpstat.output + =  bp->wptr - bp->rptr;

	for(;;){
	}
	urpstat.output += BLEN(bp);
	putq(q, bp);
	output(up);
}

/*
 * wait for an AINIT
 *  start output
 */
void
urpopenwait(Urp *up, Queue *q)
static void
output(Urp *up)
{
	while((up->state&ISOPENING) && !(up->state&RCLOSE)) {
		proc->state = Waiting;
		up->proc = proc;
		putctl1(q->next, M_RDATA, INIT1);
		if((up->state&ISOPENING) == 0){
			up->proc = 0;
			if(proc->state == Waiting){
				proc->state = Running;
				return;
			}
		}
		sched();
		up->proc = 0;
	}
}
	Block *bp, *nbp;
	ulong now;
	Queue *q;
	int n;

/*
 *  wait till transmission is complete
 */
void
urpxmitwait(Urp *up, Queue *q)
{
	Block *bp;
	int debug;
	if(!canqlock(&up->xmit))
		return;

	debug = (q->flag&QDEBUG);
	if(waserror()){
		print("urp output error\n");
		qunlock(&up->xmit);
		nexterror();
	}

	up->timeout = 0;
	while(!EMPTY(q) || up->WS<up->WNX){
		/*
		 *  clean up if the channel closed
		 */
		if (up->state & RCLOSE) {
			while(bp = getq(q))
				freeb(bp);
			up->WACK = up->WS = up->WNX;
		}
		/*
		 * free acked blocks
		 */
		urpfreeacked(up);
		/*
		 * retransmit if requested
		 */
		if(up->state&REXMIT)
			urprexmit(up, q);
		/*
		 * fill up the window
		 */
		if(!EMPTY(q) && WINDOW(up))
			urpfillwindow(up, q);
		/*
		 * ask other end for its status
		 */
		if(up->timeout){
			urpstat.enqsx++;
			putctl1(q->next, M_RDATA, ENQ);
			up->timeout = 0;
	/*
	 *  if still initing and it's time to rexmit, send an INIT1
	 */
	now = NOW;
	if(up->state & INITING){
		if(now > up->timer){
			sendctl(up, INIT1);
			up->timer = now + MSrexmit;
		}
		lock(up->olock);
		if((up->state&RCLOSE) == 0 && up->WS! = up->WNX) {
			proc->state = Waiting;
			up->proc = proc;
			unlock(up->olock);
			sched();
		} else
			unlock(up->olock);
		qunlock(&up->xmit);
		poperror();
		return;
	}
	urpfreeacked(up);
	up->WS = up->WACK = up->WF = up->WNX = up->WNX&07;
}

/*
 * fill up the urp output window
 */
void
urpfillwindow(Urp *up, Queue *q)
{
	Block *bp, *xbp;
	int debug;

	debug = (q->flag&QDEBUG);

	/*
	 * now process any thing that fits in the flow control window
	 *  fill the transmit buffers
	 */
	while (WINDOW(up)) {
		bp = getq(q);
		if (bp  ==  NULL)
			break;
		/* force MAXBLOCK length segments */
		if (bp->rptr+up->maxblock < bp->wptr) {
			xbp = allocb(0);
			if (xbp == NULL) {
				putbq(q, bp);
				break;
			}
			xbp->rptr = bp->rptr;
			bp->rptr + =  up->maxblock;
			xbp->wptr = bp->rptr;
			putbq(q, bp);
			bp = xbp;
		}
		/*
		 *  put new block in the block array.  if something is already
		 *  there, it should be because we haven't gotten around to freeing
		 *  it yet.  If not, complain.
		 */
		if (up->xb[up->WNX&07]) {
			urpfreeacked(up);
			if (up->xb[up->WNX&07]) {
				uprint(up, "urpfillwindow: overlap");
				freeb(up->xb[up->WNX&07]);
			}
	q = up->wq;
	for(bp = getq(q); bp && up->xb[up->nxb]==0; up->nxb = NEXT(up->nxb)){
		if(BLEN(bp) > up->maxblock){
			nbp = up->xb[up->nxb] = allocb(0);
			nbp->rptr = bp->rptr;
			nbp->wptr = bp->rptr = bp->rptr + up->maxblock;
		} else {
			up->xb[up->nxb] = bp;
			bp = getq(q);
		}
		up->xb[up->WNX&07] = bp;
		up->WNX++;
		urpxmit(q, bp, up->WNX-1);
	}
	if(bp)
		putbq(q, bp);
/*	print("output w(%d) up->xb[%d](%ux) up->nxb(%d) up->state(%ux)\n",
		WINDOW(up), up->next, up->xb[up->next], up->nxb, up->state);
/**/
	/*
	 *  if a retransmit time has elapsed since a transmit, send an ENQ
	 */
	if(up->unechoed != up->next && NOW > up->timer){
		print("sENQ\n");
		up->timer = NOW + MSrexmit;
		up->state &= ~REJECTING;
		sendctl(up, ENQ);
		qunlock(&up->xmit);
		poperror();
		return;
	}

	/*
	 *  if there's a window open, push some blocks out
	 */
	while(WINDOW(up)>0 && up->xb[up->next]!=0 && canqlock(&up->xl[up->next])){
		if(up->xb[up->next])
			sendblock(up, up->next);
		qunlock(&up->xl[up->next]);
		up->next = NEXT(up->next);
	}
	qunlock(&up->xmit);
	poperror();
}

/*
 *  Send out a message, with trailer.
 *  send a control byte, put the byte at the end of the allocated
 *  space in case a lower layer needs header room.
 */
void
urpxmit(Queue *q, Block *bp, int seqno)
static void
sendctl(Urp *up, int ctl)
{
	Urp *up = (Urp *)q->ptr;
	int size;
	Block *xbp;
	int debug;
	Block *bp;

	if (bp == NULL) {
		print("null bp in urpxmit\n");
	if(up->wq->next->len > Streamhi)
		return;
	}
	debug = (q->flag&QDEBUG);
	size = bp->wptr - bp->rptr;
	seqno & =  07;
	/* send ptr to block, if non-empty */
	if (size) {
		if ((xbp = allocb(0))  ==  NULL){
			print("can't xmit\n");
			return;
		}
		xbp->rptr = bp->rptr;
		xbp->wptr = bp->wptr;
		xbp->type = bp->type;
		PUTNEXT(q, xbp);
	}
	/* send trailer */
	if ((xbp = allocb(3))  ==  NULL){
		print("can't xmit2\n");
		return;
	}
	xbp->type = M_RDATA;
	*xbp->wptr++ = (bp->class&S_DELIM) ? BOT: BOTM;
	*xbp->wptr++ = size;
	*xbp->wptr++ = size >> 8;
	PUTNEXT(q, xbp);
	putctl1(q->next, M_RDATA, SEQ + seqno);
	up->timer = DKPTIME;
	bp = allocb(1);
	bp->wptr = bp->lim;
	bp->rptr = bp->lim-1;
	*bp->rptr = ctl;
	bp->flags |= S_DELIM;
	PUTNEXT(up->wq, bp);
}

void
urprexmit(Urp *up, Queue *q)
/*
 *  send a block.
 */
static void
sendblock(Urp *up, int bn)
{
	int i;
	Block *bp, *m, *nbp;
	int n;

	for (i = up->WACK; i<up->WNX; i++) {
		urpxmit(q, up->xb[i&07], i);
		urpstat.rxmit++;
	}
	up->state & =  ~REXMIT;
	up->timer = NOW + MSrexmit;
	if(up->wq->next->len > Streamhi)
		return;

	/*
	 *  message 1, the BOT and the data
	 */
	bp = up->xb[bn];
	m = allocb(1);
	m->rptr = m->lim - 1;
	m->wptr = m->lim;
	*m->rptr = (bp->flags & S_DELIM) ? BOT : BOTM;
	nbp = m->next = allocb(0);
	nbp->rptr = bp->rptr;
	nbp->wptr = bp->wptr;
	nbp->flags |= S_DELIM;
	PUTNEXT(up->wq, m);

	/*
	 *  message 2, the block length and the SEQ
	 */
	m = allocb(3);
	m->rptr = m->lim - 3;
	m->wptr = m->lim;
	n = BLEN(bp);
	m->rptr[0] = SEQ | bn;
	m->rptr[1] = n;
	m->rptr[2] = n<<8;
	m->flags |= S_DELIM;
	PUTNEXT(up->wq, m);
}

/*


@@ 677,30 685,52 @@ rcvack(Urp *up, int msg)
	int seqno;
	int next;

	seqno = msg&07;
	next = (seqno+1) & 0x7
	seqno = msg&Nmask;
	next = NEXT(seqno);

	lock(up);
	if(BETWEEN(seqno, up->WACK, up->WNX))
		up->WACK = next;
	/*
	 *  release any acknowledged blocks
	 */
	if(IN(seqno, up->unacked, up->next)){
		for(; up->unacked != next; up->unacked = NEXT(up->unacked)){
			qlock(&up->xl[up->unacked]);
			if(up->xb[up->unacked])
				freeb(up->xb[up->unacked]);
			up->xb[up->unacked] = 0;
			qunlock(&up->xl[up->unacked]);
		}
	}

	switch(msg & 0370){
	case ECHO:
		up->timer = MSrexmit;	/* push off ENQ timeout */
		if(BETWEEN(seqno, up->WS, up->WNX)){
			up->WS = next;
			wakeup(&up->r);
		if(IN(seqno, up->unechoed, up->next)) {
			up->unechoed = next;
		}
		/*
		 *  the next reject at the start of a window starts a 
		 *  retransmission.
		 */
		up->state &= ~REJECTING;
		break;
	case REJ:
		if(IN(seqno, up->unechoed, up->next))
			up->unechoed = next;
		/*
		 *  ... FALL THROUGH ...
		 */
	case ACK:
		if(up->WACK == next){
			up->state |= REXMIT;
			wakeup(&up->r);
		/*
		 *  start a retransmission if we aren't retransmitting
		 *  and this is the start of a window.
		 */
		if(up->unechoed==next && !(up->state & REJECTING)){
			up->state |= REJECTING;
			up->next = next;
		}
		break;
	}
	unlock(up);

	wakeup(&up->rq->r);
}

/*


@@ 717,54 747,12 @@ flushinput(Urp *up)
}

/*
 *  send a control character down stream
 */
static void
sendctl(Queue *q, int x)
{
	Block *bp;

	/*
	 *  send anything queued
	 */
	while(bp = getq(q))
		PUTNEXT(q, bp);

	/*
	 *  send the new byte
	 */
	bp = allocb(1);
	*bp->wptr++ = x;
	PUTNEXT(q, bp);
}

/*
 *  queue a control character to be sent down stream
 */
static void
queuectl(Urp *up, int x)
{
	Block *bp;
	Queue *q;

	q = up->wq;
	bp = allocb(1);
	*bp->wptr++ = x;
	putq(q, bp);
	wakeup(&up->r);
}

/*
 *  initialize output
 */
static void
initoutput(Urp *up, int window)
{
	/*
	 *  ack any outstanding blocks
	 */
	if(up->WS<up->WNX)
		urprack(up, ECHO+((up->WNX-1)&07));
	int i;

	/*
	 *  set output window


@@ 774,21 762,33 @@ initoutput(Urp *up, int window)
		up->maxblock = 64;
	if(up->maxblock > Streamhi/4)
		up->maxblock = Streamhi/4;
	up->XW = 4;
	up->maxblock -= 4;
	up->maxout = 3;

	/*
	 *  set sequence varialbles
	 */
	up->WS = 1;
	up->WACK = 1;
	up->WNX = 1;
	up->unechoed = 1;
	up->unacked = 1;
	up->next = 1;
	up->nxb = 1;

	/*
	 *  free any outstanding blocks
	 */
	for(i = 0; i < 8; i++){
		qlock(&up->xl[i]);
		if(up->xb[i])
			freeb(up->xb[i]);
		qunlock(&up->xl[i]);
	}

	/*
	 *  tell the other side we've inited
	 */
	up->state |= ISOPENING;
	up->timer = MSrexmit;
	sendctl(q->next, INIT1);
	up->state |= INITING;
	up->timer = NOW + MSrexmit;
	sendctl(up, INIT1);
}

/*


@@ 803,6 803,40 @@ initinput(Urp *up, int window)
	up->trx = 0;
	up->iseq = 0;
	up->lastecho = ECHO+0;
	up->WF = 1;
	flushinput(up);
}

/*
 *  do retransmissions etc
 */
static int
todo(void *arg)
{
	Urp *up;

	up = (Urp *)arg;
	return (WINDOW(up)>0 && up->wq->len>0 && !(up->state&INITING));
}
static void
urpkproc(void *arg)
{
	Urp *up;

	up = (Urp *)arg;

	for(;;){
		if(up->state & (HUNGUP|CLOSING)){
			if(isflushed(up))
				wakeup(&up->r);
			if(up->state & HUNGUP)
				break;
		}
		if((up->lastecho&Nmask)!=up->iseq && up->rq->next->len<Streamhi)
			sendctl(up, up->lastecho = ECHO+up->iseq);
		output(up);
		tsleep(&up->rq->r, todo, up, MSrexmit/2);
	}
	DPRINT("urpkproc exiting %ux\n", up);
	up->kstarted = 0;
	up->state = 0;
}

M power/dat.h => power/dat.h +3 -1
@@ 242,6 242,7 @@ struct Orig
	ulong	mqid;
	ulong	minca;			/* base of region in chan */
	ulong	maxca;			/* end of region in chan */
int nmod;
};

struct Page


@@ 439,7 440,8 @@ struct Stream {
#define STREAMTYPE(x)	((x)&0x1f)
#define STREAMID(x)	(((x)&~CHDIR)>>5)
#define STREAMQID(i,t)	(((i)<<5)|(t))
#define PUTNEXT(q,b)	(*(q)->next->put)((q)->next, bp)
#define PUTNEXT(q,b)	(*(q)->next->put)((q)->next, b)
#define BLEN(b)		((b)->wptr - (b)->rptr)

/*
 *  stream file qid's & high water mark

M power/devhs.c => power/devhs.c +410 -75
@@ 8,17 8,54 @@

#include	"io.h"

typedef struct Hsvme	Hsvme;
typedef struct Device	Device;

enum {
	Maxtxburst=	1024,		/* maximum transmit burst size */
	Maxburst=	1023,		/* maximum transmit burst size */
	Vmevec=		0xd0,		/* vme vector for interrupts */
	Intlevel=	5,		/* level to interrupt on */
	Nhsvme=		1,
};

struct hsvmestats {
	ulong	parity;
	ulong	rintr;
	ulong	tintr;
} hsvmestats;
/*
 *  hsvme datakit board
 */
struct Device {
	ushort	version;
	ushort	pad0x02;
	ushort	vector;
	ushort	pad0x06;
	ushort	csr;
	ushort	pad0x0A;
	ushort	data;
};
#define HSVME		VMEA24SUP(Device, 0xF90000)

struct Hsvme {
	QLock;

	QLock	xmit;
	Device	*addr;
	int	vec;		/* interupt vector */
	Rendez	r;		/* output process */
	Rendez	kr;		/* input kernel process */
	int	chan;		/* current input channel */
	Queue	*rq;		/* read queue */
	uchar	buf[1024];	/* bytes being collected */
	uchar	*wptr;		/* pointer into buf */
	int	kstarted;	/* true if kernel process started */

	/* statistics */

	ulong	parity;		/* parity errors */
	ulong	rintr;		/* rcv interrupts */
	ulong	tintr;		/* transmit interrupts */
	ulong	in;		/* bytes in */
	ulong	out;		/* bytes out */
};

Hsvme hsvme[Nhsvme];

#define ALIVE		0x0001
#define IENABLE		0x0004


@@ 42,21 79,30 @@ struct hsvmestats {
#define TXEOD		0x0400
#define NND		0x8000

Rendez hsvmer;

#define DELAY(n)	{ \
	register int N = 12*(n); \
	while (--N > 0); \
	}

static void hsvmeintr(int);
static void hsvmekproc(void*);

/*
 *  hsvme stream module definition
 */
static void hsvmeoput(Queue*, Block*);
static void hsvmestopen(Queue*, Stream*);
static void hsvmestclose(Queue*);
Qinfo hsvmeinfo = { nullput, hsvmeoput, hsvmestopen, hsvmestclose, "hsvme" };

/*
 *  restart a VME board
 */
void
hsvmerestart(struct hsvme *addr)
hsvmerestart(Hsvme *hp)
{
	Device *addr;

	addr = hp->addr;

	addr->csr = RESET;
	wbflush();
	DELAY(20000);
	delay(20);

	/*
	 *  set interrupt vector


@@ 64,29 110,32 @@ hsvmerestart(struct hsvme *addr)
	 *  set forcew to a known value
	 *  interrupt on level `Intlevel'
	 */
	addr->vector = 0xd0;
	addr->vector = hp->vec;
	addr->csr = NORESET|IPL(Intlevel)|IENABLE|ALIVE;
	wbflush();
	DELAY(500);
	delay(1);
	addr->csr = NORESET|IPL(Intlevel)|FORCEW|IENABLE|ALIVE;
	wbflush();
	DELAY(500);
	delay(1);
}

/*
 *  reset all vme boards
 */
void
hsvmereset(void)
{
	struct hsvme *addr;

	addr = HSVME;
	addr->csr = RESET;
	int i;
	Hsvme *hp;

	for(i=0; i<Nhsvme; i++){
		hsvme[i].addr = HSVME+i;
		hsvme[i].vec = Vmevec+i;
		hsvme[i].addr->csr = RESET;
		setvmevec(hsvme[i].vec, hsvmeintr);
	}	
	wbflush();
	DELAY(20000);

	/*
	 *  routine to call on interrupt
	 */
	setvmevec(Vmevec, hsvmeintr);
	delay(20);
}

void


@@ 95,23 144,26 @@ hsvmeinit(void)
}

/*
 *  enable the device for interrupts
 *  enable the device for interrupts, spec is the device number
 */
static int
never(void *arg)
{
	return 0;
}
Chan*
hsvmeattach(char *spec)
{
	struct hsvme *addr;

	addr = HSVME;
	hsvmerestart(addr);
	print("hsvme csr %ux\n", addr->csr);

	return devattach('h', spec);
	Hsvme *hp;
	int i;
	Chan *c;

	i = strtoul(spec, 0, 0);
	if(i >= Nhsvme)
		error(0, Ebadarg);
	hp = &hsvme[i];
	hsvmerestart(hp);
	print("hsvme [%d] csr %ux\n", i, hp->addr->csr);

	c = devattach('h', spec);
	c->dev = i;
	c->qid = CHDIR;
	return c;
}

Chan*


@@ 123,25 175,23 @@ hsvmeclone(Chan *c, Chan *nc)
int	 
hsvmewalk(Chan *c, char *name)
{
	if(c->qid != CHDIR)
		return 0;
	if(strcmp(name, "hsvme") == 0){
		c->qid = 1;
		return 1;
	}
	return 0;
	return devwalk(c, name, 0, 0, streamgen);
}

void	 
hsvmestat(Chan *c, char *dp)
{
	print("hsvmestat\n");
	error(0, Egreg);
	devstat(c, dp, 0, 0, streamgen);
}

Chan*
hsvmeopen(Chan *c, int omode)
{
	if(c->qid == CHDIR){
		if(omode != OREAD)
			error(0, Eperm);
	}else
		streamopen(c, &hsvmeinfo);
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;


@@ 157,20 207,20 @@ hsvmecreate(Chan *c, char *name, int omode, ulong perm)
void	 
hsvmeclose(Chan *c)
{
	if(c->qid != CHDIR)
		streamclose(c);
}

long	 
hsvmeread(Chan *c, void *buf, long n)
{
	error(0, Egreg);
	return 0;
	return streamread(c, buf, n);
}

long	 
hsvmewrite(Chan *c, void *buf, long n)
{
	error(0, Egreg);
	return 0;
	return streamwrite(c, buf, n, 0);
}

void	 


@@ 197,31 247,316 @@ hsvmeerrstr(Error *e, char *buf)
	rooterrstr(e, buf);
}

/*
 *	the stream routines
 */

/*
 *  create the kernel process for input
 */
static void
hsvmestopen(Queue *q, Stream *s)
{
	Hsvme *hp;
	char name[32];

	hp = &hsvme[s->dev];
	sprint(name, "**hsvme%d**", s->dev);
	q->ptr = q->other->ptr = hp;
	hp->rq = q;
	kproc(name, hsvmekproc, hp);
}

/*
 *  kill off the kernel process
 */
static int
kdead(void *arg)
{
	Hsvme *hp;

	hp = (Hsvme *)arg;
	return hp->kstarted == 0;
}
static void
hsvmeintr(int vec)
hsvmestclose(Queue * q)
{
	ushort csr;
	struct hsvme *addr;
	Hsvme *hp;

	hp = (Hsvme *)q->ptr;
	qlock(hp);
	hp->rq = 0;
	qunlock(hp);
	wakeup(&hp->kr);
	sleep(&hp->r, kdead, hp);
}

	print("hsvme intr\n");
	addr = HSVME;
	wbflush();
	csr = addr->csr;
	do {
		if (addr->csr & REF) {
			hsvmestats.rintr++;
			print("hsvme rintr\n");
/*
 *  free all blocks of a message in `q', `bp' is the first block
 *  of the message
 */
static void
freemsg(Queue *q, Block *bp)
{
	for(; bp; bp = getq(q)){
		if(bp->flags & S_DELIM){
			freeb(bp);
			return;
		}
		if (addr->csr & XHF) {
			hsvmestats.tintr++;
			print("hsvme tintr\n");
		freeb(bp);
	}
}

/*
 *  return true if the output fifo is at least half empty.
 *  the implication is that it can take at least another 1000 byte burst.
 */
static int
halfempty(void *arg)
{
	Device *addr;

	addr = (Device*)arg;
	return addr->csr & XHF;
}

/*
 *  output a block
 *
 *  the first 2 bytes of every message are the channel number,
 *  low order byte first.  the third is a possible trailing control
 *  character.
 */
void
hsvmeoput(Queue *q, Block *bp)
{
	Device *addr;
	Hsvme *hp;
	int burst;
	int chan;
	int ctl;
	int n;

	if(bp->type != M_DATA){
		freeb(bp);
		return;
	}

	/*
	 *  get a whole message before handing bytes to the device
	 */
	if(!putq(q, bp))
		return;

	/*
	 *  one transmitter at a time
	 */
	hp = (Hsvme *)q->ptr;
	qlock(&hp->xmit);
	addr = hp->addr;

	/*
	 *  parse message
	 */
	bp = getq(q);
	if(bp->wptr - bp->rptr < 3){
		freemsg(q, bp);
		qunlock(&hp->xmit);
		return;
	}
	chan = CHNO | *bp->rptr++ | (*bp->rptr++<<8);
	ctl = *bp->rptr++;

	/*
	 *  send the 8 bit data, burst are up to Maxburst (9-bit) bytes long
	 */
	if(!(addr->csr & XHF))
		sleep(&hp->r, halfempty, addr);
/*	print("->%.2uo\n", CHNO|chan);/**/
	addr->data = CHNO|chan;
	burst = Maxburst;
	while(bp){
		if(burst == 0){
			addr->data = TXEOD;
/*			print("->%.2uo\n", TXEOD); /**/
			if(!(addr->csr & XHF))
				sleep(&hp->r, halfempty, addr);
/*			print("->%.2uo\n", CHNO|chan); /**/
			addr->data = CHNO|chan;
			burst = Maxburst;
		}
		if ((csr^XFF) & (XFF|EROFLOW|EFRAME|EPARITY|EXOFLOW)) {
			hsvmestats.parity++;
			hsvmerestart(addr);
			print("hsvme %ux: reset, csr = 0x%x\n",
				HSVME, csr);
		n = bp->wptr - bp->rptr;
		if(n > burst)
			n = burst;
		burst -= n;
		while(n--){
/*			print("->%.2uo\n", *bp->rptr); /**/
			addr->data = *bp->rptr++;
		}
		wbflush();
	} while ((csr = addr->csr) & REF);
		if(bp->rptr >= bp->wptr){
			if(bp->flags & S_DELIM){
				freeb(bp);
				break;
			}
			freeb(bp);
			bp = getq(q);
		}
	}

	/*
	 *  send the control byte if there is one
	 */
	if(ctl){
/*		print("->%.2uo\n", CTL|ctl); /**/
		addr->data = CTL|ctl;
	}

	/*
	 *  start the fifo emptying
	 */
/*	print("->%.2uo\n", TXEOD); /**/
	addr->data = TXEOD;

	qunlock(&hp->xmit);
}

/*
 *  return true if the input fifo is non-empty
 */
static int
notempty(void *arg)
{
	Device *addr;

	addr = (Device *)arg;
	return addr->csr & REF;
}

/*
 *  fill a block with what is currently buffered and send it upstream
 */
static void
upstream(Hsvme *hp, unsigned int ctl)
{
	int n;
	Block *bp;

	n = hp->wptr - hp->buf;
	bp = allocb(3 + n);
	bp->wptr[0] = hp->chan;
	bp->wptr[1] = hp->chan>>8;
	bp->wptr[2] = ctl;
	if(n)
		memcpy(&bp->wptr[3], hp->buf, n);
	bp->wptr += 3 + n;
	bp->flags |= S_DELIM;
	PUTNEXT(hp->rq, bp);
	hp->wptr = hp->buf;
}

/*
 *  Read bytes from the input fifo.  Since we take an interrupt every
 *  time the fifo goes non-empty, we need to waste time to let the
 *  fifo fill up.
 */
static void
hsvmekproc(void *arg)
{
	Hsvme *hp;
	Device *addr;
	unsigned int c;

	hp = (Hsvme *)arg;
	addr = hp->addr;
	hp->kstarted = 1;
	hp->wptr = hp->buf;

	for(;;){
		/*
		 *  die if the device is closed
		 */
		qlock(hp);
		if(hp->rq == 0){
			qunlock(hp);
			hp->kstarted = 0;
			wakeup(&hp->r);
			return;
		}

		/*
		 *  let the fifo fill a bit
		 */
		delay(1);

		/*
		 *  0xFFFF means an empty fifo
		 */
		while ((c = addr->data) != 0xFFFF) {
/*			print(" %.2uo<-\n", c); /**/
			if(c & CHNO){
				c &= 0x1FF;
				if(hp->chan == c)
					continue;
				/*
				 *  new channel, put anything saved upstream
				 */
				if(hp->wptr - hp->buf != 0)
					upstream(hp, 0);
				hp->chan = c;
			} else if(c & NND){
				/*
				 *  ctl byte, this ends a message
				 */
				upstream(hp, c);
			} else {
				/*
				 *  data byte, put in local buffer
				 */
				*hp->wptr++ = c;
				if(hp->wptr == &hp->buf[sizeof hp->buf])
					upstream(hp, 0);
			}
		}
		qunlock(hp);

		/*
		 *  sleep if input fifo empty
		 */
		if(!notempty(addr))
			sleep(&hp->kr, notempty, addr);
	}
}

/*
 *  only one flavor interrupt.   we have to use the less than half full
 *  and not empty bits to figure out whom to wake.
 */
static void
hsvmeintr(int vec)
{
	ushort csr;
	Device *addr;
	Hsvme *hp;

	hp = &hsvme[vec - Vmevec];
	if(hp < hsvme || hp > &hsvme[Nhsvme]){
		print("bad hsvme vec\n");
		return;
	}
	csr = hp->addr->csr;

	if (csr & REF) {
		hp->rintr++;
		wakeup(&hp->kr);
	}
	if (csr & XHF) {
		hp->tintr++;
		wakeup(&hp->r);
	}
	if ((csr^XFF) & (XFF|EROFLOW|EFRAME|EPARITY|EXOFLOW)) {
		hp->parity++;
		hsvmerestart(hp);
		print("hsvme %d: reset, csr = 0x%ux\n",
			HSVME, csr);
	}
}

M power/errno.h => power/errno.h +6 -1
@@ 36,10 36,15 @@ enum{
	Etoosmall,	/* read or write too small */
	Ehungup,	/* write to hungup stream */
	Ebadnet,	/* illegal network address */
	Enoifc,		/* no free nonet interface slots */
	Enoifc,		/* no free interface slots */
	Enodev,		/* no free devices */
	Ebadctl,	/* bad process control request */
	Enonote,	/* note overflow */
	Eintr,		/* interrupted */
	Edestbusy,	/* datakit destination busy */
	Enetbusy,	/* datakit controller busy */
	Edestotl,	/* datakit destination out to lunch */
	Enetotl,	/* datakit controller out to lunch */
	Erejected,	/* datakit destination rejected call */
	Egreg,		/* it's all greg's fault */
};

M power/fns.h => power/fns.h +4 -1
@@ 59,6 59,7 @@ void	freepte(Orig*);
void	freesegs(int);
void	freealarm(Alarm*);
Block	*getb(Blist*);
int	getfields(char*, char**, int, char);
Block	*getq(Queue*);
void	gettlb(int, ulong*);
ulong	gettlbvirt(int);


@@ 110,6 111,7 @@ void	pgrpcpy(Pgrp*, Pgrp*);
void	pgrpinit(void);
int	postnote(Proc*, int, char*, int);
int	pprint(char*, ...);
Block	*prepend(Block*, int);
void	prflush(void);
void	printinit(void);
void	printslave(void);


@@ 121,6 123,7 @@ void	putmmu(ulong, ulong);
void	puttlb(ulong, ulong);
void	puttlbx(int, ulong, ulong);
int	putb(Blist*, Block*);
void	putbq(Blist*, Block*);
int	putq(Queue*, Block*);
void	putstrn(char*, long);
ulong	pwait(Waitmsg*);


@@ 149,7 152,7 @@ Devgen	streamgen;
void	streamclose(Chan*);
void	streaminit(void);
long	streamread(Chan*, void*, long);
long	streamwrite(Chan*, void*, long);
long	streamwrite(Chan*, void*, long, int);
Stream*	streamnew(Chan*, Qinfo*);
void	streamopen(Chan*, Qinfo*);
int	streamparse(char*, Block*);

M power/main.c => power/main.c +1 -1
@@ 371,7 371,7 @@ confinit(void)
	conf.npage = i*1024/4;

	conf.npte = 20000;
	conf.nmod = 500;
	conf.nmod = 2000;
	conf.nalarm = 1000;
	conf.norig = 500;
	conf.nchan = 500;