From 6de675e78382921545dd741041adeba4a1cd05c5 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Mon, 12 Mar 1990 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1990-03-12 --- gnot/boot.c | 130 +- gnot/boot.h | 3016 ++++++++++++++++++++++++++++++++++------------- gnot/clock.c | 4 +- gnot/dat.h | 4 +- gnot/devcons.c | 12 +- gnot/devdk.c | 1462 +++++++++++++++++++++++ gnot/devincon.c | 764 ++++++++++++ gnot/devmnt.c | 2 + gnot/devpipe.c | 2 +- gnot/errno.h | 7 +- gnot/fault.c | 4 +- gnot/fns.h | 11 +- gnot/lock.c | 6 + gnot/main.c | 26 +- gnot/proc.c | 59 + gnot/stream.c | 180 ++- gnot/sturp.c | 845 +++++++++++++ gnot/sysproc.c | 16 +- gnot/trap.c | 1 + port/devcons.c | 10 +- port/devdk.c | 1459 +++++++++++++++++++++++ port/devlance.c | 4 +- port/devmnt.c | 1 + port/devpipe.c | 2 +- port/fault.c | 4 +- port/page.c | 5 + port/proc.c | 11 +- port/stream.c | 179 ++- port/sturp.c | 728 ++++++------ power/dat.h | 4 +- power/devhs.c | 485 ++++++-- power/errno.h | 7 +- power/fns.h | 5 +- power/main.c | 2 +- 34 files changed, 8120 insertions(+), 1337 deletions(-) create mode 100644 gnot/devdk.c create mode 100644 gnot/devincon.c create mode 100644 gnot/sturp.c create mode 100644 port/devdk.c diff --git a/gnot/boot.c b/gnot/boot.c index 2fda2656e0ef20877e0b2eb57121cfdef497f4f5..68453f1cd3ec5721405c4f8f5e248b68de682018 100644 --- a/gnot/boot.c +++ b/gnot/boot.c @@ -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 diff --git a/gnot/boot.h b/gnot/boot.h index 1326e57631e31850a05715f567f5c38a1bfc5122..cd4a92dbbaf28ebc1307822ebeb7983bff7d6838 100644 --- a/gnot/boot.h +++ b/gnot/boot.h @@ -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, }; diff --git a/gnot/clock.c b/gnot/clock.c index 2bfd3e4e32b68b8efe86e6a2785a2aeb972ba271..1b1456c4f0f321513bf145bbe259c68b7d614979 100644 --- a/gnot/clock.c +++ b/gnot/clock.c @@ -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; diff --git a/gnot/dat.h b/gnot/dat.h index 6fde871cd26dfbd3d2fc3d165344f1fa48eb36b4..ca74d0139ff1241c766bf88a8cc60c9662b61f59 100644 --- a/gnot/dat.h +++ b/gnot/dat.h @@ -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 diff --git a/gnot/devcons.c b/gnot/devcons.c index 4c6048bc5fdbc636454255de7f9e3805c93851ac..9287da2cb75b87acec612a7a007ae8c9514debac 100644 --- a/gnot/devcons.c +++ b/gnot/devcons.c @@ -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; } +*/ diff --git a/gnot/devdk.c b/gnot/devdk.c new file mode 100644 index 0000000000000000000000000000000000000000..393049b2db2da598e0737c00e43dedc5a98fa29a --- /dev/null +++ b/gnot/devdk.c @@ -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<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<= 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 && linelines){ + 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; dkicsc==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; ilines; 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); + } +} diff --git a/gnot/devincon.c b/gnot/devincon.c new file mode 100644 index 0000000000000000000000000000000000000000..98130bbf63058ea05532431122cb2cbfa307eb54 --- /dev/null +++ b/gnot/devincon.c @@ -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; icmd = 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; + } +} diff --git a/gnot/devmnt.c b/gnot/devmnt.c index 6fdc537c0b9bf4c8dc9860acfdc884665cc2bd84..502d31adfc9a535917fa3eb23eb041f80dadbe13 100644 --- a/gnot/devmnt.c +++ b/gnot/devmnt.c @@ -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); diff --git a/gnot/devpipe.c b/gnot/devpipe.c index 25982e151c47603956c0c187a7c5c3aa51c778ce..4e6b2cace5496d679cf69afd501d71f071fc28ff 100644 --- a/gnot/devpipe.c +++ b/gnot/devpipe.c @@ -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 diff --git a/gnot/errno.h b/gnot/errno.h index 9e8c2fb90810941ce401a6153144ce7a35f8e7aa..4b815c5257409bea51ba3abd6d82af178e728b98 100644 --- a/gnot/errno.h +++ b/gnot/errno.h @@ -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 */ }; diff --git a/gnot/fault.c b/gnot/fault.c index 614cc2a5745c3358ce1affd263480ea96fa3d145..7b472c4486cbc68ab0c0a84a91a4778d3f07bf10 100644 --- a/gnot/fault.c +++ b/gnot/fault.c @@ -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); diff --git a/gnot/fns.h b/gnot/fns.h index 09fe8a007972594635665408aabaf4ca2dfeaaf1..756055d2c646728469bfae9cca1ece849c0dad03 100644 --- a/gnot/fns.h +++ b/gnot/fns.h @@ -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); diff --git a/gnot/lock.c b/gnot/lock.c index 2a53ee561c45ed1adda7ab19a1bbdbf1e5804578..c66fa3e09e93a0669eab5382ee1981b3d692f294 100644 --- a/gnot/lock.c +++ b/gnot/lock.c @@ -66,6 +66,12 @@ qlock(QLock *q) sched(); } +int +canqlock(QLock *q) +{ + return canlock(&q->use); +} + void qunlock(QLock *q) { diff --git a/gnot/main.c b/gnot/main.c index caf53f1d31d5ba46300570763987c8671a9652a5..6faf5925c7df169cfa2739e0bdd21f4015528a9f 100644 --- a/gnot/main.c +++ b/gnot/main.c @@ -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(); @@ -27,6 +41,12 @@ main(void) schedinit(); } +void +unloadboot(void) +{ + strncpy(protouser, BOOT->user, NAMELEN); +} + void machinit(void) { @@ -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; diff --git a/gnot/proc.c b/gnot/proc.c index 053c6a236b27f0228c9409598aa46f55ab2b093f..4b2bdf5ba03af2f51ce414333d7d2e6fda05fab7 100644 --- a/gnot/proc.c +++ b/gnot/proc.c @@ -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(); +} diff --git a/gnot/stream.c b/gnot/stream.c index 360b6db85bac109118ae0cff172eb64bc85d6615..309c68cc221f75894935de32f8353171dcff32a7 100644 --- a/gnot/stream.c +++ b/gnot/stream.c @@ -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,12 +380,86 @@ 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 */ @@ -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 && iticks*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=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->lenlastecho); + 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->lenlastecho = 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; +} diff --git a/gnot/sysproc.c b/gnot/sysproc.c index 668f19b2a0a723b8bf3c23e762b4a41e6b637858..46838b421114b0cb66d248eb6b6ef56c6b6269b2 100644 --- a/gnot/sysproc.c +++ b/gnot/sysproc.c @@ -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; diff --git a/gnot/trap.c b/gnot/trap.c index 3993e88ffd37aff9366dab5c0e54d3c447e42476..5f87657e6c95430769d946028019560a7f370b1f 100644 --- a/gnot/trap.c +++ b/gnot/trap.c @@ -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 diff --git a/port/devcons.c b/port/devcons.c index cd1a3c1cddca2ae31204d2c25efd36015fa6d57e..5a18808d0d703dfc21cc553a3083c349e32e3621 100644 --- a/port/devcons.c +++ b/port/devcons.c @@ -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 */ diff --git a/port/devdk.c b/port/devdk.c new file mode 100644 index 0000000000000000000000000000000000000000..1eb337605664e3e7e261e0f4eeddeeab092b233c --- /dev/null +++ b/port/devdk.c @@ -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<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<= 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 && linelines){ + 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; dkicsc==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; ilines; 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); + } +} diff --git a/port/devlance.c b/port/devlance.c index 3538a79108c7b49d01921e73751ccfca0cc283e1..b50c957250c78a8b407556e43d9bf23e6338a2b0 100644 --- a/port/devlance.c +++ b/port/devlance.c @@ -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 diff --git a/port/devmnt.c b/port/devmnt.c index e33688ffa958f0ee9f5d616a34008c8cadad2876..502d31adfc9a535917fa3eb23eb041f80dadbe13 100644 --- a/port/devmnt.c +++ b/port/devmnt.c @@ -468,6 +468,7 @@ mntremove(Chan *c) mhfree(mh); nexterror(); } + decref(m); mh->thdr.type = Tremove; mh->thdr.fid = c->fid; mntxmit(m, mh); diff --git a/port/devpipe.c b/port/devpipe.c index 25982e151c47603956c0c187a7c5c3aa51c778ce..4e6b2cace5496d679cf69afd501d71f071fc28ff 100644 --- a/port/devpipe.c +++ b/port/devpipe.c @@ -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 diff --git a/port/fault.c b/port/fault.c index 51bb69b48647c2b5de43153041311bf667b4a978..fb96b97067d8bbd86bf3f699120c666ffd5e237e 100644 --- a/port/fault.c +++ b/port/fault.c @@ -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); diff --git a/port/page.c b/port/page.c index 2889dcbeda970773d0016f2228fceb4cc93dd18c..bd47ec9f3b5d9c53956a25dee60d807812568497 100644 --- a/port/page.c +++ b/port/page.c @@ -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); diff --git a/port/proc.c b/port/proc.c index d0fad9f0ba699e260e04bba2c83478dc2f566564..d889f0fe03aa77d745ec12c70b5d79a643bc5468 100644 --- a/port/proc.c +++ b/port/proc.c @@ -518,16 +518,23 @@ proctab(int i) #include DEBUG() { - int i; + int i, j; Proc *p; + Orig *o; print("DEBUG\n"); for(i=0; istate != 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; jseg[j].o; + if(o) + print(" s%d %d %d\n", j, o->nmod, o->npte); + } + } } } diff --git a/port/stream.c b/port/stream.c index 2478fdc6ff255e8b1a15830b245a378a60366de8..198aa4a51d3313fcc45b5a16fa320185a0c713fe 100644 --- a/port/stream.c +++ b/port/stream.c @@ -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,12 +381,86 @@ 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 */ @@ -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 && iticks*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=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=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->WSWNX) - 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->lenlastecho); - 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->WSWNX){ - /* - * 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; iWNX; 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); } /* @@ -716,55 +746,13 @@ flushinput(Urp *up) up->trx = 0; } -/* - * 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->WSWNX) - 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->lenlastecho = 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; +} diff --git a/power/dat.h b/power/dat.h index 4003dd8e5320b0b39a933edfebe4165819c2b451..cf4a7e6bb2f38555d709cbd82c10162db2652a0e 100644 --- a/power/dat.h +++ b/power/dat.h @@ -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 diff --git a/power/devhs.c b/power/devhs.c index 91f5665a3cbb6cbe90f8129779fa97b6687c751b..418df73ba37eb90e77582abd65e2e12fe5275c63 100644 --- a/power/devhs.c +++ b/power/devhs.c @@ -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; icsr = 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); + } } diff --git a/power/errno.h b/power/errno.h index 9e8c2fb90810941ce401a6153144ce7a35f8e7aa..4b815c5257409bea51ba3abd6d82af178e728b98 100644 --- a/power/errno.h +++ b/power/errno.h @@ -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 */ }; diff --git a/power/fns.h b/power/fns.h index 2a039f7575fa2344213e1cf682578986c1073df2..ef54c143f18570f5f0a673c762e5cdb417e794cd 100644 --- a/power/fns.h +++ b/power/fns.h @@ -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*); diff --git a/power/main.c b/power/main.c index 8ee8343bd2c7dc197074d06e34e44054828febe9..d0c8492b32269b06911191e1af3f5bc5dd9529dc 100644 --- a/power/main.c +++ b/power/main.c @@ -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;