Browse Source

Plan 9 from Bell Labs 2006-02-18

David du Colombier 18 years ago
parent
commit
a46b828b14
100 changed files with 12063 additions and 4137 deletions
  1. 125 98
      dist/replica/_plan9.db
  2. 125 98
      dist/replica/plan9.db
  3. 143 0
      dist/replica/plan9.log
  4. BIN
      sys/doc/fs/fs.pdf
  5. 539 477
      sys/doc/fs/fs.ps
  6. 8 4
      sys/doc/fs/mkfile
  7. 52 9
      sys/doc/fs/p0
  8. 23 15
      sys/doc/fs/p1
  9. 98 92
      sys/doc/fs/p2
  10. 73 13
      sys/doc/fs/p4
  11. 3 3
      sys/doc/fs/p5
  12. 57 49
      sys/doc/fs/p6
  13. 1 1
      sys/doc/fs/p7
  14. 24 0
      sys/doc/fs/p9
  15. 76 0
      sys/doc/fs/pa
  16. 17 0
      sys/man/4/consolefs
  17. 17 0
      sys/man/8/fsconfig
  18. 20 20
      sys/src/fs/9netics32.16k/9net32.16kfs.c
  19. 6 4
      sys/src/fs/9netics32.16k/dat.h
  20. 15 15
      sys/src/fs/9netics32.16k/fns.h
  21. 6 2
      sys/src/fs/9netics32.16k/io.h
  22. 85 0
      sys/src/fs/9netics32.16k/mem.h
  23. 18 17
      sys/src/fs/9netics32.16k/mkfile
  24. 21 19
      sys/src/fs/9netics64.8k/9net64.8kfs.c
  25. 7 3
      sys/src/fs/9netics64.8k/dat.h
  26. 15 15
      sys/src/fs/9netics64.8k/fns.h
  27. 6 2
      sys/src/fs/9netics64.8k/io.h
  28. 85 0
      sys/src/fs/9netics64.8k/mem.h
  29. 23 21
      sys/src/fs/9netics64.8k/mkfile
  30. 11 12
      sys/src/fs/choline/9cholinefs.c
  31. 5 4
      sys/src/fs/choline/dat.h
  32. 15 15
      sys/src/fs/choline/fns.h
  33. 6 2
      sys/src/fs/choline/io.h
  34. 1 1
      sys/src/fs/choline/mem.h
  35. 19 16
      sys/src/fs/choline/mkfile
  36. 773 0
      sys/src/fs/dev/apc.c
  37. 199 181
      sys/src/fs/dev/cw.c
  38. 17 18
      sys/src/fs/dev/fworm.c
  39. 31 46
      sys/src/fs/dev/juke.c
  40. 28 28
      sys/src/fs/dev/mworm.c
  41. 0 961
      sys/src/fs/dev/sony.c
  42. 15 15
      sys/src/fs/dev/wren.c
  43. 68 0
      sys/src/fs/doc/changes
  44. 15 0
      sys/src/fs/doc/words
  45. 128 0
      sys/src/fs/doc/worm.fs
  46. 81 0
      sys/src/fs/doc/worm.fs64
  47. 0 0
      sys/src/fs/doc/worms.32-bit
  48. 11 12
      sys/src/fs/emelie/9emeliefs.c
  49. 6 4
      sys/src/fs/emelie/dat.h
  50. 15 15
      sys/src/fs/emelie/fns.h
  51. 6 2
      sys/src/fs/emelie/io.h
  52. 1 1
      sys/src/fs/emelie/mem.h
  53. 20 15
      sys/src/fs/emelie/mkfile
  54. 181 0
      sys/src/fs/fs/9fsfs.c
  55. 35 0
      sys/src/fs/fs/dat.h
  56. 79 0
      sys/src/fs/fs/fns.h
  57. 246 0
      sys/src/fs/fs/io.h
  58. 1 1
      sys/src/fs/fs/mem.h
  59. 144 0
      sys/src/fs/fs/mkfile
  60. 179 0
      sys/src/fs/fs64/9fsfs64.c
  61. 35 0
      sys/src/fs/fs64/dat.h
  62. 79 0
      sys/src/fs/fs64/fns.h
  63. 246 0
      sys/src/fs/fs64/io.h
  64. 1 1
      sys/src/fs/fs64/mem.h
  65. 143 0
      sys/src/fs/fs64/mkfile
  66. 2 2
      sys/src/fs/ip/arp.c
  67. 78 79
      sys/src/fs/ip/il.c
  68. 2 2
      sys/src/fs/ip/ip.c
  69. 5 5
      sys/src/fs/ip/ip.h
  70. 3 7
      sys/src/fs/ip/iproute.c
  71. 4 1
      sys/src/fs/mkfile
  72. 17 9
      sys/src/fs/pc/8250.c
  73. 66 18
      sys/src/fs/pc/8253.c
  74. 63 0
      sys/src/fs/pc/compat.c
  75. 32 1
      sys/src/fs/pc/compat.h
  76. 0 1289
      sys/src/fs/pc/devata.c
  77. 32 29
      sys/src/fs/pc/dosfs.c
  78. 12 12
      sys/src/fs/pc/dosfs.h
  79. 20 11
      sys/src/fs/pc/ether8139.c
  80. 171 117
      sys/src/fs/pc/ether83815.c
  81. 1363 0
      sys/src/fs/pc/ether83815.mii.c
  82. 20 20
      sys/src/fs/pc/etherdp83820.c
  83. 8 7
      sys/src/fs/pc/etherga620.c
  84. 11 9
      sys/src/fs/pc/etherif.c
  85. 2060 0
      sys/src/fs/pc/etherigbe.c
  86. 185 10
      sys/src/fs/pc/ethermii.c
  87. 77 9
      sys/src/fs/pc/ethermii.h
  88. 14 14
      sys/src/fs/pc/floppy.c
  89. 11 2
      sys/src/fs/pc/l.s
  90. 1 2
      sys/src/fs/pc/malloc.c
  91. 2 1
      sys/src/fs/pc/mkfile
  92. 2 2
      sys/src/fs/pc/pc.c
  93. 27 1
      sys/src/fs/pc/pci.c
  94. 29 24
      sys/src/fs/pc/scsincr53c8xx.c
  95. 2729 0
      sys/src/fs/pc/sdata.c
  96. 375 0
      sys/src/fs/pc/sdscsi.c
  97. 3 3
      sys/src/fs/pc/toy.c
  98. 82 107
      sys/src/fs/port/9p1.c
  99. 2 2
      sys/src/fs/port/9p1.h
  100. 27 15
      sys/src/fs/port/9p1lib.c

+ 125 - 98
dist/replica/_plan9.db

@@ -5635,18 +5635,20 @@ sys/doc/fossil.ms - 664 sys sys 1138396406 31439
 sys/doc/fossil.pdf - 664 sys sys 1042123169 63200
 sys/doc/fossil.ps - 664 sys sys 1135487951 313552
 sys/doc/fs - 20000000775 sys sys 945616779 0
-sys/doc/fs/fs.pdf - 664 sys sys 1020384351 47177
-sys/doc/fs/fs.ps - 664 sys sys 960837905 276918
-sys/doc/fs/mkfile - 664 sys sys 1138459286 273
-sys/doc/fs/p0 - 664 sys sys 1138396562 682
-sys/doc/fs/p1 - 664 sys sys 953844581 989
-sys/doc/fs/p2 - 664 sys sys 953844581 3749
+sys/doc/fs/fs.pdf - 664 sys sys 1140157478 64965
+sys/doc/fs/fs.ps - 664 sys sys 1140157389 298023
+sys/doc/fs/mkfile - 664 sys sys 1140157064 366
+sys/doc/fs/p0 - 664 sys sys 1138445459 1677
+sys/doc/fs/p1 - 664 sys sys 1094002291 800
+sys/doc/fs/p2 - 664 sys sys 1094112719 3775
 sys/doc/fs/p3 - 664 sys sys 944959592 1475
-sys/doc/fs/p4 - 664 sys sys 953844581 3662
-sys/doc/fs/p5 - 664 sys sys 944959592 1143
-sys/doc/fs/p6 - 664 sys sys 953844573 6235
-sys/doc/fs/p7 - 664 sys sys 953844581 958
+sys/doc/fs/p4 - 664 sys sys 1094178190 5764
+sys/doc/fs/p5 - 664 sys sys 1094002662 1140
+sys/doc/fs/p6 - 664 sys sys 1094177480 6091
+sys/doc/fs/p7 - 664 sys sys 1094000398 960
 sys/doc/fs/p8 - 664 sys sys 953844574 881
+sys/doc/fs/p9 - 664 sys sys 1140157346 659
+sys/doc/fs/pa - 664 sys sys 1094002902 2027
 sys/doc/il - 20000000775 sys sys 945616779 0
 sys/doc/il/il.ms - 664 sys sys 1138396552 11390
 sys/doc/il/il.pdf - 664 sys sys 1020384351 44630
@@ -7583,7 +7585,7 @@ sys/man/4/acme - 664 sys sys 1134233060 10274
 sys/man/4/archfs - 664 sys sys 960000712 533
 sys/man/4/cdfs - 664 sys sys 1026846913 3638
 sys/man/4/cfs - 664 sys sys 1015024813 1758
-sys/man/4/consolefs - 664 sys sys 1069179473 3920
+sys/man/4/consolefs - 664 sys sys 1140209468 4241
 sys/man/4/dossrv - 664 sys sys 1015024813 4176
 sys/man/4/execnet - 664 sys sys 1019866708 1069
 sys/man/4/exportfs - 664 sys sys 1115314261 4653
@@ -7696,7 +7698,7 @@ sys/man/8/dhcpd - 664 sys sys 1032654987 5237
 sys/man/8/drawterm - 664 sys sys 1135901219 1741
 sys/man/8/fossilcons - 664 sys sys 1138466274 18276
 sys/man/8/fs - 664 sys sys 1055701170 15029
-sys/man/8/fsconfig - 664 sys sys 1045501600 8142
+sys/man/8/fsconfig - 664 sys sys 1107841165 8350
 sys/man/8/getflags - 664 sys sys 1139839200 1712
 sys/man/8/gpsfs - 664 sys sys 1123643451 4924
 sys/man/8/httpd - 664 sys sys 1121198377 6675
@@ -14269,120 +14271,145 @@ sys/src/cmd/winwatch.c - 664 sys sys 1113312596 4869
 sys/src/cmd/xd.c - 664 sys sys 1124993597 5770
 sys/src/cmd/yacc.c - 664 sys sys 1116770313 57945
 sys/src/fs - 20000000775 sys sys 1015110109 0
+sys/src/fs/9netics32.16k - 20000000775 sys sys 1140158691 0
+sys/src/fs/9netics32.16k/9net32.16kfs.c - 664 sys sys 1140167903 3208
+sys/src/fs/9netics32.16k/dat.h - 664 sys sys 1140167904 612
+sys/src/fs/9netics32.16k/fns.h - 664 sys sys 1140167905 1961
+sys/src/fs/9netics32.16k/io.h - 664 sys sys 1097661949 6688
+sys/src/fs/9netics32.16k/mem.h - 664 sys sys 1140167906 2907
+sys/src/fs/9netics32.16k/mkfile - 664 sys sys 1140167908 1615
+sys/src/fs/9netics64.8k - 20000000775 sys sys 1140158692 0
+sys/src/fs/9netics64.8k/9net64.8kfs.c - 664 sys sys 1140167920 3235
+sys/src/fs/9netics64.8k/dat.h - 664 sys sys 1095751680 611
+sys/src/fs/9netics64.8k/fns.h - 664 sys sys 1140167922 1961
+sys/src/fs/9netics64.8k/io.h - 664 sys sys 1097661949 6688
+sys/src/fs/9netics64.8k/mem.h - 664 sys sys 1140167924 2907
+sys/src/fs/9netics64.8k/mkfile - 664 sys sys 1140167925 1608
 sys/src/fs/choline - 20000000775 sys sys 1015110109 0
-sys/src/fs/choline/9cholinefs.c - 664 sys sys 1041361226 2962
-sys/src/fs/choline/dat.h - 664 sys sys 1037805081 528
-sys/src/fs/choline/fns.h - 664 sys sys 1037805081 1968
-sys/src/fs/choline/io.h - 664 sys sys 1037805081 6576
-sys/src/fs/choline/mem.h - 664 sys sys 1015110108 2865
-sys/src/fs/choline/mkfile - 664 sys sys 1091803608 1676
+sys/src/fs/choline/9cholinefs.c - 664 sys sys 1140168020 2989
+sys/src/fs/choline/dat.h - 664 sys sys 1140168017 612
+sys/src/fs/choline/fns.h - 664 sys sys 1140168018 1961
+sys/src/fs/choline/io.h - 664 sys sys 1140168018 6688
+sys/src/fs/choline/mem.h - 664 sys sys 1140168019 2909
+sys/src/fs/choline/mkfile - 664 sys sys 1140168021 1751
 sys/src/fs/dev - 20000000775 sys sys 1015109978 0
-sys/src/fs/dev/cw.c - 664 sys sys 1047663897 42252
-sys/src/fs/dev/fworm.c - 664 sys sys 1015109971 1737
-sys/src/fs/dev/juke.c - 664 sys sys 1055699807 22892
+sys/src/fs/dev/apc.c - 664 sys sys 1140167918 14910
+sys/src/fs/dev/cw.c - 664 sys sys 1097712558 43252
+sys/src/fs/dev/fworm.c - 664 sys sys 1140167912 1845
+sys/src/fs/dev/juke.c - 664 sys sys 1097578476 22885
 sys/src/fs/dev/mkfile - 664 sys sys 1015109974 132
-sys/src/fs/dev/mworm.c - 664 sys sys 1037805087 4138
-sys/src/fs/dev/sony.c - 664 sys sys 1037805088 17049
-sys/src/fs/dev/wren.c - 664 sys sys 1015109978 2327
+sys/src/fs/dev/mworm.c - 664 sys sys 1140167915 4264
+sys/src/fs/dev/wren.c - 664 sys sys 1140167916 2392
+sys/src/fs/doc - 20000000775 sys sys 1140158639 0
+sys/src/fs/doc/changes - 664 sys sys 1140168029 2967
+sys/src/fs/doc/words - 664 sys sys 1140168029 507
+sys/src/fs/doc/worm.fs - 664 sys sys 1140168030 4057
+sys/src/fs/doc/worm.fs64 - 664 sys sys 1101811981 1554
+sys/src/fs/doc/worms.32-bit - 664 sys sys 1140168032 2806
 sys/src/fs/emelie - 20000000775 sys sys 1015110098 0
-sys/src/fs/emelie/9pcfs.c - 664 sys sys 1041361237 2962
-sys/src/fs/emelie/dat.h - 664 sys sys 1037805101 502
-sys/src/fs/emelie/fns.h - 664 sys sys 1037805101 1968
-sys/src/fs/emelie/io.h - 664 sys sys 1037805101 6576
-sys/src/fs/emelie/mem.h - 664 sys sys 1015110098 2865
-sys/src/fs/emelie/mkfile - 664 sys sys 1091803614 1697
+sys/src/fs/emelie/9emeliefs.c - 664 sys sys 1140168026 2989
+sys/src/fs/emelie/dat.h - 664 sys sys 1140168023 612
+sys/src/fs/emelie/fns.h - 664 sys sys 1140168023 1961
+sys/src/fs/emelie/io.h - 664 sys sys 1140168024 6688
+sys/src/fs/emelie/mem.h - 664 sys sys 1140168025 2909
+sys/src/fs/emelie/mkfile - 664 sys sys 1140168027 1750
+sys/src/fs/fs - 20000000775 sys sys 1140158693 0
+sys/src/fs/fs/9fsfs.c - 664 sys sys 1140168015 3308
+sys/src/fs/fs/dat.h - 664 sys sys 1140168010 611
+sys/src/fs/fs/fns.h - 664 sys sys 1140168011 1961
+sys/src/fs/fs/io.h - 664 sys sys 1097661949 6688
+sys/src/fs/fs/mem.h - 664 sys sys 1140168013 2909
+sys/src/fs/fs/mkfile - 664 sys sys 1140168014 1663
+sys/src/fs/fs64 - 20000000775 sys sys 1140158693 0
+sys/src/fs/fs64/9fsfs64.c - 664 sys sys 1140168004 3294
+sys/src/fs/fs64/dat.h - 664 sys sys 1140168005 611
+sys/src/fs/fs64/fns.h - 664 sys sys 1140168005 1961
+sys/src/fs/fs64/io.h - 664 sys sys 1140168006 6688
+sys/src/fs/fs64/mem.h - 664 sys sys 1020579319 2909
+sys/src/fs/fs64/mkfile - 664 sys sys 1140168008 1603
 sys/src/fs/ip - 20000000775 sys sys 1015109990 0
-sys/src/fs/ip/arp.c - 664 sys sys 1015109981 8696
+sys/src/fs/ip/arp.c - 664 sys sys 1097578954 8696
 sys/src/fs/ip/icmp.c - 664 sys sys 1015109981 991
-sys/src/fs/ip/il.c - 664 sys sys 1015109983 19423
-sys/src/fs/ip/ip.c - 664 sys sys 1015109984 3859
-sys/src/fs/ip/ip.h - 664 sys sys 1015109985 6691
+sys/src/fs/ip/il.c - 664 sys sys 1140167928 19479
+sys/src/fs/ip/ip.c - 664 sys sys 1097578933 3859
+sys/src/fs/ip/ip.h - 664 sys sys 1140167930 6691
 sys/src/fs/ip/ipaux.c - 664 sys sys 1015109986 1041
-sys/src/fs/ip/iproute.c - 664 sys sys 1015109987 7421
+sys/src/fs/ip/iproute.c - 664 sys sys 1097579099 7386
 sys/src/fs/ip/mkfile - 664 sys sys 1015109988 127
 sys/src/fs/ip/sntp.c - 664 sys sys 1037805108 5539
 sys/src/fs/ip/udp.c - 664 sys sys 1015109990 754
-sys/src/fs/mkfile - 664 sys sys 1015110109 195
+sys/src/fs/mkfile - 664 sys sys 1140167932 231
 sys/src/fs/pc - 20000000775 sys sys 1015110085 0
-sys/src/fs/pc/8250.c - 664 sys sys 1015110032 6795
-sys/src/fs/pc/8253.c - 664 sys sys 1032062036 6206
+sys/src/fs/pc/8250.c - 664 sys sys 1140161725 6957
+sys/src/fs/pc/8253.c - 664 sys sys 1097579039 7264
 sys/src/fs/pc/cga.c - 664 sys sys 1015110034 1484
-sys/src/fs/pc/compat.c - 664 sys sys 1037805115 546
-sys/src/fs/pc/compat.h - 664 sys sys 1039794577 1636
-sys/src/fs/pc/devata.c - 664 sys sys 1037805116 26981
-sys/src/fs/pc/dosfs.c - 664 sys sys 1015110040 16461
-sys/src/fs/pc/dosfs.h - 664 sys sys 1037805116 2667
+sys/src/fs/pc/compat.c - 664 sys sys 1140164905 1575
+sys/src/fs/pc/compat.h - 664 sys sys 1140167957 2426
+sys/src/fs/pc/dosfs.c - 664 sys sys 1097712365 16637
+sys/src/fs/pc/dosfs.h - 664 sys sys 1140167937 2696
 sys/src/fs/pc/ether2114x.c - 664 sys sys 1015110044 31874
-sys/src/fs/pc/ether8139.c - 664 sys sys 1039794573 18318
+sys/src/fs/pc/ether8139.c - 664 sys sys 1086569718 18552
 sys/src/fs/pc/ether82557.c - 664 sys sys 1037805116 24037
-sys/src/fs/pc/ether83815.c - 664 sys sys 1037805116 27025
-sys/src/fs/pc/etherdp83820.c - 664 sys sys 1084469622 30749
+sys/src/fs/pc/ether83815.c - 664 sys sys 1079341568 28160
+sys/src/fs/pc/ether83815.mii.c - 664 sys sys 1049156663 31622
+sys/src/fs/pc/etherdp83820.c - 664 sys sys 1092268058 30976
 sys/src/fs/pc/etherelnk3.c - 664 sys sys 1015110054 42732
-sys/src/fs/pc/etherga620.c - 664 sys sys 1032126774 24721
+sys/src/fs/pc/etherga620.c - 664 sys sys 1140166315 24732
 sys/src/fs/pc/etherga620fw.h - 664 sys sys 1032126775 222295
-sys/src/fs/pc/etherif.c - 664 sys sys 1037805117 6324
+sys/src/fs/pc/etherif.c - 664 sys sys 1140162742 6375
 sys/src/fs/pc/etherif.h - 664 sys sys 1015110057 730
-sys/src/fs/pc/ethermii.c - 664 sys sys 1037805117 1247
-sys/src/fs/pc/ethermii.h - 664 sys sys 1037805117 980
-sys/src/fs/pc/floppy.c - 664 sys sys 1047663990 13993
+sys/src/fs/pc/etherigbe.c - 664 sys sys 1131948319 46571
+sys/src/fs/pc/ethermii.c - 664 sys sys 1140167954 4689
+sys/src/fs/pc/ethermii.h - 664 sys sys 1140167955 3259
+sys/src/fs/pc/floppy.c - 664 sys sys 1097579636 14022
 sys/src/fs/pc/kbd.c - 664 sys sys 1015110060 6030
-sys/src/fs/pc/l.s - 664 sys sys 1032062001 10656
+sys/src/fs/pc/l.s - 664 sys sys 1096008336 10838
 sys/src/fs/pc/lock.c - 664 sys sys 1045502910 3591
-sys/src/fs/pc/malloc.c - 664 sys sys 1037805118 2854
-sys/src/fs/pc/mkfile - 664 sys sys 1037805118 369
+sys/src/fs/pc/malloc.c - 664 sys sys 1096789023 2840
+sys/src/fs/pc/mkfile - 664 sys sys 1140167942 426
 sys/src/fs/pc/mmu.c - 664 sys sys 1015110065 8414
 sys/src/fs/pc/nvr.c - 664 sys sys 1015110066 669
-sys/src/fs/pc/pc.c - 664 sys sys 1047663991 7938
-sys/src/fs/pc/pci.c - 664 sys sys 1037805118 11638
+sys/src/fs/pc/pc.c - 664 sys sys 1140167943 7985
+sys/src/fs/pc/pci.c - 664 sys sys 1094949424 12118
 sys/src/fs/pc/script.i - 664 sys sys 1015110072 27323
 sys/src/fs/pc/scsi.c - 664 sys sys 1091803674 8661
 sys/src/fs/pc/scsibuslogic.c - 664 sys sys 1015110077 28645
-sys/src/fs/pc/scsincr53c8xx.c - 664 sys sys 1037805119 53483
-sys/src/fs/pc/toy.c - 664 sys sys 1015110085 2166
+sys/src/fs/pc/scsincr53c8xx.c - 664 sys sys 1112488792 53673
+sys/src/fs/pc/sdata.c - 664 sys sys 1097712291 61793
+sys/src/fs/pc/sdscsi.c - 664 sys sys 1140164880 7042
+sys/src/fs/pc/toy.c - 664 sys sys 1140167948 2166
 sys/src/fs/pc/trap.c - 664 sys sys 1055699801 7946
 sys/src/fs/port - 20000000775 sys sys 1015110029 0
-sys/src/fs/port/9p1.c - 664 sys sys 1015109993 30114
-sys/src/fs/port/9p1.h - 664 sys sys 1015109994 2122
-sys/src/fs/port/9p1lib.c - 664 sys sys 1015109996 7629
-sys/src/fs/port/9p2.c - 664 sys sys 1044290935 36142
-sys/src/fs/port/all.h - 664 sys sys 1045502862 1927
-sys/src/fs/port/auth.c - 664 sys sys 1041361345 7608
-sys/src/fs/port/chk.c - 664 sys sys 1047663951 14533
-sys/src/fs/port/clock.c - 664 sys sys 1015110006 4033
-sys/src/fs/port/con.c - 664 sys sys 1037805158 16254
-sys/src/fs/port/config.c - 664 sys sys 1047663951 18054
-sys/src/fs/port/console.c - 664 sys sys 1015110011 4821
-sys/src/fs/port/data.c - 664 sys sys 1041361346 4411
-sys/src/fs/port/dentry.c - 664 sys sys 1015110013 3801
-sys/src/fs/port/devcons.c - 664 sys sys 1015110014 4329
+sys/src/fs/port/9p1.c - 664 sys sys 1140167971 29982
+sys/src/fs/port/9p1.h - 664 sys sys 1140167972 2128
+sys/src/fs/port/9p1lib.c - 664 sys sys 1101627232 7911
+sys/src/fs/port/9p2.c - 664 sys sys 1098094593 36248
+sys/src/fs/port/all.h - 664 sys sys 1140167977 1932
+sys/src/fs/port/auth.c - 664 sys sys 1097573906 7609
+sys/src/fs/port/chk.c - 664 sys sys 1097832483 15683
+sys/src/fs/port/clock.c - 664 sys sys 1097580538 4033
+sys/src/fs/port/con.c - 664 sys sys 1097580641 16327
+sys/src/fs/port/config.c - 664 sys sys 1107835943 20264
+sys/src/fs/port/console.c - 664 sys sys 1101627646 4886
+sys/src/fs/port/data.c - 664 sys sys 1140167986 4510
+sys/src/fs/port/dentry.c - 664 sys sys 1098156404 6345
+sys/src/fs/port/devcons.c - 664 sys sys 1095983755 4451
+sys/src/fs/port/devsd.c - 664 sys sys 1097712378 10993
 sys/src/fs/port/fcmd.c - 664 sys sys 1015110015 1266
-sys/src/fs/port/iobuf.c - 664 sys sys 1045502864 4845
-sys/src/fs/port/lib.h - 664 sys sys 1091803590 3339
+sys/src/fs/port/fs.h - 664 sys sys 1140168001 682
+sys/src/fs/port/iobuf.c - 664 sys sys 1140167988 4956
+sys/src/fs/port/lib.h - 664 sys sys 1091960844 3701
 sys/src/fs/port/lrand.c - 664 sys sys 1091803568 1093
-sys/src/fs/port/main.c - 664 sys sys 1047663952 5698
-sys/src/fs/port/mkfile - 664 sys sys 1015110018 189
-sys/src/fs/port/portdat.h - 664 sys sys 1091849428 16750
-sys/src/fs/port/portfns.h - 664 sys sys 1037805159 7454
+sys/src/fs/port/main.c - 664 sys sys 1140167990 7639
+sys/src/fs/port/mkfile - 664 sys sys 1140167991 214
+sys/src/fs/port/portdat.h - 664 sys sys 1140163786 19670
+sys/src/fs/port/portfns.h - 664 sys sys 1140167994 7526
 sys/src/fs/port/print.c - 664 sys sys 1015110022 153
-sys/src/fs/port/proc.c - 664 sys sys 1015110023 5244
-sys/src/fs/port/sub.c - 664 sys sys 1045502866 23708
-sys/src/fs/port/time.c - 664 sys sys 1041361347 6179
-sys/src/fs/port/uidgid.c - 664 sys sys 1015110030 9194
-sys/src/fs/roro - 20000000775 sys sys 1015110093 0
-sys/src/fs/roro/9rorofs.c - 664 sys sys 1041361248 2914
-sys/src/fs/roro/dat.h - 664 sys sys 1037805167 502
-sys/src/fs/roro/fns.h - 664 sys sys 1037805167 1968
-sys/src/fs/roro/io.h - 664 sys sys 1037805168 6576
-sys/src/fs/roro/mem.h - 664 sys sys 1015110092 2865
-sys/src/fs/roro/mkfile - 664 sys sys 1091803629 1650
-sys/src/fs/sony - 20000000775 sys sys 1015110104 0
-sys/src/fs/sony/9sonyfs.c - 664 sys sys 1041361261 2962
-sys/src/fs/sony/dat.h - 664 sys sys 1037805175 490
-sys/src/fs/sony/fns.h - 664 sys sys 1037805175 1968
-sys/src/fs/sony/io.h - 664 sys sys 1037805175 6576
-sys/src/fs/sony/mem.h - 664 sys sys 1015110103 2865
-sys/src/fs/sony/mkfile - 664 sys sys 1091803623 1594
-sys/src/fs/worms - 664 sys sys 1015110031 2806
+sys/src/fs/port/proc.c - 664 sys sys 1097580775 5364
+sys/src/fs/port/sd.h - 664 sys sys 1097577251 2167
+sys/src/fs/port/sub.c - 664 sys sys 1098157264 25628
+sys/src/fs/port/time.c - 664 sys sys 1140167998 6222
+sys/src/fs/port/uidgid.c - 664 sys sys 1097574050 9192
 sys/src/games - 20000000775 sys sys 1095792091 0
 sys/src/games/4s.c - 664 sys sys 1128271724 1285
 sys/src/games/5s.c - 664 sys sys 1128271724 3621
@@ -14397,7 +14424,7 @@ sys/src/games/mahjongg/mahjongg.c - 664 sys sys 1137080360 3631
 sys/src/games/mahjongg/mahjongg.h - 664 sys sys 1137080360 1687
 sys/src/games/mahjongg/mkfile - 664 sys sys 1095792293 230
 sys/src/games/memo.c - 664 sys sys 1130853201 6471
-sys/src/games/mkfile - 664 sys sys 1128271766 586
+sys/src/games/mkfile - 664 sys sys 1140203009 595
 sys/src/games/music - 20000000775 sys sys 1103793915 0
 sys/src/games/music/Readme - 664 sys sys 1103793914 488
 sys/src/games/music/debug.h - 664 sys sys 1103793914 201

+ 125 - 98
dist/replica/plan9.db

@@ -5635,18 +5635,20 @@ sys/doc/fossil.ms - 664 sys sys 1138396406 31439
 sys/doc/fossil.pdf - 664 sys sys 1042123169 63200
 sys/doc/fossil.ps - 664 sys sys 1135487951 313552
 sys/doc/fs - 20000000775 sys sys 945616779 0
-sys/doc/fs/fs.pdf - 664 sys sys 1020384351 47177
-sys/doc/fs/fs.ps - 664 sys sys 960837905 276918
-sys/doc/fs/mkfile - 664 sys sys 1138459286 273
-sys/doc/fs/p0 - 664 sys sys 1138396562 682
-sys/doc/fs/p1 - 664 sys sys 953844581 989
-sys/doc/fs/p2 - 664 sys sys 953844581 3749
+sys/doc/fs/fs.pdf - 664 sys sys 1140157478 64965
+sys/doc/fs/fs.ps - 664 sys sys 1140157389 298023
+sys/doc/fs/mkfile - 664 sys sys 1140157064 366
+sys/doc/fs/p0 - 664 sys sys 1138445459 1677
+sys/doc/fs/p1 - 664 sys sys 1094002291 800
+sys/doc/fs/p2 - 664 sys sys 1094112719 3775
 sys/doc/fs/p3 - 664 sys sys 944959592 1475
-sys/doc/fs/p4 - 664 sys sys 953844581 3662
-sys/doc/fs/p5 - 664 sys sys 944959592 1143
-sys/doc/fs/p6 - 664 sys sys 953844573 6235
-sys/doc/fs/p7 - 664 sys sys 953844581 958
+sys/doc/fs/p4 - 664 sys sys 1094178190 5764
+sys/doc/fs/p5 - 664 sys sys 1094002662 1140
+sys/doc/fs/p6 - 664 sys sys 1094177480 6091
+sys/doc/fs/p7 - 664 sys sys 1094000398 960
 sys/doc/fs/p8 - 664 sys sys 953844574 881
+sys/doc/fs/p9 - 664 sys sys 1140157346 659
+sys/doc/fs/pa - 664 sys sys 1094002902 2027
 sys/doc/il - 20000000775 sys sys 945616779 0
 sys/doc/il/il.ms - 664 sys sys 1138396552 11390
 sys/doc/il/il.pdf - 664 sys sys 1020384351 44630
@@ -7583,7 +7585,7 @@ sys/man/4/acme - 664 sys sys 1134233060 10274
 sys/man/4/archfs - 664 sys sys 960000712 533
 sys/man/4/cdfs - 664 sys sys 1026846913 3638
 sys/man/4/cfs - 664 sys sys 1015024813 1758
-sys/man/4/consolefs - 664 sys sys 1069179473 3920
+sys/man/4/consolefs - 664 sys sys 1140209468 4241
 sys/man/4/dossrv - 664 sys sys 1015024813 4176
 sys/man/4/execnet - 664 sys sys 1019866708 1069
 sys/man/4/exportfs - 664 sys sys 1115314261 4653
@@ -7696,7 +7698,7 @@ sys/man/8/dhcpd - 664 sys sys 1032654987 5237
 sys/man/8/drawterm - 664 sys sys 1135901219 1741
 sys/man/8/fossilcons - 664 sys sys 1138466274 18276
 sys/man/8/fs - 664 sys sys 1055701170 15029
-sys/man/8/fsconfig - 664 sys sys 1045501600 8142
+sys/man/8/fsconfig - 664 sys sys 1107841165 8350
 sys/man/8/getflags - 664 sys sys 1139839200 1712
 sys/man/8/gpsfs - 664 sys sys 1123643451 4924
 sys/man/8/httpd - 664 sys sys 1121198377 6675
@@ -14269,120 +14271,145 @@ sys/src/cmd/winwatch.c - 664 sys sys 1113312596 4869
 sys/src/cmd/xd.c - 664 sys sys 1124993597 5770
 sys/src/cmd/yacc.c - 664 sys sys 1116770313 57945
 sys/src/fs - 20000000775 sys sys 1015110109 0
+sys/src/fs/9netics32.16k - 20000000775 sys sys 1140158691 0
+sys/src/fs/9netics32.16k/9net32.16kfs.c - 664 sys sys 1140167903 3208
+sys/src/fs/9netics32.16k/dat.h - 664 sys sys 1140167904 612
+sys/src/fs/9netics32.16k/fns.h - 664 sys sys 1140167905 1961
+sys/src/fs/9netics32.16k/io.h - 664 sys sys 1097661949 6688
+sys/src/fs/9netics32.16k/mem.h - 664 sys sys 1140167906 2907
+sys/src/fs/9netics32.16k/mkfile - 664 sys sys 1140167908 1615
+sys/src/fs/9netics64.8k - 20000000775 sys sys 1140158692 0
+sys/src/fs/9netics64.8k/9net64.8kfs.c - 664 sys sys 1140167920 3235
+sys/src/fs/9netics64.8k/dat.h - 664 sys sys 1095751680 611
+sys/src/fs/9netics64.8k/fns.h - 664 sys sys 1140167922 1961
+sys/src/fs/9netics64.8k/io.h - 664 sys sys 1097661949 6688
+sys/src/fs/9netics64.8k/mem.h - 664 sys sys 1140167924 2907
+sys/src/fs/9netics64.8k/mkfile - 664 sys sys 1140167925 1608
 sys/src/fs/choline - 20000000775 sys sys 1015110109 0
-sys/src/fs/choline/9cholinefs.c - 664 sys sys 1041361226 2962
-sys/src/fs/choline/dat.h - 664 sys sys 1037805081 528
-sys/src/fs/choline/fns.h - 664 sys sys 1037805081 1968
-sys/src/fs/choline/io.h - 664 sys sys 1037805081 6576
-sys/src/fs/choline/mem.h - 664 sys sys 1015110108 2865
-sys/src/fs/choline/mkfile - 664 sys sys 1091803608 1676
+sys/src/fs/choline/9cholinefs.c - 664 sys sys 1140168020 2989
+sys/src/fs/choline/dat.h - 664 sys sys 1140168017 612
+sys/src/fs/choline/fns.h - 664 sys sys 1140168018 1961
+sys/src/fs/choline/io.h - 664 sys sys 1140168018 6688
+sys/src/fs/choline/mem.h - 664 sys sys 1140168019 2909
+sys/src/fs/choline/mkfile - 664 sys sys 1140168021 1751
 sys/src/fs/dev - 20000000775 sys sys 1015109978 0
-sys/src/fs/dev/cw.c - 664 sys sys 1047663897 42252
-sys/src/fs/dev/fworm.c - 664 sys sys 1015109971 1737
-sys/src/fs/dev/juke.c - 664 sys sys 1055699807 22892
+sys/src/fs/dev/apc.c - 664 sys sys 1140167918 14910
+sys/src/fs/dev/cw.c - 664 sys sys 1097712558 43252
+sys/src/fs/dev/fworm.c - 664 sys sys 1140167912 1845
+sys/src/fs/dev/juke.c - 664 sys sys 1097578476 22885
 sys/src/fs/dev/mkfile - 664 sys sys 1015109974 132
-sys/src/fs/dev/mworm.c - 664 sys sys 1037805087 4138
-sys/src/fs/dev/sony.c - 664 sys sys 1037805088 17049
-sys/src/fs/dev/wren.c - 664 sys sys 1015109978 2327
+sys/src/fs/dev/mworm.c - 664 sys sys 1140167915 4264
+sys/src/fs/dev/wren.c - 664 sys sys 1140167916 2392
+sys/src/fs/doc - 20000000775 sys sys 1140158639 0
+sys/src/fs/doc/changes - 664 sys sys 1140168029 2967
+sys/src/fs/doc/words - 664 sys sys 1140168029 507
+sys/src/fs/doc/worm.fs - 664 sys sys 1140168030 4057
+sys/src/fs/doc/worm.fs64 - 664 sys sys 1101811981 1554
+sys/src/fs/doc/worms.32-bit - 664 sys sys 1140168032 2806
 sys/src/fs/emelie - 20000000775 sys sys 1015110098 0
-sys/src/fs/emelie/9pcfs.c - 664 sys sys 1041361237 2962
-sys/src/fs/emelie/dat.h - 664 sys sys 1037805101 502
-sys/src/fs/emelie/fns.h - 664 sys sys 1037805101 1968
-sys/src/fs/emelie/io.h - 664 sys sys 1037805101 6576
-sys/src/fs/emelie/mem.h - 664 sys sys 1015110098 2865
-sys/src/fs/emelie/mkfile - 664 sys sys 1091803614 1697
+sys/src/fs/emelie/9emeliefs.c - 664 sys sys 1140168026 2989
+sys/src/fs/emelie/dat.h - 664 sys sys 1140168023 612
+sys/src/fs/emelie/fns.h - 664 sys sys 1140168023 1961
+sys/src/fs/emelie/io.h - 664 sys sys 1140168024 6688
+sys/src/fs/emelie/mem.h - 664 sys sys 1140168025 2909
+sys/src/fs/emelie/mkfile - 664 sys sys 1140168027 1750
+sys/src/fs/fs - 20000000775 sys sys 1140158693 0
+sys/src/fs/fs/9fsfs.c - 664 sys sys 1140168015 3308
+sys/src/fs/fs/dat.h - 664 sys sys 1140168010 611
+sys/src/fs/fs/fns.h - 664 sys sys 1140168011 1961
+sys/src/fs/fs/io.h - 664 sys sys 1097661949 6688
+sys/src/fs/fs/mem.h - 664 sys sys 1140168013 2909
+sys/src/fs/fs/mkfile - 664 sys sys 1140168014 1663
+sys/src/fs/fs64 - 20000000775 sys sys 1140158693 0
+sys/src/fs/fs64/9fsfs64.c - 664 sys sys 1140168004 3294
+sys/src/fs/fs64/dat.h - 664 sys sys 1140168005 611
+sys/src/fs/fs64/fns.h - 664 sys sys 1140168005 1961
+sys/src/fs/fs64/io.h - 664 sys sys 1140168006 6688
+sys/src/fs/fs64/mem.h - 664 sys sys 1020579319 2909
+sys/src/fs/fs64/mkfile - 664 sys sys 1140168008 1603
 sys/src/fs/ip - 20000000775 sys sys 1015109990 0
-sys/src/fs/ip/arp.c - 664 sys sys 1015109981 8696
+sys/src/fs/ip/arp.c - 664 sys sys 1097578954 8696
 sys/src/fs/ip/icmp.c - 664 sys sys 1015109981 991
-sys/src/fs/ip/il.c - 664 sys sys 1015109983 19423
-sys/src/fs/ip/ip.c - 664 sys sys 1015109984 3859
-sys/src/fs/ip/ip.h - 664 sys sys 1015109985 6691
+sys/src/fs/ip/il.c - 664 sys sys 1140167928 19479
+sys/src/fs/ip/ip.c - 664 sys sys 1097578933 3859
+sys/src/fs/ip/ip.h - 664 sys sys 1140167930 6691
 sys/src/fs/ip/ipaux.c - 664 sys sys 1015109986 1041
-sys/src/fs/ip/iproute.c - 664 sys sys 1015109987 7421
+sys/src/fs/ip/iproute.c - 664 sys sys 1097579099 7386
 sys/src/fs/ip/mkfile - 664 sys sys 1015109988 127
 sys/src/fs/ip/sntp.c - 664 sys sys 1037805108 5539
 sys/src/fs/ip/udp.c - 664 sys sys 1015109990 754
-sys/src/fs/mkfile - 664 sys sys 1015110109 195
+sys/src/fs/mkfile - 664 sys sys 1140167932 231
 sys/src/fs/pc - 20000000775 sys sys 1015110085 0
-sys/src/fs/pc/8250.c - 664 sys sys 1015110032 6795
-sys/src/fs/pc/8253.c - 664 sys sys 1032062036 6206
+sys/src/fs/pc/8250.c - 664 sys sys 1140161725 6957
+sys/src/fs/pc/8253.c - 664 sys sys 1097579039 7264
 sys/src/fs/pc/cga.c - 664 sys sys 1015110034 1484
-sys/src/fs/pc/compat.c - 664 sys sys 1037805115 546
-sys/src/fs/pc/compat.h - 664 sys sys 1039794577 1636
-sys/src/fs/pc/devata.c - 664 sys sys 1037805116 26981
-sys/src/fs/pc/dosfs.c - 664 sys sys 1015110040 16461
-sys/src/fs/pc/dosfs.h - 664 sys sys 1037805116 2667
+sys/src/fs/pc/compat.c - 664 sys sys 1140164905 1575
+sys/src/fs/pc/compat.h - 664 sys sys 1140167957 2426
+sys/src/fs/pc/dosfs.c - 664 sys sys 1097712365 16637
+sys/src/fs/pc/dosfs.h - 664 sys sys 1140167937 2696
 sys/src/fs/pc/ether2114x.c - 664 sys sys 1015110044 31874
-sys/src/fs/pc/ether8139.c - 664 sys sys 1039794573 18318
+sys/src/fs/pc/ether8139.c - 664 sys sys 1086569718 18552
 sys/src/fs/pc/ether82557.c - 664 sys sys 1037805116 24037
-sys/src/fs/pc/ether83815.c - 664 sys sys 1037805116 27025
-sys/src/fs/pc/etherdp83820.c - 664 sys sys 1084469622 30749
+sys/src/fs/pc/ether83815.c - 664 sys sys 1079341568 28160
+sys/src/fs/pc/ether83815.mii.c - 664 sys sys 1049156663 31622
+sys/src/fs/pc/etherdp83820.c - 664 sys sys 1092268058 30976
 sys/src/fs/pc/etherelnk3.c - 664 sys sys 1015110054 42732
-sys/src/fs/pc/etherga620.c - 664 sys sys 1032126774 24721
+sys/src/fs/pc/etherga620.c - 664 sys sys 1140166315 24732
 sys/src/fs/pc/etherga620fw.h - 664 sys sys 1032126775 222295
-sys/src/fs/pc/etherif.c - 664 sys sys 1037805117 6324
+sys/src/fs/pc/etherif.c - 664 sys sys 1140162742 6375
 sys/src/fs/pc/etherif.h - 664 sys sys 1015110057 730
-sys/src/fs/pc/ethermii.c - 664 sys sys 1037805117 1247
-sys/src/fs/pc/ethermii.h - 664 sys sys 1037805117 980
-sys/src/fs/pc/floppy.c - 664 sys sys 1047663990 13993
+sys/src/fs/pc/etherigbe.c - 664 sys sys 1131948319 46571
+sys/src/fs/pc/ethermii.c - 664 sys sys 1140167954 4689
+sys/src/fs/pc/ethermii.h - 664 sys sys 1140167955 3259
+sys/src/fs/pc/floppy.c - 664 sys sys 1097579636 14022
 sys/src/fs/pc/kbd.c - 664 sys sys 1015110060 6030
-sys/src/fs/pc/l.s - 664 sys sys 1032062001 10656
+sys/src/fs/pc/l.s - 664 sys sys 1096008336 10838
 sys/src/fs/pc/lock.c - 664 sys sys 1045502910 3591
-sys/src/fs/pc/malloc.c - 664 sys sys 1037805118 2854
-sys/src/fs/pc/mkfile - 664 sys sys 1037805118 369
+sys/src/fs/pc/malloc.c - 664 sys sys 1096789023 2840
+sys/src/fs/pc/mkfile - 664 sys sys 1140167942 426
 sys/src/fs/pc/mmu.c - 664 sys sys 1015110065 8414
 sys/src/fs/pc/nvr.c - 664 sys sys 1015110066 669
-sys/src/fs/pc/pc.c - 664 sys sys 1047663991 7938
-sys/src/fs/pc/pci.c - 664 sys sys 1037805118 11638
+sys/src/fs/pc/pc.c - 664 sys sys 1140167943 7985
+sys/src/fs/pc/pci.c - 664 sys sys 1094949424 12118
 sys/src/fs/pc/script.i - 664 sys sys 1015110072 27323
 sys/src/fs/pc/scsi.c - 664 sys sys 1091803674 8661
 sys/src/fs/pc/scsibuslogic.c - 664 sys sys 1015110077 28645
-sys/src/fs/pc/scsincr53c8xx.c - 664 sys sys 1037805119 53483
-sys/src/fs/pc/toy.c - 664 sys sys 1015110085 2166
+sys/src/fs/pc/scsincr53c8xx.c - 664 sys sys 1112488792 53673
+sys/src/fs/pc/sdata.c - 664 sys sys 1097712291 61793
+sys/src/fs/pc/sdscsi.c - 664 sys sys 1140164880 7042
+sys/src/fs/pc/toy.c - 664 sys sys 1140167948 2166
 sys/src/fs/pc/trap.c - 664 sys sys 1055699801 7946
 sys/src/fs/port - 20000000775 sys sys 1015110029 0
-sys/src/fs/port/9p1.c - 664 sys sys 1015109993 30114
-sys/src/fs/port/9p1.h - 664 sys sys 1015109994 2122
-sys/src/fs/port/9p1lib.c - 664 sys sys 1015109996 7629
-sys/src/fs/port/9p2.c - 664 sys sys 1044290935 36142
-sys/src/fs/port/all.h - 664 sys sys 1045502862 1927
-sys/src/fs/port/auth.c - 664 sys sys 1041361345 7608
-sys/src/fs/port/chk.c - 664 sys sys 1047663951 14533
-sys/src/fs/port/clock.c - 664 sys sys 1015110006 4033
-sys/src/fs/port/con.c - 664 sys sys 1037805158 16254
-sys/src/fs/port/config.c - 664 sys sys 1047663951 18054
-sys/src/fs/port/console.c - 664 sys sys 1015110011 4821
-sys/src/fs/port/data.c - 664 sys sys 1041361346 4411
-sys/src/fs/port/dentry.c - 664 sys sys 1015110013 3801
-sys/src/fs/port/devcons.c - 664 sys sys 1015110014 4329
+sys/src/fs/port/9p1.c - 664 sys sys 1140167971 29982
+sys/src/fs/port/9p1.h - 664 sys sys 1140167972 2128
+sys/src/fs/port/9p1lib.c - 664 sys sys 1101627232 7911
+sys/src/fs/port/9p2.c - 664 sys sys 1098094593 36248
+sys/src/fs/port/all.h - 664 sys sys 1140167977 1932
+sys/src/fs/port/auth.c - 664 sys sys 1097573906 7609
+sys/src/fs/port/chk.c - 664 sys sys 1097832483 15683
+sys/src/fs/port/clock.c - 664 sys sys 1097580538 4033
+sys/src/fs/port/con.c - 664 sys sys 1097580641 16327
+sys/src/fs/port/config.c - 664 sys sys 1107835943 20264
+sys/src/fs/port/console.c - 664 sys sys 1101627646 4886
+sys/src/fs/port/data.c - 664 sys sys 1140167986 4510
+sys/src/fs/port/dentry.c - 664 sys sys 1098156404 6345
+sys/src/fs/port/devcons.c - 664 sys sys 1095983755 4451
+sys/src/fs/port/devsd.c - 664 sys sys 1097712378 10993
 sys/src/fs/port/fcmd.c - 664 sys sys 1015110015 1266
-sys/src/fs/port/iobuf.c - 664 sys sys 1045502864 4845
-sys/src/fs/port/lib.h - 664 sys sys 1091803590 3339
+sys/src/fs/port/fs.h - 664 sys sys 1140168001 682
+sys/src/fs/port/iobuf.c - 664 sys sys 1140167988 4956
+sys/src/fs/port/lib.h - 664 sys sys 1091960844 3701
 sys/src/fs/port/lrand.c - 664 sys sys 1091803568 1093
-sys/src/fs/port/main.c - 664 sys sys 1047663952 5698
-sys/src/fs/port/mkfile - 664 sys sys 1015110018 189
-sys/src/fs/port/portdat.h - 664 sys sys 1091849428 16750
-sys/src/fs/port/portfns.h - 664 sys sys 1037805159 7454
+sys/src/fs/port/main.c - 664 sys sys 1140167990 7639
+sys/src/fs/port/mkfile - 664 sys sys 1140167991 214
+sys/src/fs/port/portdat.h - 664 sys sys 1140163786 19670
+sys/src/fs/port/portfns.h - 664 sys sys 1140167994 7526
 sys/src/fs/port/print.c - 664 sys sys 1015110022 153
-sys/src/fs/port/proc.c - 664 sys sys 1015110023 5244
-sys/src/fs/port/sub.c - 664 sys sys 1045502866 23708
-sys/src/fs/port/time.c - 664 sys sys 1041361347 6179
-sys/src/fs/port/uidgid.c - 664 sys sys 1015110030 9194
-sys/src/fs/roro - 20000000775 sys sys 1015110093 0
-sys/src/fs/roro/9rorofs.c - 664 sys sys 1041361248 2914
-sys/src/fs/roro/dat.h - 664 sys sys 1037805167 502
-sys/src/fs/roro/fns.h - 664 sys sys 1037805167 1968
-sys/src/fs/roro/io.h - 664 sys sys 1037805168 6576
-sys/src/fs/roro/mem.h - 664 sys sys 1015110092 2865
-sys/src/fs/roro/mkfile - 664 sys sys 1091803629 1650
-sys/src/fs/sony - 20000000775 sys sys 1015110104 0
-sys/src/fs/sony/9sonyfs.c - 664 sys sys 1041361261 2962
-sys/src/fs/sony/dat.h - 664 sys sys 1037805175 490
-sys/src/fs/sony/fns.h - 664 sys sys 1037805175 1968
-sys/src/fs/sony/io.h - 664 sys sys 1037805175 6576
-sys/src/fs/sony/mem.h - 664 sys sys 1015110103 2865
-sys/src/fs/sony/mkfile - 664 sys sys 1091803623 1594
-sys/src/fs/worms - 664 sys sys 1015110031 2806
+sys/src/fs/port/proc.c - 664 sys sys 1097580775 5364
+sys/src/fs/port/sd.h - 664 sys sys 1097577251 2167
+sys/src/fs/port/sub.c - 664 sys sys 1098157264 25628
+sys/src/fs/port/time.c - 664 sys sys 1140167998 6222
+sys/src/fs/port/uidgid.c - 664 sys sys 1097574050 9192
 sys/src/games - 20000000775 sys sys 1095792091 0
 sys/src/games/4s.c - 664 sys sys 1128271724 1285
 sys/src/games/5s.c - 664 sys sys 1128271724 3621
@@ -14397,7 +14424,7 @@ sys/src/games/mahjongg/mahjongg.c - 664 sys sys 1137080360 3631
 sys/src/games/mahjongg/mahjongg.h - 664 sys sys 1137080360 1687
 sys/src/games/mahjongg/mkfile - 664 sys sys 1095792293 230
 sys/src/games/memo.c - 664 sys sys 1130853201 6471
-sys/src/games/mkfile - 664 sys sys 1128271766 586
+sys/src/games/mkfile - 664 sys sys 1140203009 595
 sys/src/games/music - 20000000775 sys sys 1103793915 0
 sys/src/games/music/Readme - 664 sys sys 1103793914 488
 sys/src/games/music/debug.h - 664 sys sys 1103793914 201

+ 143 - 0
dist/replica/plan9.log

@@ -27617,3 +27617,146 @@
 1140112894 0 c 386/bin/iostats - 775 sys sys 1140112236 100160
 1140121896 0 c 386/bin/fossil/fossil - 775 sys sys 1140121256 363650
 1140139901 0 d sys/doc/fs/xx - 664 sys sys 944959592 0
+1140157906 0 c sys/doc/fs/fs.pdf - 664 sys sys 1140157478 64965
+1140157906 1 c sys/doc/fs/fs.ps - 664 sys sys 1140157389 298023
+1140157906 2 c sys/doc/fs/mkfile - 664 sys sys 1140157064 366
+1140157906 3 c sys/doc/fs/p0 - 664 sys sys 1138445459 1677
+1140157906 4 c sys/doc/fs/p1 - 664 sys sys 1094002291 800
+1140157906 5 c sys/doc/fs/p2 - 664 sys sys 1094112719 3775
+1140157906 6 c sys/doc/fs/p4 - 664 sys sys 1094178190 5764
+1140157906 7 c sys/doc/fs/p5 - 664 sys sys 1094002662 1140
+1140157906 8 c sys/doc/fs/p6 - 664 sys sys 1094177480 6091
+1140157906 9 c sys/doc/fs/p7 - 664 sys sys 1094000398 960
+1140157906 10 a sys/doc/fs/p9 - 664 sys sys 1140157346 659
+1140157906 11 a sys/doc/fs/pa - 664 sys sys 1094002902 2027
+1140157906 12 c sys/man/8/fsconfig - 664 sys sys 1107841165 8350
+1140159707 0 a sys/src/fs/9netics32.16k - 20000000775 sys sys 1140158691 0
+1140159707 1 a sys/src/fs/9netics64.8k - 20000000775 sys sys 1140158692 0
+1140159707 2 a sys/src/fs/doc - 20000000775 sys sys 1140158639 0
+1140159707 3 a sys/src/fs/fs - 20000000775 sys sys 1140158693 0
+1140159707 4 a sys/src/fs/fs64 - 20000000775 sys sys 1140158693 0
+1140159707 5 d sys/src/fs/sony/mkfile - 664 sys sys 1091803623 0
+1140159707 6 d sys/src/fs/sony/mem.h - 664 sys sys 1015110103 0
+1140159707 7 d sys/src/fs/sony/io.h - 664 sys sys 1037805175 0
+1140159707 8 d sys/src/fs/sony/fns.h - 664 sys sys 1037805175 0
+1140159707 9 d sys/src/fs/sony/dat.h - 664 sys sys 1037805175 0
+1140159707 10 d sys/src/fs/sony/9sonyfs.c - 664 sys sys 1041361261 0
+1140159707 11 d sys/src/fs/sony - 20000000775 sys sys 1015110104 0
+1140159707 12 d sys/src/fs/roro/mkfile - 664 sys sys 1091803629 0
+1140159707 13 d sys/src/fs/roro/mem.h - 664 sys sys 1015110092 0
+1140159707 14 d sys/src/fs/roro/io.h - 664 sys sys 1037805168 0
+1140159707 15 d sys/src/fs/roro/fns.h - 664 sys sys 1037805167 0
+1140159707 16 d sys/src/fs/roro/dat.h - 664 sys sys 1037805167 0
+1140159707 17 d sys/src/fs/roro/9rorofs.c - 664 sys sys 1041361248 0
+1140159707 18 d sys/src/fs/roro - 20000000775 sys sys 1015110093 0
+1140168708 0 a sys/src/fs/9netics32.16k/9net32.16kfs.c - 664 sys sys 1140167903 3208
+1140168708 1 a sys/src/fs/9netics32.16k/dat.h - 664 sys sys 1140167904 612
+1140168708 2 a sys/src/fs/9netics32.16k/fns.h - 664 sys sys 1140167905 1961
+1140168708 3 a sys/src/fs/9netics32.16k/io.h - 664 sys sys 1097661949 6688
+1140168708 4 a sys/src/fs/9netics32.16k/mem.h - 664 sys sys 1140167906 2907
+1140168708 5 a sys/src/fs/9netics32.16k/mkfile - 664 sys sys 1140167908 1615
+1140168708 6 a sys/src/fs/9netics64.8k/9net64.8kfs.c - 664 sys sys 1140167920 3235
+1140168708 7 a sys/src/fs/9netics64.8k/dat.h - 664 sys sys 1095751680 611
+1140168708 8 a sys/src/fs/9netics64.8k/fns.h - 664 sys sys 1140167922 1961
+1140168708 9 a sys/src/fs/9netics64.8k/io.h - 664 sys sys 1097661949 6688
+1140168708 10 a sys/src/fs/9netics64.8k/mem.h - 664 sys sys 1140167924 2907
+1140168708 11 a sys/src/fs/9netics64.8k/mkfile - 664 sys sys 1140167925 1608
+1140168708 12 c sys/src/fs/choline/9cholinefs.c - 664 sys sys 1140168020 2989
+1140168708 13 c sys/src/fs/choline/dat.h - 664 sys sys 1140168017 612
+1140168708 14 c sys/src/fs/choline/fns.h - 664 sys sys 1140168018 1961
+1140168708 15 c sys/src/fs/choline/io.h - 664 sys sys 1140168018 6688
+1140168708 16 c sys/src/fs/choline/mem.h - 664 sys sys 1140168019 2909
+1140168708 17 c sys/src/fs/choline/mkfile - 664 sys sys 1140168021 1751
+1140168708 18 a sys/src/fs/dev/apc.c - 664 sys sys 1140167918 14910
+1140168708 19 c sys/src/fs/dev/cw.c - 664 sys sys 1097712558 43252
+1140168708 20 c sys/src/fs/dev/fworm.c - 664 sys sys 1140167912 1845
+1140168708 21 c sys/src/fs/dev/juke.c - 664 sys sys 1097578476 22885
+1140168708 22 c sys/src/fs/dev/mworm.c - 664 sys sys 1140167915 4264
+1140168708 23 c sys/src/fs/dev/wren.c - 664 sys sys 1140167916 2392
+1140168708 24 a sys/src/fs/doc/changes - 664 sys sys 1140168029 2967
+1140168708 25 a sys/src/fs/doc/words - 664 sys sys 1140168029 507
+1140168708 26 a sys/src/fs/doc/worm.fs - 664 sys sys 1140168030 4057
+1140168708 27 a sys/src/fs/doc/worm.fs64 - 664 sys sys 1101811981 1554
+1140168708 28 a sys/src/fs/doc/worms.32-bit - 664 sys sys 1140168032 2806
+1140168708 29 a sys/src/fs/emelie/9emeliefs.c - 664 sys sys 1140168026 2989
+1140168708 30 c sys/src/fs/emelie/dat.h - 664 sys sys 1140168023 612
+1140168708 31 c sys/src/fs/emelie/fns.h - 664 sys sys 1140168023 1961
+1140168708 32 c sys/src/fs/emelie/io.h - 664 sys sys 1140168024 6688
+1140168708 33 c sys/src/fs/emelie/mem.h - 664 sys sys 1140168025 2909
+1140168708 34 c sys/src/fs/emelie/mkfile - 664 sys sys 1140168027 1750
+1140168708 35 a sys/src/fs/fs/9fsfs.c - 664 sys sys 1140168015 3308
+1140168708 36 a sys/src/fs/fs/dat.h - 664 sys sys 1140168010 611
+1140168708 37 a sys/src/fs/fs/fns.h - 664 sys sys 1140168011 1961
+1140168708 38 a sys/src/fs/fs/io.h - 664 sys sys 1097661949 6688
+1140168708 39 a sys/src/fs/fs/mem.h - 664 sys sys 1140168013 2909
+1140168708 40 a sys/src/fs/fs/mkfile - 664 sys sys 1140168014 1663
+1140168708 41 a sys/src/fs/fs64/9fsfs64.c - 664 sys sys 1140168004 3294
+1140168708 42 a sys/src/fs/fs64/dat.h - 664 sys sys 1140168005 611
+1140168708 43 a sys/src/fs/fs64/fns.h - 664 sys sys 1140168005 1961
+1140168708 44 a sys/src/fs/fs64/io.h - 664 sys sys 1140168006 6688
+1140168708 45 a sys/src/fs/fs64/mem.h - 664 sys sys 1020579319 2909
+1140168708 46 a sys/src/fs/fs64/mkfile - 664 sys sys 1140168008 1603
+1140168708 47 c sys/src/fs/ip/arp.c - 664 sys sys 1097578954 8696
+1140168708 48 c sys/src/fs/ip/il.c - 664 sys sys 1140167928 19479
+1140168708 49 c sys/src/fs/ip/ip.c - 664 sys sys 1097578933 3859
+1140168708 50 c sys/src/fs/ip/ip.h - 664 sys sys 1140167930 6691
+1140168708 51 c sys/src/fs/ip/iproute.c - 664 sys sys 1097579099 7386
+1140168708 52 c sys/src/fs/mkfile - 664 sys sys 1140167932 231
+1140168708 53 c sys/src/fs/pc/8250.c - 664 sys sys 1140161725 6957
+1140168708 54 c sys/src/fs/pc/8253.c - 664 sys sys 1097579039 7264
+1140168708 55 c sys/src/fs/pc/compat.c - 664 sys sys 1140164905 1575
+1140168708 56 c sys/src/fs/pc/compat.h - 664 sys sys 1140167957 2426
+1140168708 57 c sys/src/fs/pc/dosfs.c - 664 sys sys 1097712365 16637
+1140168708 58 c sys/src/fs/pc/dosfs.h - 664 sys sys 1140167937 2696
+1140168708 59 c sys/src/fs/pc/ether8139.c - 664 sys sys 1086569718 18552
+1140168708 60 c sys/src/fs/pc/ether83815.c - 664 sys sys 1079341568 28160
+1140168708 61 a sys/src/fs/pc/ether83815.mii.c - 664 sys sys 1049156663 31622
+1140168708 62 c sys/src/fs/pc/etherdp83820.c - 664 sys sys 1092268058 30976
+1140168708 63 c sys/src/fs/pc/etherga620.c - 664 sys sys 1140166315 24732
+1140168708 64 c sys/src/fs/pc/etherif.c - 664 sys sys 1140162742 6375
+1140168708 65 a sys/src/fs/pc/etherigbe.c - 664 sys sys 1131948319 46571
+1140168708 66 c sys/src/fs/pc/ethermii.c - 664 sys sys 1140167954 4689
+1140168708 67 c sys/src/fs/pc/ethermii.h - 664 sys sys 1140167955 3259
+1140168708 68 c sys/src/fs/pc/floppy.c - 664 sys sys 1097579636 14022
+1140168708 69 c sys/src/fs/pc/l.s - 664 sys sys 1096008336 10838
+1140168708 70 c sys/src/fs/pc/malloc.c - 664 sys sys 1096789023 2840
+1140168708 71 c sys/src/fs/pc/mkfile - 664 sys sys 1140167942 426
+1140168708 72 c sys/src/fs/pc/pc.c - 664 sys sys 1140167943 7985
+1140168708 73 c sys/src/fs/pc/pci.c - 664 sys sys 1094949424 12118
+1140168708 74 c sys/src/fs/pc/scsincr53c8xx.c - 664 sys sys 1112488792 53673
+1140168708 75 a sys/src/fs/pc/sdata.c - 664 sys sys 1097712291 61793
+1140168708 76 a sys/src/fs/pc/sdscsi.c - 664 sys sys 1140164880 7042
+1140168708 77 c sys/src/fs/pc/toy.c - 664 sys sys 1140167948 2166
+1140168708 78 c sys/src/fs/port/9p1.c - 664 sys sys 1140167971 29982
+1140168708 79 c sys/src/fs/port/9p1.h - 664 sys sys 1140167972 2128
+1140168708 80 c sys/src/fs/port/9p1lib.c - 664 sys sys 1101627232 7911
+1140168708 81 c sys/src/fs/port/9p2.c - 664 sys sys 1098094593 36248
+1140168708 82 c sys/src/fs/port/all.h - 664 sys sys 1140167977 1932
+1140168708 83 c sys/src/fs/port/auth.c - 664 sys sys 1097573906 7609
+1140168708 84 c sys/src/fs/port/chk.c - 664 sys sys 1097832483 15683
+1140168708 85 c sys/src/fs/port/clock.c - 664 sys sys 1097580538 4033
+1140168708 86 c sys/src/fs/port/con.c - 664 sys sys 1097580641 16327
+1140168708 87 c sys/src/fs/port/config.c - 664 sys sys 1107835943 20264
+1140168708 88 c sys/src/fs/port/console.c - 664 sys sys 1101627646 4886
+1140168708 89 c sys/src/fs/port/data.c - 664 sys sys 1140167986 4510
+1140168708 90 c sys/src/fs/port/dentry.c - 664 sys sys 1098156404 6345
+1140168708 91 c sys/src/fs/port/devcons.c - 664 sys sys 1095983755 4451
+1140168708 92 a sys/src/fs/port/devsd.c - 664 sys sys 1097712378 10993
+1140168708 93 a sys/src/fs/port/fs.h - 664 sys sys 1140168001 682
+1140168708 94 c sys/src/fs/port/iobuf.c - 664 sys sys 1140167988 4956
+1140168708 95 c sys/src/fs/port/lib.h - 664 sys sys 1091960844 3701
+1140168708 96 c sys/src/fs/port/main.c - 664 sys sys 1140167990 7639
+1140168708 97 c sys/src/fs/port/mkfile - 664 sys sys 1140167991 214
+1140168708 98 c sys/src/fs/port/portdat.h - 664 sys sys 1140163786 19670
+1140168708 99 c sys/src/fs/port/portfns.h - 664 sys sys 1140167994 7526
+1140168708 100 c sys/src/fs/port/proc.c - 664 sys sys 1097580775 5364
+1140168708 101 a sys/src/fs/port/sd.h - 664 sys sys 1097577251 2167
+1140168708 102 c sys/src/fs/port/sub.c - 664 sys sys 1098157264 25628
+1140168708 103 c sys/src/fs/port/time.c - 664 sys sys 1140167998 6222
+1140168708 104 c sys/src/fs/port/uidgid.c - 664 sys sys 1097574050 9192
+1140168708 105 d sys/src/fs/worms - 664 sys sys 1015110031 0
+1140168708 106 d sys/src/fs/pc/devata.c - 664 sys sys 1037805116 0
+1140168708 107 d sys/src/fs/emelie/9pcfs.c - 664 sys sys 1041361237 0
+1140168708 108 d sys/src/fs/dev/sony.c - 664 sys sys 1037805088 0
+1140202918 0 c sys/src/games/mkfile - 664 sys sys 1140203009 595
+1140210121 0 c sys/man/4/consolefs - 664 sys sys 1140209468 4241

BIN
sys/doc/fs/fs.pdf


File diff suppressed because it is too large
+ 539 - 477
sys/doc/fs/fs.ps


+ 8 - 4
sys/doc/fs/mkfile

@@ -9,14 +9,18 @@ OBJ=\
 	p6\
 	p7\
 	p8\
+	p9\
+	pa\
 
 fs.ps:D:	$OBJ
-	{echo $FONTS; cat $OBJ } | troff -ms | lp -dstdout >fs.ps
-	../cleanps fs.ps
+	{echo $FONTS; tbl $OBJ } | eqn | troff -ms | lp -dstdout >$target
+	../cleanps $target
 
 fs.trout:D:	$OBJ
-	{echo $FONTS; cat $OBJ } | troff -ms >fs.trout
+	{echo $FONTS; tbl $OBJ } | eqn | troff -ms >$target
 
 fs.html:D:
-	htmlroff -ms -mhtml $OBJ >fs.html
+	{echo $FONTS; tbl $OBJ } | eqn | htmlroff -ms -mhtml >$target
 
+clean:V:
+	rm -f fs.^(html trout ps pdf)

+ 52 - 9
sys/doc/fs/p0

@@ -1,20 +1,51 @@
-.HTML "The Plan 9 File Server
+.HTML "The 64-bit Standalone Plan 9 File Server
+.de Ex
+.TA 0.5i 1i 1.5i 2i 2.5i 3i 3.5i 4i 4.5i 5i 5.5i
+.P1
+.TA 0.5i 1i 1.5i 2i 2.5i 3i 3.5i 4i 4.5i 5i 5.5i
+..
+.de Ee
+.P2
+..
+
+.EQ
+delim $$
+.EN
 .FP lucidasans
+
 .TL
-The Plan 9 File Server
+The 64-bit Standalone Plan 9 File Server
 .AU
-Ken Thompson
+Ken Thompson*
 ken@plan9.bell-labs.com
+.FS
+\l'4i'
+.br
+* now
+.CW ken@entrisphere.com
+.FE
+.AU
+Geoff Collyer
+.CW geoff@collyer.net
 .AB
-This paper describes the structure
-and the operation of Plan 9 file servers.
-The specifics apply to
-our main Plan 9 file server
+This paper is a revision of Thompson's
+.I "The Plan 9 File Server" ,
+and describes the structure
+and the operation of the new 64-bit Plan 9 file servers.
+Some specifics apply to the 32-bit
+Plan 9 file server
 Emelie,
-but
-the code is also the basis for
+which code is also the basis for
 the user level file server
 .CW kfs .
+.PP
+Collyer recently created a 64-bit version of
+Thompson's 32-bit file server, updating all file
+offsets, sizes and block numbers to 64 bits.
+In addition, triple- and quadruple-indirect
+blocks were implemented.
+File name components were extended from 27 to 55 bytes.
+Further work is planned, notably improved peripheral and protocol support.
 .AE
 .SH
 Introduction
@@ -30,3 +61,15 @@ portable,
 but it has slowly come to terms with
 its particular set of cranky computers
 and devices.
+.PP
+The file server
+.I fs64
+runs a revision of Emelie's code
+with 64-bit file sizes, offsets and block numbers
+and indirect blocks from single to quadruple.
+Actually these are 63-bit values, since the type used is
+.I vlong
+(signed
+.I "long long"
+integer),
+but 63 bits should suffice for a little while.

+ 23 - 15
sys/doc/fs/p1

@@ -14,18 +14,26 @@ virtual memory.
 The structure of the file system server
 is a set of kernel processes
 synchronizing mostly through message passing.
-In Emelie there are 26 processes of 10 types:
-.P1
-.ft R
-number name  function
-  15       \f(CWsrv\fP   Main file system server processes
-  \01       \f(CWrah\fP   Block read-ahead processes
-  \h'\w'0'u'1       \f(CWscp\fP   Sync process
-  \h'\w'0'u'1       \f(CWwcp\fP   WORM copy process
-  \h'\w'0'u'1       \f(CWcon\fP   Console process
-  \h'\w'0'u'1       \f(CWilo\fP   IL protocol process
-  \h'\w'0'u'1       \f(CWilt\fP   IL timer process
-  \h'\w'0'u'2       \f(CWethi\fP   Ethernet input process
-  \h'\w'0'u'2       \f(CWetho\fP   Ethernet output process
-  \h'\w'0'u'1       \f(CWflo\fP   Floppy disk process
-.P2
+In
+.I fs64
+there are 27 processes of 11 types:
+.KS
+.TS
+center ;
+c l c
+n lf(CW) l .
+number	name	function
+_
+15	srv	Main file system server processes
+1	rah	Block read-ahead processes
+1	scp	Sync process
+1	wcp	WORM copy process
+1	con	Console process
+1	ilo	IL protocol process
+1	ilt	IL timer process
+2	ethi	Ethernet input process
+2	etho	Ethernet output process
+1	flo	Floppy disk process
+1	snt	sntp clock-synchronisation process
+.TE
+.KE

+ 98 - 92
sys/doc/fs/p2

@@ -21,33 +21,36 @@ and sends the
 response back to the reply queue.
 .PP
 The unit of storage is a
-block of data on a device:
-.P1
-    enum
-    {
-        RBUFSIZE = 16*1024
-    };
+logical block
+(not physical sector) of data on a device:
+.Ex
+.TA 0.5i 1i 1.5i 2i 2.5i 3i 3.5i 4i 4.5i 5i 5.5i
+	enum
+	{
+		RBUFSIZE = 8*1024
+	};
 
-    typedef
-    struct
-    {
-        short   pad;
-        short	tag;
-        long	path;
-    } Tag;
+	typedef vlong Off;
+	typedef
+	struct
+	{
+		short	pad;
+		short	tag;
+		Off	path;
+	} Tag;
 
-    enum
-    {
-        BUFSIZE = RBUFSIZE - sizeof(Tag)
-    };
+	enum
+	{
+		BUFSIZE = RBUFSIZE - sizeof(Tag)
+	};
 
-    typedef
-    struct
-    {
-        uchar   data[BUFSIZE];
-        Tag     tag;
-    } Block;
-.P2
+	typedef
+	struct
+	{
+		uchar	data[BUFSIZE];
+		Tag	tag;
+	} Block;
+.Ee
 All devices are idealized as a perfect disk
 of contiguously numbered blocks each of size
 .CW RBUFSIZE .
@@ -65,31 +68,31 @@ defines the set of block addresses that comprise a file or directory.
 Unlike the i-node,
 the directory entry also has the name of the
 file or directory in it:
-.P1
-    enum
-    {
-        NAMELEN = 28,
-        NDBLOCK = 6
-    };
-.P2
-.P1
-    typedef
-    struct
-    {
-        char    name[NAMELEN];
-        short   uid;
-        short   gid;
-        ushort  mode;
-        short   wuid;
-        Qid     qid;
-        long    size;
-        long    dblock[NDBLOCK];
-        long    iblock;
-        long    diblock;
-        long    atime;
-        long    mtime;
-    } Dentry;
-.P2
+.Ex
+	enum
+	{
+		NAMELEN = 56,
+		NDBLOCK = 6,
+		NIBLOCK = 4,
+	};
+.Ee
+.Ex
+	typedef
+	struct
+	{
+		char	name[NAMELEN];
+		short	uid;
+		short	gid;
+		ushort	mode;
+		short	wuid;
+		Qid	qid;
+		Off	size;
+		Off	dblock[NDBLOCK];
+		Off	iblocks[NIBLOCK];
+		long	atime;
+		long	mtime;
+	} Dentry;
+.Ee
 Each directory entry holds the file or directory
 name, protection mode, access times, user-id, group-id, and addressing
 information.
@@ -99,64 +102,65 @@ is the user-id of the last writer of the file
 and
 .CW size
 is the size of the file in bytes.
-The first 6
+The addresses of the first 6
 blocks of the file are held in the
 .CW dblock
 array.
 If the file is larger than that,
 an indirect block is allocated that holds
 the next
-.CW BUFSIZE/sizeof(long)
-blocks of the file.
-The indirect block address is held in the structure member
-.CW iblock .
+.CW BUFSIZE/sizeof(Off)
+block addresses of the file.
+The indirect block address is held in
+.CW iblocks[0] .
 If the file is larger yet,
 then there is a double indirect block that points
 at indirect blocks.
 The double indirect address is held in
-.CW diblock
+.CW iblocks[1]
 and can point at another
-.CW (BUFSIZE/sizeof(long))\u\s-2\&2\s+2\d
+.CW (BUFSIZE/sizeof(Off))\u\s-2\&2\s+2\d
 blocks of data.
+This is extended through a quadruple indirect block at
+.CW iblocks[3]
+but the code is now parameterised to permit easily changing the
+number of direct blocks and the depth of indirect blocks,
+and also the maximum size of a file name component.
 The maximum addressable size of a file is
-therefore 275 Gbytes.
-There is a tighter restriction of
-2\u\s-2\&32\s+2\d
-bytes because the length of a file is maintained in
-a long.
-Even so,
-sloppy use of long arithmetic restricts the length to
-2\u\s-2\&31\s+2\d
-bytes.
-These numbers are based on Emelie
-which has a block size of 16K and
-.CW sizeof(long)
-is 4.
-It would be different if the size of a block
-changed.
+therefore 7.93 petabytes at a block size of 8k,
+but 7.98 exabytes (just under $2 sup 63$ bytes) at a block size of 32k.
+File size is restricted to $2 sup 63 - 1$ bytes in any case
+because the length of a file is maintained in a
+(signed)
+.I vlong .
+These numbers are based on
+.I fs64
+which has a block size of 8k and
+.CW sizeof(Off)
+is 8.
 .PP
 The declarations of the indirect and double indirect blocks
 are as follows.
-.P1
-    enum
-    {
-        INDPERBUF = BUFSIZE/sizeof(long),
-    };
-.P2
-.P1
-    typedef
-    {
-        long    dblock[INDPERBUF];
-        Tag     ibtag;
-    } Iblock;
-.P2
-.P1
-    typedef
-    {
-        long    iblock[INDPERBUF];
-        Tag     dibtag;
-    } Diblock;
-.P2
+.Ex
+	enum
+	{
+		INDPERBUF = BUFSIZE/sizeof(Off),
+	};
+.Ee
+.Ex
+	typedef
+	{
+		Off	dblock[INDPERBUF];
+		Tag	ibtag;
+	} Iblock;
+.Ee
+.Ex
+	typedef
+	{
+		Off	iblock[INDPERBUF];
+		Tag	dibtag;
+	} Diblock;
+.Ee
 .PP
 The root of a file system is a single directory entry
 at a known block address.
@@ -164,7 +168,9 @@ A directory is a file that consists of a list of
 directory entries.
 To make access easier,
 a directory entry cannot cross blocks.
-In Emelie there are 233 directory entries per block.
+In
+.I fs64
+there are 47 directory entries per block.
 .PP
 The device on which the blocks reside is implicit
 and ultimately comes from the 9P

+ 73 - 13
sys/doc/fs/p4

@@ -42,6 +42,18 @@ then the pseudo-device is the N-way block
 interleaving of the sub-devices.
 The size of the interleaved device is
 N times the size of the smallest sub-device.
+.IP "\fID\fP = {\fIDD\fP...}"
+.br
+This is a set of devices that
+constitute a `mirror' of the first sub-device, and form a single device.
+A write to the device is performed,
+at the same block address,
+on the sub-devices, in right-to-left order.
+A read from the device is performed on each sub-device,
+in left-to-right order, until a read succeeds without error,
+or the set is exhausted.
+One can think of this as a poor man's RAID 1.
+The size of the device is the size of the smallest sub-device.
 .IP "\fID\fP = \f(CWp\fP\fIDN1.N2\fP"
 .br
 This is a partition of a sub-device.
@@ -62,6 +74,20 @@ This second device is partitioned
 into a set of block flags and a set of blocks.
 The flags are used to generate errors if a
 block is ever written twice or read without being written first.
+.IP "\fID\fP = \f(CWx\fP\fID\fP"
+.br
+This is a byte-swapped version of the file system on D.
+Since the file server currently writes integers in metadata to disk
+in native byte order, moving a file system to a machine of the other
+major byte order (e.g., MIPS to Pentium)
+requires the use of
+.CW x .
+It knows the sizes of the various integer fields in the file system metadata.
+Ideally, the file server would follow the Plan 9 religion and write a consistent
+byte order on disk, regardless of processor.
+In the mean time, it should be possible to automatically determine the need
+for byte-swapping by examining data in the super-block of each file system,
+though this has not been implemented yet.
 .IP "\fID\fP = \f(CWc\fP\fIDD\fP"
 .br
 This is the cache/WORM device made up of a cache (read-write)
@@ -78,13 +104,27 @@ be referenced as
 in this pseudo device.
 The second dump taken that day will be
 .CW /1995/02181 .
-.IP "\fID\fP = \f(CWw\fP\fIN1.N2\fP"
+.IP "\fID\fP = \f(CWw\fP\fIN1.N2.N3\fP"
+.br
+This is a SCSI disk on controller N1, target N2 and logical unit number N3.
+.IP "\fID\fP = \f(CWh\fP\fIN1.N2.0\fP"
 .br
-This is a SCSI disk on controller N1 and target N2.
-.IP "\fID\fP = \f(CWl\fP\fIN1.N2\fP"
+This is an (E)IDE or *ATA disk on controller N1, target N2
+(target 0 is the IDE master, 1 the slave device).
+These disks are currently run via programmed I/O, not DMA,
+so they tend to be slower to access than SCSI disks.
+.IP "\fID\fP = \f(CWr\fP\fIN1\fP"
 .br
 This is the same as
 .CW w ,
+but refers to a side of a WORM disc.
+See the
+.I j
+device.
+.IP "\fID\fP = \f(CWl\fP\fIN1\fP"
+.br
+This is the same as
+.CW r ,
 but one block from the SCSI disk is removed for labeling.
 .IP "\fID\fP = \f(CWj(\fP\fID\d\s-2\&1\s+2\u\fID\d\s-2\&2\s+2\u\f(CW*)\fID\d\s-2\&3\s+2\u\f1"
 .br
@@ -93,7 +133,7 @@ is the juke box SCSI interface.
 The
 .I D\d\s-2\&2\s+2\u 's
 are the SCSI drives in the juke box
-and  the
+and the
 .I D\d\s-2\&3\s+2\u 's
 are the demountable platters in the juke box.
 .I D\d\s-2\&1\s+2\u
@@ -103,32 +143,52 @@ must be
 .CW w .
 .I D\d\s-2\&3\s+2\u
 must be pseudo devices of
-.CW w
+.CW w ,
+.CW r ,
 or
 .CW l
 devices.
 .PP
-For both
-.CW w
+For
+.CW w ,
+.CW h ,
+.CW l ,
 and
 .CW r
 devices any of the configuration numbers
 can be replaced by an iterator of the form
 .CW <\fIN1-N2\fP> .
+N1 can be greater than N2, indicating a descending sequence.
 Thus
-.P1
-    [w0.<2-6>]
-.P2
+.Ex
+	[w0.<2-6>]
+.Ee
 is the interleaved SCSI disks on SCSI targets
 2 through 6 of SCSI controller 0.
 The main file system on
 Emelie
 is defined by the configuration string
-.P1
-    c[w1.<0-5>.0]j(w6w5w4w3w2)l(<0-236>l<238-474>)
-.P2
+.Ex
+	c[w1.<0-5>.0]j(w6w5w4w3w2)(l<0-236>l<238-474>)
+.Ee
 This is a cache/WORM driver.
 The cache is three interleaved disks on SCSI controller 1
 targets 0, 1, 2, 3, 4, and 5.
 The WORM half of the cache/WORM
 is 474 jukebox disks.
+Another file server,
+.I choline ,
+has a main file system defined by
+.Ex
+	c[w<1-3>]j(w1.<6-0>.0)(l<0-124>l<128-252>)
+.Ee
+The order of
+.CW w1.<6-0>.0
+matters here, since the optical jukebox's WORM drives's
+SCSI target ids,
+as delivered,
+run in descending order relative to the numbers of the drives
+in SCSI commands
+(e.g., the jukebox controller is SCSI target 6,
+drive #1 is SCSI target 5,
+and drive #6 is SCSI target 0).

+ 3 - 3
sys/doc/fs/p5

@@ -32,8 +32,8 @@ since the relative block mark is set to 1
 and only block offset 0 is read.
 This is to prevent some
 fairly common action such as
-.P1
-    file *
-.P2
+.Ex
+	file *
+.Ee
 from swamping the file system with read-ahead
 requests that will never be used.

+ 57 - 49
sys/doc/fs/p6

@@ -10,9 +10,13 @@ by performing operations on its two constituent devices
 the read-write c-device and the write-once-read-many
 w-device.
 The block numbers on the four devices are distinct,
-although the cw addresses,
+although the
+.I cw
+addresses,
 dump addresses,
-and the w addresses are
+and the
+.I w
+addresses are
 highly correlated.
 .PP
 The cw-driver uses the w-device as the
@@ -26,34 +30,34 @@ mapped through a hash table kept on a partition of the c-device.
 .PP
 The map portion of the c-device consists of blocks of buckets of entries.
 The declarations follow.
-.P1
-    enum
-    {
-        BKPERBLK = 10,
-        CEPERBK  = (BUFSIZE - BKPERBLK*sizeof(long)) /
-                   (sizeof(Centry)*BKPERBLK),
-    };
-.P2
-.P1
-    typedef
-    struct
-    {
-        ushort   age;
-        short    state;
-        long     waddr;
-    } Centry;
-.P2
-.P1
-    typedef
-    struct
-    {
-        long     agegen;
-        Centry   entry[CEPERBK];
-    } Bucket;
-.P2
-.P1
-    Bucket   bucket[BKPERBLK];
-.P2
+.Ex
+	enum
+	{
+		BKPERBLK = 10,
+		CEPERBK	= (BUFSIZE - BKPERBLK*sizeof(Off)) /
+				(sizeof(Centry)*BKPERBLK),
+	};
+.Ee
+.Ex
+	typedef
+	struct
+	{
+		ushort	age;
+		short	state;
+		Off	waddr;
+	} Centry;
+.Ee
+.Ex
+	typedef
+	struct
+	{
+		long	agegen;
+		Centry	entry[CEPERBK];
+	} Bucket;
+.Ee
+.Ex
+	Bucket	bucket[BKPERBLK];
+.Ee
 There is exactly one entry structure for each block in the
 data partition of the c-device.
 A bucket contains all of the w-addresses that have
@@ -69,10 +73,10 @@ from zero.
 .PP
 The following steps go into converting a w-address into a c-address.
 The bucket is found by
-.P1
-    bucket_number = w-address % total_buckets
-    getbuf(c-device, bucket_offset + bucket_number/BKPERBLK);
-.P2
+.Ex
+	bucket_number = w-address % total_buckets;
+	getbuf(c-device, bucket_offset + bucket_number/BKPERBLK);
+.Ee
 After the desired bucket is found,
 the desired entry is found by a linear search within the bucket for the
 entry with the desired
@@ -80,17 +84,17 @@ entry with the desired
 .PP
 The state variable in the entry is
 one of the following.
-.P1
-    enum
-    {
-        Cnone    = 0,
-        Cdirty,
-        Cdump,
-        Cread,
-        Cwrite,
-        Cdump1,
-    };
-.P2
+.Ex
+	enum
+	{
+		Cnone	= 0,
+		Cdirty,
+		Cdump,
+		Cread,
+		Cwrite,
+		Cdump1,
+	};
+.Ee
 Every w-address has a state.
 Blocks that are not in the
 c-device have the implied
@@ -157,7 +161,8 @@ blocks with
 state.
 .PP
 The dump algorithm is as follows:
-a) The tree on the cw-device is walked
+.IP a)
+The tree on the cw-device is walked
 as long as the blocks visited have been
 modified since the last dump.
 These are the blocks with state
@@ -175,7 +180,8 @@ The directory containing that directory must be
 modified for the same reason.
 The tree walk is thus drastically restrained and the
 tree walk does not take much time.
-b) All
+.IP b)
+All
 .CW Cwrite
 blocks found in the tree search
 are relocated to new blank blocks on the w-device
@@ -195,13 +201,15 @@ These blocks are marked for later
 writing to the w-device
 with the state
 .CW Cdump .
-c) All open files that were pointing to modified
+.IP c)
+All open files that were pointing to modified
 blocks are reopened to point at the corresponding
 reallocated blocks.
 This causes the directories leading to the
 open files to be modified.
 Thus the invariant discussed in a) is maintained.
-d) The background dumping process will slowly
+.IP d)
+The background dumping process will slowly
 go through the map of the c-device and write out
 all blocks with
 .CW Cdump

+ 1 - 1
sys/doc/fs/p7

@@ -45,4 +45,4 @@ side of disk 0.
 On Emelie,
 the main file system is configured
 on both sides of the first 237 disks,
-platters 0-236 and 238-474.
+platters 0\-236 and 238\-474.

+ 24 - 0
sys/doc/fs/p9

@@ -0,0 +1,24 @@
+.SH
+Acknowledgements
+.PP
+Ken Thompson created the Plan 9 file server
+and maintained it for many years.
+The cached WORM driver is based upon
+Sean Quinlan's PhD. thesis and prototype.
+Jim McKie maintained the IBM-PC-dependent code,
+a thankless job.
+Bruce Ellis modified the
+.I 8c
+compiler in 2004
+to generate much faster code for common
+.I vlong
+operations, which made the 64-bit file server feasible.
+Nigel Roles contributed support for the APC UPS
+and the NCR/Symbios/LSI-Logic SCSI host adaptors.
+.
+.SH
+References
+.IP [1]
+Sean Quinlan, ``A Cached WORM File System,''
+.I "Software\(emPractice and Experience" ,
+Vol 21., No 12., December 1991, pp. 1289\-1299.

+ 76 - 0
sys/doc/fs/pa

@@ -0,0 +1,76 @@
+.bp
+.SH
+Appendix:
+Maximum File Sizes in the 64-bit File Server
+.PP
+The maximum size of a single file in a Plan 9 file server's
+file system with 64-bit block numbers
+is determined by the file system block size
+(there are single, double, triple and quadruple indirect blocks).
+The maximum size is thus
+$d ( 6 + x + x sup 2 + x sup 3 + x sup 4 )$
+bytes, where
+$d = blocksize - ( 2 + 2 + 8 )$
+and
+$x = left floor d over 8 right floor$,
+8 being the size in bytes of a
+.I "long long"
+block number.
+.LP
+Note that
+$2 sup 63 ~ = ~ 9,223,372,036,854,775,808 ~ = ~ 8$
+EB (binary exabytes).
+.LP
+.KS
+.TS
+center ;
+c s s
+c c c
+n n n .
+Maximum File Sizes
+blocksize	max. file size	in bytes
+_
+1k	239.455G	257,112,525,120
+2k	7.73795T	8,507,967,771,456
+3k	59.4174T	65,330,091,351,360
+4k	251.779T	276,833,619,879,744
+5k	770.93T	847,646,649,807,168
+6k	1.87752P	2,113,900,586,253,120
+7k	4.0645P	4,576,215,491,839,296
+8k	7.93382P	8,932,685,435,522,880
+_
+9k	14.3102P	16,111,863,841,429,824
+10k	24.2524P	27,305,748,837,688,128
+11k	39.0823P	44,002,768,605,261,120
+12k	60.4146P	68,020,766,726,780,736
+13k	90.1856P	101,539,987,535,380,800
+14k	130.683P	147,136,061,463,530,304
+15k	184.575P	207,812,990,391,866,688
+16k	254.939P	287,036,132,998,029,120
+_
+17k	345.293P	388,765,190,105,491,776
+18k	459.621P	517,487,190,032,397,120
+19k	602.407P	678,249,473,940,389,184
+20k	778.66P	876,692,681,183,446,848
+21k	993.946P	1,119,083,734,656,717,120
+22k	1.22502E	1,412,348,826,145,348,416
+23k	1.53012E	1,764,106,401,673,323,840
+24k	1.89319E	2,182,700,146,852,294,464
+25k	2.32213E	2,677,231,972,230,412,608
+26k	2.82551E	3,257,594,998,641,165,120
+27k	3.41264E	3,934,506,542,552,206,656
+28k	4.09355E	4,719,541,101,414,192,960
+29k	4.87905E	5,625,163,339,009,614,144
+30k	5.78076E	6,664,761,070,801,627,968
+31k	6.81111E	7,852,678,249,282,893,120
+32k	7.98341E	9,204,247,949,324,402,496
+_
+33k	9.31184E	10,735,825,353,524,316,480
+\&...
+48k	60.666E	69,943,138,363,646,533,440
+\&...
+56k	131.149E	151,204,569,706,075,533,120
+\&...
+64k	255.734E	294,841,790,119,418,167,104
+.TE
+.KE

+ 17 - 0
sys/man/4/consolefs

@@ -190,6 +190,11 @@ by the ASCII time to the file
 An example of 2 consoles complete with console logging is:
 .IP
 .EX
+% cat /lib/ndb/consoledb
+group=sys
+	uid=glenda
+console=bootes dev=/dev/eia0 gid=sys
+console=fornax dev=/dev/eia1 gid=sys
 % aux/consolefs
 % ls -p /mnt/consoles
 bootes
@@ -199,6 +204,18 @@ fornaxctl
 % clog /mnt/consoles/fornax /sys/log/fornax &
 % clog /mnt/consoles/bootes /sys/log/bootes &
 .EE
+.PP
+The console server's default name space must 
+mount the consoles for 
+.I C
+to import.
+This can be arranged by adding
+.IP
+.EX
+mount /srv/consoles /mnt/consoles
+.EE
+to
+.BR /lib/namespace.$sysname .
 .SH FILES
 .TF /lib/ndb/consoledb
 .TP

+ 17 - 0
sys/man/8/fsconfig

@@ -46,6 +46,10 @@ fsconfig \- configuring a file server
 .PP
 .B copyworm
 .PP
+.B copydev
+.I from-dev
+.I to-dev
+.PP
 .B halt
 .PP
 .B end
@@ -324,6 +328,19 @@ will cause the server to
 .I immediately
 exit and reboot.
 .PP
+.I Copydev
+will copy the device
+.I from-dev
+to the device
+.IR to-dev .
+block by block,
+and panic.
+.PP
+.I Halt
+will cause the server to
+.I immediately
+exit and reboot.
+.PP
 The various configuration commands only record what to do; they write
 no data to disk.  The command
 .I end

+ 20 - 20
sys/src/fs/sony/9sonyfs.c → sys/src/fs/9netics32.16k/9net32.16kfs.c

@@ -13,36 +13,34 @@
 int FIXEDSIZE = 1;
 
 #ifndef	DATE
-#define	DATE	568011600L+4*3600
+#define	DATE	1094098624L
 #endif
 
-ulong	mktime		= DATE;				/* set by mkfile */
+Timet	mktime		= DATE;				/* set by mkfile */
 Startsb	startsb[] =
 {
-	"main",		2,
+	"main",		2,	/* */
 	0
 };
 
 Dos dos;
 
-static
-struct
+static struct
 {
 	char	*name;
-	long	(*read)(int, void*, long);
-	vlong	(*seek)(int, vlong);
-	long	(*write)(int, void*, long);
+	Off	(*read)(int, void*, long);
+	Devsize	(*seek)(int, Devsize);
+	Off	(*write)(int, void*, long);
 	int	(*part)(int, char*);
-} nvrdevs[] =
-{
+} nvrdevs[] = {
 	{ "fd", floppyread, floppyseek, floppywrite, 0, },
 	{ "hd", ataread,   ataseek,   atawrite,   setatapart, },
-	/*
-	{ "sd", scsiread,   scsiseek,   scsiwrite,   setscsipart, },
-	 */
+	/* { "sd", scsiread,   scsiseek,   scsiwrite,   setscsipart, },  */
 	{ 0, },
 };
 
+void apcinit(void);
+
 void
 otherinit(void)
 {
@@ -53,6 +51,7 @@ otherinit(void)
 	printcpufreq();
 	etherinit();
 	scsiinit();
+	apcinit();
 
 	s = spllo();
 	nhd = atainit();
@@ -159,20 +158,21 @@ touser(void)
 void
 localconfinit(void)
 {
-	conf.nfile = 40000;
+	/* conf.nfile = 60000; */	/* from emelie */
 	conf.nodump = 0;
-	conf.firstsb = 13219302;
-	conf.recovsb = 0;
+	conf.dumpreread = 0;
+	conf.firstsb = 0;	/* time- & jukebox-dependent optimisation */
+	conf.recovsb = 0;	/* 971531 is 24 june 2003, before w3 died */
 	conf.ripoff = 1;
-	conf.nlgmsg = 100;
-	conf.nsmmsg = 500;
+	conf.nlgmsg = 1100;	/* @8576 bytes, for packets */
+	conf.nsmmsg = 500;	/* @128 bytes */
 
-	conf.minuteswest = 5*60;
+	conf.minuteswest = 8*60;
 	conf.dsttime = 1;
 }
 
 int (*fsprotocol[])(Msgbuf*) = {
-	serve9p1,
+	serve9p1,		/* TODO: do we still need 9P1? */
 	serve9p2,
 	nil,
 };

+ 6 - 4
sys/src/fs/roro/dat.h → sys/src/fs/9netics32.16k/dat.h

@@ -1,9 +1,11 @@
-#define RBUFSIZE	(16*1024)	/* raw buffer size */
 /*
- * verify that the kernel prints this size
- * when you first boot this kernel.
- * #define	DSIZE		157933
+ * The most fundamental constant.
+ * The code will not compile with RBUFSIZE made a variable;
+ * for one thing, RBUFSIZE determines FEPERBUF, which determines
+ * the number of elements in a free-list-block array.
  */
+#define RBUFSIZE	(16*1024)	/* raw buffer size */
+
 #include "../port/portdat.h"
 
 extern	Mach	mach0;

+ 15 - 15
sys/src/fs/roro/fns.h → sys/src/fs/9netics32.16k/fns.h

@@ -12,9 +12,9 @@ void	etherinit(void);
 void	etherstart(void);
 int	floppyinit(void);
 void	floppyproc(void);
-long	floppyread(int, void*, long);
-vlong	floppyseek(int, vlong);
-long	floppywrite(int, void*, long);
+Off	floppyread(int, void*, long);
+Devsize	floppyseek(int, Devsize);
+Off	floppywrite(int, void*, long);
 void	fpinit(void);
 char*	getconf(char*);
 ulong	getcr0(void);
@@ -23,12 +23,11 @@ ulong	getcr4(void);
 int	getfields(char*, char**, int, char);
 ulong	getstatus(void);
 int	atainit(void);
-long	ataread(int, void*, long);
-vlong	ataseek(int, vlong);
-long	atawrite(int, void*, long);
+Off	ataread(int, void*, long);
+Devsize	ataseek(int, Devsize);
+Off	atawrite(int, void*, long);
 void	i8042a20(void);
 void	i8042reset(void);
-void	idecheck(Device*);
 int	inb(int);
 void	insb(int, void*, int);
 ushort	ins(int);
@@ -55,11 +54,12 @@ void	putcr3(ulong);
 void	putcr4(ulong);
 void	puttr(ulong);
 void	rdmsr(int, vlong*);
-void	rdtsc(uvlong*);
+void	wrmsr(int, vlong);
+void	(*cycles)(uvlong*);
 void	scsiinit(void);
-long	scsiread(int, void*, long);
-long	scsiseek(int, long);
-long	scsiwrite(int, void*, long);
+Off	scsiread(int, void*, long);
+Devsize	scsiseek(int, Devsize);
+Off	scsiwrite(int, void*, long);
 int	setatapart(int, char*);
 int	setscsipart(int, char*);
 void	setvec(int, void (*)(Ureg*, void*), void*);
@@ -70,10 +70,10 @@ int	uartgetc(void);
 void	uartputc(int);
 void	wbflush(void);
 void	cpuid(char*, int*, int*);
+
 #define PADDR(a)	((ulong)(a)&~KZERO)
 
 void	ideinit(Device *d);
-int	ideread(Device *d, long,  void*);
-int idewrite(Device *d, long, void*);
-long	atasize(Device *d);
-void	atainitstub(Device *d);
+Devsize	idesize(Device *d);
+int	ideread(Device *d,  Devsize, void*);
+int	idewrite(Device *d, Devsize, void*);

+ 6 - 2
sys/src/fs/roro/io.h → sys/src/fs/9netics32.16k/io.h

@@ -161,9 +161,12 @@ typedef struct Pcidev {
 		int	size;
 	} mem[6];
 
+	uchar	rid;
+	uchar	ccrp;
+	uchar	ccrb;
 	uchar	intl;			/* interrupt line */
-	ushort	ccru;
-
+	ushort	ccru;			/* is uchar in cpu kernel */
+	ulong	pcr;
 
 	Pcidev*	list;
 	Pcidev*	bridge;			/* down a bus */
@@ -181,6 +184,7 @@ extern Pcidev* pcimatch(Pcidev*, int, int);
 extern Pcidev* pcimatchtbdf(int);
 extern void pcireset(void);
 extern void pcisetbme(Pcidev*);
+extern void pciclrbme(Pcidev*);
 
 /*
  *  a parsed plan9.ini line

+ 85 - 0
sys/src/fs/9netics32.16k/mem.h

@@ -0,0 +1,85 @@
+#define	BY2WD		4			/* bytes per word */
+#define BY2V		8			/* bytes per vlong */
+#define	BY2PG		4096			/* bytes per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define PGROUND(s)	(((s)+(BY2PG-1))&~(BY2PG-1))
+#define MB		(1024*1024)
+
+#define	HZ		(82)			/* clock frequency */
+#define	TK2MS(t)	(((ulong)(t)*1000)/HZ)	/* ticks to milliseconds - beware rounding */
+#define	MS2TK(t)	(((ulong)(t)*HZ)/1000)	/* milliseconds to ticks - beware rounding */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+
+/*
+ * Fundamental addresses
+ */
+#define IDTADDR		0x80000800		/* idt */
+#define APBOOTSTRAP	0x80001000		/* AP bootstrap code */
+#define CONFADDR	0x80001200		/* info passed from boot loader */
+#define CPU0PDB		0x80002000		/* bootstrap processor PDB */
+#define CPU0PTE		0x80003000		/* bootstrap processor PTE's for 0-2MB */
+#define CPU0MACHPTE	0x80004000		/* bootstrap processor PTE for MACHADDR */
+#define CPU0MACH	0x80005000		/* Mach for bootstrap processor */
+
+#define	KZERO		0x80000000		/* base of kernel address space */
+#define	KTZERO		0x80100000		/* first address in kernel text */
+
+#define	MACHSIZE	4096
+
+/*
+ *  known 80386 segments (in GDT) and their selectors
+ */
+#define	NULLSEG	0	/* null segment */
+#define	KDSEG	1	/* kernel data/stack */
+#define	KESEG	2	/* kernel executable */
+#define	UDSEG	3	/* user data/stack */
+#define	UESEG	4	/* user executable */
+#define TSSSEG	5	/* task segment */
+#define N386SEG	6	/* number of segments */
+
+#define SELGDT	(0<<3)	/* selector is in gdt */
+#define	SELLDT	(1<<3)	/* selector is in ldt */
+
+#define SELECTOR(i, t, p)	(((i)<<3) | (t) | (p))
+
+#define NULLSEL	SELECTOR(NULLSEG, SELGDT, 0)
+#define KESEL	SELECTOR(KESEG, SELGDT, 0)
+#define KDSEL	SELECTOR(KDSEG, SELGDT, 0)
+#define UESEL	SELECTOR(UESEG, SELGDT, 3)
+#define UDSEL	SELECTOR(UDSEG, SELGDT, 3)
+#define TSSSEL	SELECTOR(TSSSEG, SELGDT, 0)
+
+/*
+ *  fields in segment descriptors
+ */
+#define SEGDATA	(0x10<<8)	/* data/stack segment */
+#define SEGEXEC	(0x18<<8)	/* executable segment */
+#define	SEGTSS	(0x9<<8)	/* TSS segment */
+#define SEGCG	(0x0C<<8)	/* call gate */
+#define	SEGIG	(0x0E<<8)	/* interrupt gate */
+#define SEGTG	(0x0F<<8)	/* task gate */
+#define SEGTYPE	(0x1F<<8)
+
+#define SEGP	(1<<15)		/* segment present */
+#define SEGPL(x) ((x)<<13)	/* priority level */
+#define SEGB	(1<<22)		/* granularity 1==4k (for expand-down) */
+#define SEGG	(1<<23)		/* granularity 1==4k (for other) */
+#define SEGE	(1<<10)		/* expand down */
+#define SEGW	(1<<9)		/* writable (for data/stack) */
+#define	SEGR	(1<<9)		/* readable (for code) */
+#define SEGD	(1<<22)		/* default 1==32bit (for code) */
+
+/*
+ *  physical MMU
+ */
+#define	PTEVALID	(1<<0)
+#define	PTEUNCACHED	(1<<4)
+#define PTEWRITE	(1<<1)
+#define	PTERONLY	(0<<1)
+#define	PTEKERNEL	(0<<2)
+#define	PTEUSER		(1<<2)
+#define PTESIZE		(1<<7)
+
+#define MACHADDR	((ulong)&mach0)		/* hack number 1 */
+
+#define IFLAG		0x200	/* psw: interrupt enable, to be accurate */

+ 18 - 17
sys/src/fs/roro/mkfile → sys/src/fs/9netics32.16k/mkfile

@@ -1,4 +1,4 @@
-CONF=roro
+CONF=net32.16k
 p=9
 
 objtype=386
@@ -7,6 +7,7 @@ objtype=386
 TARG=$p$CONF'fs'
 
 DEV=\
+	apc.$O\
 	cw.$O\
 	fworm.$O\
 	juke.$O\
@@ -51,7 +52,9 @@ PC=\
 	8250.$O\
 	8253.$O\
 	cga.$O\
-	devata.$O\
+	devsd.$O\
+	sdscsi.$O\
+	sdata.$O\
 	dosfs.$O\
 	floppy.$O\
 	kbd.$O\
@@ -65,16 +68,17 @@ PC=\
 	trap.$O\
 
 ETHER=\
-	etherif.$O\
+	compat.$O\
 	ether2114x.$O\
-	etherelnk3.$O\
+	ether8139.$O\
 	ether82557.$O\
-	compat.$O\
-	ethermii.$O\
 	ether83815.$O\
 	etherdp83820.$O\
-	ether8139.$O\
+	etherelnk3.$O\
 	etherga620.$O\
+	etherif.$O\
+	etherigbe.$O\
+	ethermii.$O\
 
 SCSI=\
 	scsi.$O\
@@ -88,7 +92,7 @@ OBJ=\
 	$IP\
 	$ETHER\
 	$SCSI\
-	
+
 HFILES=\
 	../port/all.h\
 	../port/lib.h\
@@ -103,11 +107,11 @@ HFILES=\
 
 LIB=\
 	-lauthsrv\
-	/$objtype/lib/libc.a\
+	-lc\
 	-lsec\
 
 # -I../pc & -DFS are for compat.h
-CFLAGS=-FVw -I. -I../port -I../pc -DFS
+CFLAGS=-FTVw -I. -I../port -I../pc -DFS -DOLD
 
 all:V:	$TARG
 
@@ -116,17 +120,14 @@ all:V:	$TARG
 <../dev/mkfile
 <../ip/mkfile
 
-$TARG:	$OBJ $TARG.$O
-	$LD -o $target -l -T0x80100020 $prereq $LIB
-	size $target
-
-$TARG.$O:	$TARG.c
+$TARG:	$TARG.c $OBJ
 	$CC $CFLAGS -DDATE'='`{date -n} $TARG.c
+	$LD -o $target -l -T0x80100020 $OBJ $TARG.$O $LIB
+	size $target
 
 install:V: $TARG
 	cp $TARG /$objtype/
-	#9fs dinar && cp $TARG /n/dinar/$objtype/
-	#import lookout / /n/lookout && cp $TARG /n/lookout/$objtype/
+#	9fs stand && cp -x $TARG /n/stand/$objtype
 
 $TARG.$O:	../pc/dosfs.h
 

+ 21 - 19
sys/src/fs/roro/9rorofs.c → sys/src/fs/9netics64.8k/9net64.8kfs.c

@@ -13,36 +13,34 @@
 int FIXEDSIZE = 1;
 
 #ifndef	DATE
-#define	DATE	568011600L+4*3600
+#define	DATE	1094098624L
 #endif
 
-ulong	mktime		= DATE;				/* set by mkfile */
+Timet	mktime		= DATE;				/* set by mkfile */
 Startsb	startsb[] =
 {
-	"main",		2,
+	"main",		2,	/* */
 	0
 };
 
 Dos dos;
 
-static
-struct
+static struct
 {
 	char	*name;
-	long	(*read)(int, void*, long);
-	vlong	(*seek)(int, vlong);
-	long	(*write)(int, void*, long);
+	Off	(*read)(int, void*, long);
+	Devsize	(*seek)(int, Devsize);
+	Off	(*write)(int, void*, long);
 	int	(*part)(int, char*);
-} nvrdevs[] =
-{
+} nvrdevs[] = {
 	{ "fd", floppyread, floppyseek, floppywrite, 0, },
 	{ "hd", ataread,   ataseek,   atawrite,   setatapart, },
-	/*
-	{ "sd", scsiread,   scsiseek,   scsiwrite,   setscsipart, },
-	 */
+	/* { "sd", scsiread,   scsiseek,   scsiwrite,   setscsipart, },  */
 	{ 0, },
 };
 
+void apcinit(void);
+
 void
 otherinit(void)
 {
@@ -53,6 +51,7 @@ otherinit(void)
 	printcpufreq();
 	etherinit();
 	scsiinit();
+	apcinit();
 
 	s = spllo();
 	nhd = atainit();
@@ -159,18 +158,21 @@ touser(void)
 void
 localconfinit(void)
 {
-	conf.nfile = 5000;
-	conf.nodump = 1;
-	conf.firstsb = 0;
-	conf.recovsb = 0;
+	/* conf.nfile = 60000; */	/* from emelie */
+	conf.nodump = 0;
+	conf.dumpreread = 0;
+	conf.firstsb = 0;	/* time- & jukebox-dependent optimisation */
+	conf.recovsb = 0;	/* 971531 is 24 june 2003, before w3 died */
 	conf.ripoff = 1;
+	conf.nlgmsg = 1100;	/* @8576 bytes, for packets */
+	conf.nsmmsg = 500;	/* @128 bytes */
 
-	conf.minuteswest = 5*60;
+	conf.minuteswest = 8*60;
 	conf.dsttime = 1;
 }
 
 int (*fsprotocol[])(Msgbuf*) = {
-	serve9p1,
+	/* 64-bit file servers can't serve 9P1 correctly: NAMELEN is too big */
 	serve9p2,
 	nil,
 };

+ 7 - 3
sys/src/fs/sony/dat.h → sys/src/fs/9netics64.8k/dat.h

@@ -1,6 +1,10 @@
-#define RBUFSIZE	(16*1024)	/* raw buffer size */
-/* this kernel still uses DSIZE because it uses sony.c instead of juke.c */
-#define	DSIZE		157933
+/*
+ * The most fundamental constant.
+ * The code will not compile with RBUFSIZE made a variable;
+ * for one thing, RBUFSIZE determines FEPERBUF, which determines
+ * the number of elements in a free-list-block array.
+ */
+#define RBUFSIZE	(8*1024)	/* raw buffer size */
 
 #include "../port/portdat.h"
 

+ 15 - 15
sys/src/fs/sony/fns.h → sys/src/fs/9netics64.8k/fns.h

@@ -12,9 +12,9 @@ void	etherinit(void);
 void	etherstart(void);
 int	floppyinit(void);
 void	floppyproc(void);
-long	floppyread(int, void*, long);
-vlong	floppyseek(int, vlong);
-long	floppywrite(int, void*, long);
+Off	floppyread(int, void*, long);
+Devsize	floppyseek(int, Devsize);
+Off	floppywrite(int, void*, long);
 void	fpinit(void);
 char*	getconf(char*);
 ulong	getcr0(void);
@@ -23,12 +23,11 @@ ulong	getcr4(void);
 int	getfields(char*, char**, int, char);
 ulong	getstatus(void);
 int	atainit(void);
-long	ataread(int, void*, long);
-vlong	ataseek(int, vlong);
-long	atawrite(int, void*, long);
+Off	ataread(int, void*, long);
+Devsize	ataseek(int, Devsize);
+Off	atawrite(int, void*, long);
 void	i8042a20(void);
 void	i8042reset(void);
-void	idecheck(Device*);
 int	inb(int);
 void	insb(int, void*, int);
 ushort	ins(int);
@@ -55,11 +54,12 @@ void	putcr3(ulong);
 void	putcr4(ulong);
 void	puttr(ulong);
 void	rdmsr(int, vlong*);
-void	rdtsc(uvlong*);
+void	wrmsr(int, vlong);
+void	(*cycles)(uvlong*);
 void	scsiinit(void);
-long	scsiread(int, void*, long);
-long	scsiseek(int, long);
-long	scsiwrite(int, void*, long);
+Off	scsiread(int, void*, long);
+Devsize	scsiseek(int, Devsize);
+Off	scsiwrite(int, void*, long);
 int	setatapart(int, char*);
 int	setscsipart(int, char*);
 void	setvec(int, void (*)(Ureg*, void*), void*);
@@ -70,10 +70,10 @@ int	uartgetc(void);
 void	uartputc(int);
 void	wbflush(void);
 void	cpuid(char*, int*, int*);
+
 #define PADDR(a)	((ulong)(a)&~KZERO)
 
 void	ideinit(Device *d);
-int	ideread(Device *d, long,  void*);
-int idewrite(Device *d, long, void*);
-long	atasize(Device *d);
-void	atainitstub(Device *d);
+Devsize	idesize(Device *d);
+int	ideread(Device *d,  Devsize, void*);
+int	idewrite(Device *d, Devsize, void*);

+ 6 - 2
sys/src/fs/sony/io.h → sys/src/fs/9netics64.8k/io.h

@@ -161,9 +161,12 @@ typedef struct Pcidev {
 		int	size;
 	} mem[6];
 
+	uchar	rid;
+	uchar	ccrp;
+	uchar	ccrb;
 	uchar	intl;			/* interrupt line */
-	ushort	ccru;
-
+	ushort	ccru;			/* is uchar in cpu kernel */
+	ulong	pcr;
 
 	Pcidev*	list;
 	Pcidev*	bridge;			/* down a bus */
@@ -181,6 +184,7 @@ extern Pcidev* pcimatch(Pcidev*, int, int);
 extern Pcidev* pcimatchtbdf(int);
 extern void pcireset(void);
 extern void pcisetbme(Pcidev*);
+extern void pciclrbme(Pcidev*);
 
 /*
  *  a parsed plan9.ini line

+ 85 - 0
sys/src/fs/9netics64.8k/mem.h

@@ -0,0 +1,85 @@
+#define	BY2WD		4			/* bytes per word */
+#define BY2V		8			/* bytes per vlong */
+#define	BY2PG		4096			/* bytes per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define PGROUND(s)	(((s)+(BY2PG-1))&~(BY2PG-1))
+#define MB		(1024*1024)
+
+#define	HZ		(82)			/* clock frequency */
+#define	TK2MS(t)	(((ulong)(t)*1000)/HZ)	/* ticks to milliseconds - beware rounding */
+#define	MS2TK(t)	(((ulong)(t)*HZ)/1000)	/* milliseconds to ticks - beware rounding */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+
+/*
+ * Fundamental addresses
+ */
+#define IDTADDR		0x80000800		/* idt */
+#define APBOOTSTRAP	0x80001000		/* AP bootstrap code */
+#define CONFADDR	0x80001200		/* info passed from boot loader */
+#define CPU0PDB		0x80002000		/* bootstrap processor PDB */
+#define CPU0PTE		0x80003000		/* bootstrap processor PTE's for 0-2MB */
+#define CPU0MACHPTE	0x80004000		/* bootstrap processor PTE for MACHADDR */
+#define CPU0MACH	0x80005000		/* Mach for bootstrap processor */
+
+#define	KZERO		0x80000000		/* base of kernel address space */
+#define	KTZERO		0x80100000		/* first address in kernel text */
+
+#define	MACHSIZE	4096
+
+/*
+ *  known 80386 segments (in GDT) and their selectors
+ */
+#define	NULLSEG	0	/* null segment */
+#define	KDSEG	1	/* kernel data/stack */
+#define	KESEG	2	/* kernel executable */
+#define	UDSEG	3	/* user data/stack */
+#define	UESEG	4	/* user executable */
+#define TSSSEG	5	/* task segment */
+#define N386SEG	6	/* number of segments */
+
+#define SELGDT	(0<<3)	/* selector is in gdt */
+#define	SELLDT	(1<<3)	/* selector is in ldt */
+
+#define SELECTOR(i, t, p)	(((i)<<3) | (t) | (p))
+
+#define NULLSEL	SELECTOR(NULLSEG, SELGDT, 0)
+#define KESEL	SELECTOR(KESEG, SELGDT, 0)
+#define KDSEL	SELECTOR(KDSEG, SELGDT, 0)
+#define UESEL	SELECTOR(UESEG, SELGDT, 3)
+#define UDSEL	SELECTOR(UDSEG, SELGDT, 3)
+#define TSSSEL	SELECTOR(TSSSEG, SELGDT, 0)
+
+/*
+ *  fields in segment descriptors
+ */
+#define SEGDATA	(0x10<<8)	/* data/stack segment */
+#define SEGEXEC	(0x18<<8)	/* executable segment */
+#define	SEGTSS	(0x9<<8)	/* TSS segment */
+#define SEGCG	(0x0C<<8)	/* call gate */
+#define	SEGIG	(0x0E<<8)	/* interrupt gate */
+#define SEGTG	(0x0F<<8)	/* task gate */
+#define SEGTYPE	(0x1F<<8)
+
+#define SEGP	(1<<15)		/* segment present */
+#define SEGPL(x) ((x)<<13)	/* priority level */
+#define SEGB	(1<<22)		/* granularity 1==4k (for expand-down) */
+#define SEGG	(1<<23)		/* granularity 1==4k (for other) */
+#define SEGE	(1<<10)		/* expand down */
+#define SEGW	(1<<9)		/* writable (for data/stack) */
+#define	SEGR	(1<<9)		/* readable (for code) */
+#define SEGD	(1<<22)		/* default 1==32bit (for code) */
+
+/*
+ *  physical MMU
+ */
+#define	PTEVALID	(1<<0)
+#define	PTEUNCACHED	(1<<4)
+#define PTEWRITE	(1<<1)
+#define	PTERONLY	(0<<1)
+#define	PTEKERNEL	(0<<2)
+#define	PTEUSER		(1<<2)
+#define PTESIZE		(1<<7)
+
+#define MACHADDR	((ulong)&mach0)		/* hack number 1 */
+
+#define IFLAG		0x200	/* psw: interrupt enable, to be accurate */

+ 23 - 21
sys/src/fs/sony/mkfile → sys/src/fs/9netics64.8k/mkfile

@@ -1,4 +1,4 @@
-CONF=sony
+CONF=net64.8k
 p=9
 
 objtype=386
@@ -7,10 +7,11 @@ objtype=386
 TARG=$p$CONF'fs'
 
 DEV=\
+	apc.$O\
 	cw.$O\
 	fworm.$O\
+	juke.$O\
 	mworm.$O\
-	sony.$O\
 	wren.$O\
 
 IP=\
@@ -51,7 +52,9 @@ PC=\
 	8250.$O\
 	8253.$O\
 	cga.$O\
-	devata.$O\
+	devsd.$O\
+	sdscsi.$O\
+	sdata.$O\
 	dosfs.$O\
 	floppy.$O\
 	kbd.$O\
@@ -65,16 +68,17 @@ PC=\
 	trap.$O\
 
 ETHER=\
-	etherif.$O\
+	compat.$O\
 	ether2114x.$O\
-	etherelnk3.$O\
+	ether8139.$O\
 	ether82557.$O\
-	compat.$O\
-	ethermii.$O\
 	ether83815.$O\
 	etherdp83820.$O\
-	ether8139.$O\
+	etherelnk3.$O\
 	etherga620.$O\
+	etherif.$O\
+	etherigbe.$O\
+	ethermii.$O\
 
 SCSI=\
 	scsi.$O\
@@ -88,26 +92,26 @@ OBJ=\
 	$IP\
 	$ETHER\
 	$SCSI\
-	
+
 HFILES=\
 	../port/all.h\
 	../port/lib.h\
 	../port/portdat.h\
 	../port/portfns.h\
-	./dat.h\
-	./fns.h\
-	./io.h\
-	./mem.h\
+	dat.h\
+	fns.h\
+	io.h\
+	mem.h\
 	/$objtype/include/u.h\
 	/$objtype/include/ureg.h\
 
 LIB=\
 	-lauthsrv\
-	/$objtype/lib/libc.a\
+	-lc\
 	-lsec\
 
 # -I../pc & -DFS are for compat.h
-CFLAGS=-FVw -I. -I../port -I../pc -DFS
+CFLAGS=-FTVw -I. -I../port -I../pc -DFS
 
 all:V:	$TARG
 
@@ -116,16 +120,14 @@ all:V:	$TARG
 <../dev/mkfile
 <../ip/mkfile
 
-$TARG:	$OBJ $TARG.$O
-	$LD -o $target -l -T0x80100020 $prereq $LIB
-	size $target
-
-$TARG.$O:	$TARG.c
+$TARG:	$TARG.c $OBJ
 	$CC $CFLAGS -DDATE'='`{date -n} $TARG.c
+	$LD -o $target -l -T0x80100020 $OBJ $TARG.$O $LIB
+	size $target
 
 install:V: $TARG
 	cp $TARG /$objtype/
-	9fs dinar && cp $TARG /n/dinar/$objtype/
+#	9fs stand && cp -x $TARG /n/stand/$objtype
 
 $TARG.$O:	../pc/dosfs.h
 

+ 11 - 12
sys/src/fs/choline/9cholinefs.c

@@ -13,10 +13,10 @@
 int FIXEDSIZE = 1;
 
 #ifndef	DATE
-#define	DATE	568011600L+4*3600
+#define	DATE	1094098624L
 #endif
 
-ulong	mktime		= DATE;				/* set by mkfile */
+Timet	mktime		= DATE;				/* set by mkfile */
 Startsb	startsb[] =
 {
 	"main",		2,
@@ -25,24 +25,22 @@ Startsb	startsb[] =
 
 Dos dos;
 
-static
-struct
+static struct
 {
 	char	*name;
-	long	(*read)(int, void*, long);
-	vlong	(*seek)(int, vlong);
-	long	(*write)(int, void*, long);
+	Off	(*read)(int, void*, long);
+	Devsize	(*seek)(int, Devsize);
+	Off	(*write)(int, void*, long);
 	int	(*part)(int, char*);
-} nvrdevs[] =
-{
+} nvrdevs[] = {
 	{ "fd", floppyread, floppyseek, floppywrite, 0, },
 	{ "hd", ataread,   ataseek,   atawrite,   setatapart, },
-	/*
-	{ "sd", scsiread,   scsiseek,   scsiwrite,   setscsipart, },
-	 */
+	/* { "sd", scsiread,   scsiseek,   scsiwrite,   setscsipart, },  */
 	{ 0, },
 };
 
+void apcinit(void);
+
 void
 otherinit(void)
 {
@@ -53,6 +51,7 @@ otherinit(void)
 	printcpufreq();
 	etherinit();
 	scsiinit();
+	apcinit();
 
 	s = spllo();
 	nhd = atainit();

+ 5 - 4
sys/src/fs/choline/dat.h

@@ -1,9 +1,10 @@
-#define RBUFSIZE	(16*1024)	/* raw buffer size */
 /*
- * verify that the kernel reports the right size when you
- * first boot this kernel.
+ * The most fundamental constant.
+ * The code will not compile with RBUFSIZE made a variable;
+ * for one thing, RBUFSIZE determines FEPERBUF, which determines
+ * the number of elements in a free-list-block array.
  */
-/* #define	DSIZE		(79563-1)	/* worm size */
+#define RBUFSIZE	(16*1024)	/* raw buffer size */
 
 #include "../port/portdat.h"
 

+ 15 - 15
sys/src/fs/choline/fns.h

@@ -12,9 +12,9 @@ void	etherinit(void);
 void	etherstart(void);
 int	floppyinit(void);
 void	floppyproc(void);
-long	floppyread(int, void*, long);
-vlong	floppyseek(int, vlong);
-long	floppywrite(int, void*, long);
+Off	floppyread(int, void*, long);
+Devsize	floppyseek(int, Devsize);
+Off	floppywrite(int, void*, long);
 void	fpinit(void);
 char*	getconf(char*);
 ulong	getcr0(void);
@@ -23,12 +23,11 @@ ulong	getcr4(void);
 int	getfields(char*, char**, int, char);
 ulong	getstatus(void);
 int	atainit(void);
-long	ataread(int, void*, long);
-vlong	ataseek(int, vlong);
-long	atawrite(int, void*, long);
+Off	ataread(int, void*, long);
+Devsize	ataseek(int, Devsize);
+Off	atawrite(int, void*, long);
 void	i8042a20(void);
 void	i8042reset(void);
-void	idecheck(Device*);
 int	inb(int);
 void	insb(int, void*, int);
 ushort	ins(int);
@@ -55,11 +54,12 @@ void	putcr3(ulong);
 void	putcr4(ulong);
 void	puttr(ulong);
 void	rdmsr(int, vlong*);
-void	rdtsc(uvlong*);
+void	wrmsr(int, vlong);
+void	(*cycles)(uvlong*);
 void	scsiinit(void);
-long	scsiread(int, void*, long);
-long	scsiseek(int, long);
-long	scsiwrite(int, void*, long);
+Off	scsiread(int, void*, long);
+Devsize	scsiseek(int, Devsize);
+Off	scsiwrite(int, void*, long);
 int	setatapart(int, char*);
 int	setscsipart(int, char*);
 void	setvec(int, void (*)(Ureg*, void*), void*);
@@ -70,10 +70,10 @@ int	uartgetc(void);
 void	uartputc(int);
 void	wbflush(void);
 void	cpuid(char*, int*, int*);
+
 #define PADDR(a)	((ulong)(a)&~KZERO)
 
 void	ideinit(Device *d);
-int	ideread(Device *d, long,  void*);
-int idewrite(Device *d, long, void*);
-long	atasize(Device *d);
-void	atainitstub(Device *d);
+Devsize	idesize(Device *d);
+int	ideread(Device *d,  Devsize, void*);
+int	idewrite(Device *d, Devsize, void*);

+ 6 - 2
sys/src/fs/choline/io.h

@@ -161,9 +161,12 @@ typedef struct Pcidev {
 		int	size;
 	} mem[6];
 
+	uchar	rid;
+	uchar	ccrp;
+	uchar	ccrb;
 	uchar	intl;			/* interrupt line */
-	ushort	ccru;
-
+	ushort	ccru;			/* is uchar in cpu kernel */
+	ulong	pcr;
 
 	Pcidev*	list;
 	Pcidev*	bridge;			/* down a bus */
@@ -181,6 +184,7 @@ extern Pcidev* pcimatch(Pcidev*, int, int);
 extern Pcidev* pcimatchtbdf(int);
 extern void pcireset(void);
 extern void pcisetbme(Pcidev*);
+extern void pciclrbme(Pcidev*);
 
 /*
  *  a parsed plan9.ini line

+ 1 - 1
sys/src/fs/choline/mem.h

@@ -82,4 +82,4 @@
 
 #define MACHADDR	((ulong)&mach0)		/* hack number 1 */
 
-#define IFLAG		0x200
+#define IFLAG		0x200	/* psw: interrupt enable, to be accurate */

+ 19 - 16
sys/src/fs/choline/mkfile

@@ -7,6 +7,7 @@ objtype=386
 TARG=$p$CONF'fs'
 
 DEV=\
+	apc.$O\
 	cw.$O\
 	fworm.$O\
 	juke.$O\
@@ -51,7 +52,9 @@ PC=\
 	8250.$O\
 	8253.$O\
 	cga.$O\
-	devata.$O\
+	devsd.$O\
+	sdscsi.$O\
+	sdata.$O\
 	dosfs.$O\
 	floppy.$O\
 	kbd.$O\
@@ -65,16 +68,17 @@ PC=\
 	trap.$O\
 
 ETHER=\
-	etherif.$O\
+	compat.$O\
 	ether2114x.$O\
-	etherelnk3.$O\
+	ether8139.$O\
 	ether82557.$O\
-	compat.$O\
-	ethermii.$O\
 	ether83815.$O\
 	etherdp83820.$O\
-	ether8139.$O\
+	etherelnk3.$O\
 	etherga620.$O\
+	etherif.$O\
+	etherigbe.$O\
+	ethermii.$O\
 
 SCSI=\
 	scsi.$O\
@@ -88,7 +92,7 @@ OBJ=\
 	$IP\
 	$ETHER\
 	$SCSI\
-	
+
 HFILES=\
 	../port/all.h\
 	../port/lib.h\
@@ -103,11 +107,12 @@ HFILES=\
 
 LIB=\
 	-lauthsrv\
-	/$objtype/lib/libc.a\
+	-lc\
 	-lsec\
 
 # -I../pc & -DFS are for compat.h
-CFLAGS=-FVw -I. -I../port -I../pc -DFS
+# -DOLD uses 32-bit file offsets instead of 64-bit ones
+CFLAGS=-FTVw -I. -I../port -I../pc -DFS -DOLD
 
 all:V:	$TARG
 
@@ -116,17 +121,15 @@ all:V:	$TARG
 <../dev/mkfile
 <../ip/mkfile
 
-$TARG:	$OBJ $TARG.$O
-	$LD -o $target -l -T0x80100020 $prereq $LIB
-	size $target
-
-$TARG.$O:	$TARG.c
+$TARG:	$TARG.c $OBJ
 	$CC $CFLAGS -DDATE'='`{date -n} $TARG.c
+	$LD -o $target -l -T0x80100020 $OBJ $TARG.$O $LIB
+	size $target
 
 install:V: $TARG
 	cp $TARG /$objtype/
-	#import lookout / /n/lookout && cp $TARG /n/lookout/$objtype/
-	#import boundary / /n/boundary && cp $TARG /n/boundary/$objtype/
+	import lookout / /n/lookout && cp $TARG /n/lookout/$objtype/
+	import boundary / /n/boundary && cp $TARG /n/boundary/$objtype/
 
 $TARG.$O:	../pc/dosfs.h
 

+ 773 - 0
sys/src/fs/dev/apc.c

@@ -0,0 +1,773 @@
+/*
+ * manage APC (American Power Corporation) UPS, notably shutting down
+ * gracefully when power gets too low. enabled by "ups0=type=apc" in plan9.ini.
+ * N.B.: connection to UPS is assumed to be on eia1 (2nd serial port)
+ * at 2400 bps!  due to hardcoded use of uartputc1, etc., probably can't
+ * override port with "port=0" in plan9.ini entry.
+ */
+#include "all.h"
+#include "mem.h"
+#include "io.h"
+
+//#define DEBUG if(cons.flags&apc.flag)print
+#define DEBUG print
+
+#define STATUSCHANGE(bit, on) { \
+	if (((apc.status.bits&(bit)) != 0) != (on)) { \
+		apc.status.change |= (bit); \
+		apc.status.bits = (apc.status.bits&~(bit)) | ((on)? (bit): 0); \
+	} \
+}
+
+enum {
+	OnBattery = 1,
+	LowBattery = 2,
+	AbnormalCondition = 4,
+	Uartspeed = 2400,
+	Trustlowbatt = 0,	// trust the low-battery bit?  i don't.
+};
+enum { First, Reinit };
+enum { Timedout = -1, Kicked = -2, Event = -3 };	/* apcgetc return */
+
+typedef enum { General, Cycle } CmdType;
+typedef enum { UpsOff, JustOff } Action;	/* unimplemented to date */
+
+typedef struct Capability {
+	char	cmd;
+	int	n;
+	struct Capability *next;
+	char	*val[1];
+} Capability;
+
+static struct apc {
+	Lock;
+	int	flag;
+	int	gotreply;
+	int	kicked;
+	int	detected;
+	Rendez	r;
+	Rendez	doze;
+	struct {
+		Lock;
+		Rendez;
+		int	count;
+		uchar	buf[100];
+		uchar	*in;
+		uchar	*out;
+	} rxq;
+	struct {
+		Rendez;
+		int	done;
+		CmdType	cmdtype;
+		char	cmd;
+		void	*arg1, *arg2;
+		char	resp[500];	/* response */
+	} user;
+	/* hardware info */
+	char	model[50];
+	char	fwrev[10];		/* firmware revision */
+
+	Capability *cap;
+	struct {
+		ulong	bits;
+		ulong	change;
+	} status;
+	/* battery info */
+	Timet	battonticks;		/* ticks with battery on */
+	Timet	lastrepticks;		/* ticks at last report */
+	ulong	battpct;		/* battery percent */
+	ulong	battpctthen;
+
+	ulong	trigger;
+	Action	action;
+	int	port;
+} apc;
+
+extern void uartspecial1(int port, void (*rx)(int), int (*tx)(void), int baud);
+extern void uartputc1(int c);
+
+void idle(void);
+
+static int
+kicked(void*)
+{
+	return apc.kicked != 0;
+}
+
+static int
+apctxint(void)
+{
+	return -1;
+}
+
+static void
+apcputc(char c)
+{
+	uartputc1(c);
+}
+
+static void
+apcputs(char *s)
+{
+	while (*s) {
+		delay(10);
+		apcputc(*s++);
+	}
+}
+
+void
+apcrxint(int c)
+{
+	int s;
+	uchar *p;
+
+	s = splhi();
+	lock(&apc.rxq);
+	if (apc.rxq.count < sizeof(apc.rxq.buf)) {
+		p = apc.rxq.in;
+		*p++ = c;
+		if(p >= apc.rxq.buf + sizeof(apc.rxq.buf))
+			p = apc.rxq.buf;
+		apc.rxq.in = p;
+		apc.rxq.count++;
+		wakeup(&apc.rxq);
+	}
+	unlock(&apc.rxq);
+	splx(s);
+}
+
+static int
+done(void *p)
+{
+	return *(int *)p != 0;
+}
+
+static int
+eitherdone(void *)
+{
+	return apc.rxq.count != 0 || apc.kicked;
+}
+
+int
+apcgetc(int timo, int noevents)
+{
+	char c;
+	int s;
+
+loop:
+	if (timo < 0)
+		sleep(&apc.rxq, eitherdone, 0);
+	else
+		tsleep(&apc.rxq, eitherdone, 0, timo);
+	if (apc.kicked)
+		return Kicked;
+
+	s = splhi();
+	lock(&apc.rxq);
+	if (apc.rxq.count == 0) {
+		unlock(&apc.rxq);
+		splx(s);
+		if (timo >= 0)
+			return Timedout;
+		goto loop;
+	}
+	c = *apc.rxq.out++;
+	if (apc.rxq.out >= apc.rxq.buf + sizeof(apc.rxq.buf))
+		apc.rxq.out = apc.rxq.buf;
+	apc.rxq.count--;
+	unlock(&apc.rxq);
+	splx(s);
+
+	switch (c) {
+	case '!':
+		STATUSCHANGE(OnBattery, 1);
+report_event:
+		print("apc: event %c\n", c);
+		if (noevents)
+			goto loop;
+		return Event;
+	case '$':
+		STATUSCHANGE(OnBattery, 0);
+		goto report_event;
+	case '%':
+		STATUSCHANGE(LowBattery, 1);
+		goto report_event;
+	case '+':
+		STATUSCHANGE(LowBattery, 0);
+		goto report_event;
+	case '?':
+		STATUSCHANGE(AbnormalCondition, 1);
+		goto report_event;
+	case '=':
+		STATUSCHANGE(AbnormalCondition, 0);
+		goto report_event;
+	case '*':
+		print("apc: turning off\n");
+		goto loop;
+	case '#':
+		print("apc: replace battery\n");
+		goto loop;
+	case '&':
+		print("apc: check alarm register\n");
+		goto loop;
+	case 0x7c:
+		print("apc: eeprom modified\n");
+		goto loop;
+	default:
+		break;
+	}
+
+//	print("apc: got 0x%.2ux\n", c);
+	return c;
+}
+
+char *
+apcgets(char *buf, int len, int timo)
+{
+	char *q;
+	int c;
+
+	q = buf;
+	while ((c = apcgetc(timo, 1)) >= 0 && c != '\r')
+		if (q < buf + len - 1)
+			*q++ = c;
+	if (c < 0)
+		return nil;
+
+	c = apcgetc(timo, 1);
+	if (c < 0 || c != '\n')
+		return nil;
+
+	*q = 0;
+	return buf;
+}
+
+int
+apcexpect(char *s, int skiprubbish, int timo)
+{
+	int first = 1;
+
+	while (*s) {
+		int c = apcgetc(timo, 1);
+
+		if (c < 0)
+			return 0;
+		if (*s == c) {
+			s++;
+			first = 0;
+			continue;
+		}
+		if (!first)
+			return 0;
+		if (!skiprubbish)
+			return 0;
+		first = 0;
+	}
+	return 1;
+}
+
+int
+apcattention(void)				/* anybody home? */
+{
+	apcputc('Y');
+	if (!apcexpect("SM\r\n", 1, 1000))
+		return 0;
+	apc.detected = 1;
+	return 1;
+}
+
+char *
+apccmdstrresponse(char *cmd, char *buf, int len)
+{
+	char *s;
+
+	apcputs(cmd);
+	s = apcgets(buf, len, 1000);
+	if (s == nil) {
+		print("APC asleep...\n");
+		if (!apcattention())
+			return nil;
+		apcputs(cmd);
+		return apcgets(buf, len, 1000);
+	}
+	return s;
+}
+
+char *
+apccmdresponse(char cmd, char *buf, int len)
+{
+	char cmdstr[2];
+
+	cmdstr[0] = cmd;
+	cmdstr[1] = 0;
+	return apccmdstrresponse(cmdstr, buf, len);
+}
+
+static void
+parsecap(char *capstr, char locale)
+{
+	char cmd, lc, c;
+	int n, el, i, j, p;
+
+	while (*capstr) {
+		char *s;
+		Capability *cap;
+
+		cmd = *capstr++;
+		lc = *capstr++;
+		n = *capstr++ - '0';
+		el = *capstr++ - '0';
+		p = lc == '4' || lc == locale;
+		if (p) {
+			cap = ialloc(sizeof *cap + sizeof s*(n - 1), 0);
+			cap->cmd = cmd;
+			cap->n = n;
+			s = ialloc(n*(el + 1), 0);
+			for (i = 0; i < n; i++) {
+				cap->val[i] = s + i*(el + 1);
+				cap->val[i][el] = 0;
+			}
+		} else
+			cap = nil;
+		for (i = 0; i < n; i++)
+			for (j = 0; j < el; j++) {
+				c = *capstr++;
+				if (p)
+					cap->val[i][j] = c;
+			}
+		if (p) {
+			cap->next = apc.cap;
+			apc.cap = cap;
+		}
+	}
+}
+
+static char *
+cyclecmd(Capability *cap, int i)
+{
+	char *s;
+
+	for (;;) {
+		char resp[10];
+
+		s = apccmdresponse(cap->cmd, resp, sizeof(resp));
+		if (s == nil || strcmp(resp, cap->val[i]) == 0)
+			break;
+		s = apccmdresponse('-', resp, sizeof(resp));
+		if (s == nil)
+			break;
+	}
+	return s;
+}
+
+static ulong
+getfloat(char *p, int dp)
+{
+	ulong total;
+	int afterdp = -1;
+
+	total = 0;
+	for (; *p; p++)
+		if (*p == '.')
+			afterdp = 0;
+		else {
+			total = total*10 + *p - '0';
+			if (afterdp >= 0)
+				afterdp++;
+		}
+	if (afterdp < 0)
+		afterdp = 0;
+	while (afterdp > dp + 1) {
+		afterdp--;
+		total /= 10;
+	}
+	if (afterdp > dp) {
+		afterdp--;
+		total = (total + 5) / 10;
+	}
+	while (dp > afterdp) {
+		afterdp++;
+		total *= 10;
+	}
+	return total;
+}
+
+static int
+apcgetstatus(void)
+{
+	char resp[10];
+	ulong status, change;
+
+	do {
+		change = apc.status.change;
+		if (apccmdresponse('Q', resp, sizeof(resp)) == nil)
+			return 0;
+	} while (apc.status.change != change);
+	status = strtoul(resp, 0, 16);
+	if (status&(1 << 3) && apc.status.bits&OnBattery) {	/* online? */
+		apc.status.bits  &= ~OnBattery;
+		apc.status.change |= OnBattery;
+	}
+	if (status&(1 << 4) && apc.status.bits&OnBattery) {	/* on battery */
+//		apc.status.bits   |= OnBattery;
+		apc.status.change |= OnBattery;
+	}
+	if (((status&(1 << 6)) != 0) != ((apc.status.bits&LowBattery) != 0)) {
+		/* low battery */
+		apc.status.bits   ^= LowBattery;
+		apc.status.change |= LowBattery;
+	}
+	if (apccmdresponse('f', resp, sizeof(resp)) == nil)
+		return 0;
+	apc.battpct = getfloat(resp, 1);
+	return 1;
+}
+
+/*
+ * shutdown the file server gracefully.
+ */
+static void
+apcshuffle(char *why)
+{
+	char resp[10];
+
+	print("Shutting down due to %s\n", why);
+	wlock(&mainlock);	/* don't process incoming requests from net */
+	sync("powerfail");
+	apccmdstrresponse("@000",  resp, sizeof(resp));
+	print("APC responded: '%s'\n", resp);
+	print("File server is now idling.\n");
+	delay(2000);
+	splhi();
+	for (;;)
+		idle();		/* wait for the lights to go out */
+}
+
+static void
+apckick(void)
+{
+	if (apc.detected) {		/* don't blather once per minute */
+		print("No APC ups detected\n");
+		apc.detected = 0;
+	}
+	apc.kicked = 0;
+	tsleep(&apc.doze, kicked, 0, 1 * 60 * 1000);
+}
+
+static void
+apcsetup(int reinit)
+{
+	int dead;
+	Capability *cap;
+
+	if (reinit)
+		apckick();
+	do {
+		while (!apcattention())
+			apckick();
+
+		apcputc(1);
+		apcgets(apc.model, sizeof(apc.model), -1);
+		print("APC UPS model: %s\n", apc.model);
+
+		apcputc('b');
+		apcgets(apc.fwrev, sizeof(apc.fwrev), -1);
+		print("Firmware revision: %s\n", apc.fwrev);
+
+		apcputc('');
+		apcgets(apc.user.resp, sizeof(apc.user.resp), -1);
+		parsecap(apc.user.resp, apc.fwrev[strlen(apc.fwrev) - 1]);
+
+		for (cap = apc.cap; cap; cap = cap->next) {
+			int i;
+
+			print("%c %d", cap->cmd, cap->n);
+			for (i = 0; i < cap->n; i++)
+				print(" %s", cap->val[i]);
+			print("\n");
+		}
+
+		apc.status.change = 0;
+		dead = 0;
+		if (!apcgetstatus()) {
+			apckick();
+			dead = 1;
+		}
+	} while (dead);
+}
+
+static void
+apcbatton(void)
+{
+	Timet now, nextreport;
+
+	now = MACHP(0)->ticks;
+	if (apc.status.change & OnBattery) {
+		apc.lastrepticks = apc.battonticks = nextreport = now;
+		apc.battpctthen = apc.battpct;
+	} else
+		nextreport = apc.lastrepticks + MS2TK(30 * 1000);
+	if (now - nextreport >= 0) {
+		print("apc: on battery %lud seconds (%lud.%lud%%)",
+			TK2SEC(now - apc.battonticks),
+			apc.battpct / 10, apc.battpct % 10);
+		if (apc.battpct < apc.battpctthen - 10) {
+			Timet remaining = ((apc.battpct - apc.trigger) *
+			  TK2SEC(now - apc.battonticks)) /
+			  (apc.battpctthen - apc.battpct);
+
+			print(" - estimated %lud seconds left", remaining);
+		}
+		print("\n");
+		apc.lastrepticks = now;
+	}
+	if (apc.battpct <= apc.trigger)
+		apcshuffle("battery percent too low");
+	if (Trustlowbatt && apc.status.bits & LowBattery)
+		apcshuffle("low battery indicator");
+}
+
+void
+apctask(void)
+{
+	tsleep(&apc.doze, kicked, 0, 10 * 1000);
+
+	/* set up the serial port to the UPS */
+	DEBUG("apc: running: port %d trigger below %lud%% action %s\n",
+		apc.port, apc.trigger / 10,
+		(apc.action == UpsOff? "ups off": "just off"));
+	apc.rxq.in = apc.rxq.out = apc.rxq.buf;
+	uartspecial1(apc.port, apcrxint, apctxint, Uartspeed);
+
+	/*
+	 * pretend we've been talking to it so we'll get an
+	 * error message if it's not there.
+	 */
+	apc.detected = 1;
+	apcsetup(First);
+	for (;;) {
+		char *s;
+		int c;
+
+		if ((apc.status.bits & OnBattery))
+			apcbatton();
+		apc.kicked = 0;
+		apc.status.change = 0;
+
+		c = apcgetc(10 * 1000, 0);
+		if (c == Timedout || c == Event) {
+			if (!apcgetstatus())
+				apcsetup(Reinit);
+		} else if (c == Kicked) {
+			apc.kicked = 0;
+			switch (apc.user.cmdtype) {
+			case General:
+				s = apccmdresponse(apc.user.cmd,
+				  apc.user.resp, sizeof apc.user.resp);
+				break;
+			case Cycle:
+				s = cyclecmd((Capability *)apc.user.arg1,
+					(int)apc.user.arg2);
+				break;
+			default:
+				s = nil;
+				break;
+			}
+			apc.user.done = 1;
+			wakeup(&apc.user);
+			if (s == nil)
+				apcsetup(Reinit);
+		} else
+			print("apc: unexpected character '%c' (0x%.2ux)\n",
+				c, c);
+	}
+}
+
+static void
+enquiry(CmdType t, char c, void *arg1, void *arg2)
+{
+	apc.user.cmdtype = t;
+	apc.user.cmd = c;
+	apc.user.arg1 = arg1;
+	apc.user.arg2 = arg2;
+	apc.user.done = 0;
+	apc.kicked = 1;
+	apc.user.resp[0] = 0;
+	wakeup(&apc.rxq);
+	/*
+	 * BUG: can hang here forever if cable to UPS falls out or
+	 * is wired wrong (need a null modem).
+	 */
+	sleep(&apc.user, done, &apc.user.done);
+	if (apc.user.resp[0]) {
+		print("'%s'\n", apc.user.resp);
+		apc.user.resp[0] = 0;
+	}
+}
+
+static struct {
+	char ch;
+	char *cmd;
+} generalenquiries[] = {
+	{ '', "capabilities" },
+	{ 'B', "batteryvolts" },
+	{ 'C', "temperature" },
+	{ 'E', "selftestinterval" },
+	{ 'F', "frequency" },
+	{ 'L', "lineinvolts" },
+	{ 'M', "maxlineinvolts" },
+	{ 'N', "minlineinvolts" },
+	{ 'O', "lineoutvolts" },
+	{ 'P', "powerload" },
+	{ 'Q', "status" },
+	{ 'V', "firmware" },
+	{ 'X', "selftestresults" },
+	{ 'a', "protocol" },
+	{ 'b', "localid" },
+	{ 'e', "returnthresh" },
+	{ 'g', "nominalbatteryvolts" },
+	{ 'f', "battpct" },
+	{ 'h', "humidity" },
+	{ 'i', "contacts" },
+	{ 'j', "runtime" },
+	{ 'k', "alarmdelay" },
+	{ 'l', "lowtransfervolts" },
+	{ 'm', "manufactured" },
+	{ 'n', "serial" },
+	{ 'o', "onbatteryvolts" },
+	{ 'p', "grace" },
+	{ 'q', "lowbatterywarntime" },
+	{ 'r', "wakeupdelay" },
+	{ 's', "sensitivity" },
+	{ 'u', "uppertransfervolts" },
+	{ 'x', "lastbatterychange" },
+	{ 'y', "copyright" },
+	{ '~', "register1" },
+	{ ''', "register2" },
+	{ '7', "switches" },
+	{ '8', "register3" },
+	{ '9', "linequality" },
+	{ '>', "batterypacks" },
+	{ '-', "cycle" },
+};
+
+int
+vaguelyequal(char *a, char *b)
+{
+	return strcmp(a, b) == 0;
+}
+
+static void
+cycle(char *name, char *val)
+{
+	int g, i;
+	Capability *cap;
+
+	if (strcmp(name, "trigger") == 0) {
+		apc.trigger = getfloat(val, 1);
+		return;
+	}
+
+	/* convert name to enquiry */
+	for (g = 0; g < nelem(generalenquiries); g++)
+		if (strcmp(name, generalenquiries[g].cmd) == 0)
+			break;
+	if (g >= nelem(generalenquiries)) {
+		print("no such parameter '%s'\n", name);
+		return;
+	}
+	/* match enquiry to capability */
+	for (cap = apc.cap; cap; cap = cap->next)
+		if (cap->cmd == generalenquiries[g].ch)
+			break;
+	if (cap == nil) {
+		print("parameter %s cannot be set\n", name);
+		return;
+	}
+	/* search capability's legal values */
+	for (i = 0; i < cap->n; i++)
+		if (vaguelyequal(cap->val[i], val))
+			break;
+	if (i >= cap->n) {
+		print("%s: illegal value %s; try one of [", name, val);
+		for (i = 0; i < cap->n; i++) {
+			if (i > 0)
+				print(" ");
+			print("%s", cap->val[i]);
+		}
+		print("]\n");
+	} else
+		enquiry(Cycle, cap->cmd, cap, (void *)i);
+}
+
+void
+cmd_apc(int argc, char *argv[])
+{
+	int i, x;
+
+	if(argc <= 1) {
+		print("apc kick -- play now\n");
+		print("apc set var val -- set var to val\n");
+		print("apc enquiry... -- query the ups\n");
+		return;
+	}
+	for (i = 1; i < argc; i++) {
+		if(strcmp(argv[i], "kick") == 0) {
+			apc.kicked = 1;
+			wakeup(&apc.doze);
+			continue;
+		}
+		if(strcmp(argv[i], "set") == 0) {
+			if (argc - i >= 3)
+				cycle(argv[i + 1], argv[i + 2]);
+			i += 2;
+			continue;
+		}
+		for (x = 0; x < nelem(generalenquiries); x++)
+			if (strcmp(argv[i], generalenquiries[x].cmd) == 0)
+				break;
+		if (x < nelem(generalenquiries))
+			enquiry(General, generalenquiries[x].ch, nil, nil);
+		else {
+			print("no such parameter '%s'\n", argv[i]);
+			return;
+		}
+	}
+}
+
+void
+apcinit(void)
+{
+	ISAConf isa;
+	int o;
+
+	print("apcinit...");
+	memset(&isa, 0, sizeof isa);	/* prevent surprises */
+	isa.port = 1;			/* default port */
+	if (!isaconfig("ups", 0, &isa) || strcmp(isa.type, "apc") != 0) {
+		print("no ups in plan9.ini, or not type `apc'\n");
+		return;
+	}
+
+	cmd_install("apc", "subcommand -- apc ups driver", cmd_apc);
+	apc.flag = flag_install("apc", "-- verbose");
+
+	apc.trigger = 1000;
+	apc.action = UpsOff;
+
+	for (o = 0; o < isa.nopt; o++)
+		if (cistrncmp(isa.opt[o], "trigger=", 8) == 0)
+			apc.trigger = strtoul(isa.opt[o] + 8, 0, 0) * 10;
+		else if (cistrncmp(isa.opt[o], "action=", 7) == 0) {
+			if (strcmp(isa.opt[o] + 8, "off") == 0)
+				apc.action = JustOff;
+		}
+
+	apc.port = isa.port;
+	/*
+	 * it's a little early to be starting this, before config mode is
+	 * even started.
+	 */
+	print("apc...\n");
+	userinit(apctask, 0, "apc");
+}

File diff suppressed because it is too large
+ 199 - 181
sys/src/fs/dev/cw.c


+ 17 - 18
sys/src/fs/dev/fworm.c

@@ -3,11 +3,11 @@
 #define	DEBUG		0
 #define	FDEV(d)		(d->fw.fw)
 
-long
+Devsize
 fwormsize(Device *d)
 {
 	Device *fdev;
-	long l;
+	Devsize l;
 
 	fdev = FDEV(d);
 	l = devsize(fdev);
@@ -20,15 +20,15 @@ fwormream(Device *d)
 {
 	Iobuf *p;
 	Device *fdev;
-	long a, b;
+	Off a, b;
 
 	print("fworm ream\n");
 	devinit(d);
 	fdev = FDEV(d);
 	a = fwormsize(d);
 	b = devsize(fdev);
-	print("	fwsize = %ld\n", a);
-	print("	bwsize = %ld\n", b-a);
+	print("	fwsize = %lld\n", (Wideoff)a);
+	print("	bwsize = %lld\n", (Wideoff)b-a);
 	for(; a < b; a++) {
 		p = getbuf(fdev, a, Bmod|Bres);
 		if(!p)
@@ -42,34 +42,33 @@ fwormream(Device *d)
 void
 fworminit(Device *d)
 {
-
 	print("fworm init\n");
 	devinit(FDEV(d));
 }
 
 int
-fwormread(Device *d, long b, void *c)
+fwormread(Device *d, Off b, void *c)
 {
 	Iobuf *p;
 	Device *fdev;
-	long l;
+	Devsize l;
 
 	if(DEBUG)
-		print("fworm read  %ld\n", b);
+		print("fworm read  %lld\n", (Wideoff)b);
 	fdev = FDEV(d);
 	l = devsize(fdev);
 	l -= l/(BUFSIZE*8) + 1;
 	if(b >= l)
-		panic("fworm: rbounds %ld\n", b);
+		panic("fworm: rbounds %lld\n", (Wideoff)b);
 	l += b/(BUFSIZE*8);
 
 	p = getbuf(fdev, l, Bread|Bres);
 	if(!p || checktag(p, Tvirgo, l))
-		panic("fworm: checktag %ld\n", l);
+		panic("fworm: checktag %lld\n", (Wideoff)l);
 	l = b % (BUFSIZE*8);
 	if(!(p->iobuf[l/8] & (1<<(l%8)))) {
 		putbuf(p);
-		print("fworm: read %ld\n", b);
+		print("fworm: read %lld\n", (Wideoff)b);
 		return 1;
 	}
 	putbuf(p);
@@ -77,28 +76,28 @@ fwormread(Device *d, long b, void *c)
 }
 
 int
-fwormwrite(Device *d, long b, void *c)
+fwormwrite(Device *d, Off b, void *c)
 {
 	Iobuf *p;
 	Device *fdev;
-	long l;
+	Devsize l;
 
 	if(DEBUG)
-		print("fworm write %ld\n", b);
+		print("fworm write %lld\n", (Wideoff)b);
 	fdev = FDEV(d);
 	l = devsize(fdev);
 	l -= l/(BUFSIZE*8) + 1;
 	if(b >= l)
-		panic("fworm: wbounds %ld\n", b);
+		panic("fworm: wbounds %lld\n", (Wideoff)b);
 	l += b/(BUFSIZE*8);
 
 	p = getbuf(fdev, l, Bread|Bmod|Bres);
 	if(!p || checktag(p, Tvirgo, l))
-		panic("fworm: checktag %ld", l);
+		panic("fworm: checktag %lld", (Wideoff)l);
 	l = b % (BUFSIZE*8);
 	if((p->iobuf[l/8] & (1<<(l%8)))) {
 		putbuf(p);
-		print("fworm: write %ld\n", b);
+		print("fworm: write %lld\n", (Wideoff)b);
 		return 1;
 	}
 	p->iobuf[l/8] |= 1<<(l%8);

+ 31 - 46
sys/src/fs/dev/juke.c

@@ -17,8 +17,8 @@ struct	Side
 	uchar	rot;		/* if backside */
 	int	ord;		/* ordinal number for labeling */
 
-	long	time;		/* time since last access, to unspin */
-	long	stime;		/* time since last spinup, for hysteresis */
+	Timet	time;		/* time since last access, to unspin */
+	Timet	stime;		/* time since last spinup, for hysteresis */
 	long	nblock;		/* number of native blocks */
 	long	block;		/* bytes per native block */
 	long	mult;		/* multiplier to get plan9 blocks */
@@ -199,7 +199,7 @@ bestdrive(Juke *w, int side)
 {
 	Side *v, *bv[MAXDRIVE];
 	int i, s, e, drive;
-	long t, t0;
+	Timet t, t0;
 
 loop:
 	/* build table of what platters on what drives */
@@ -268,27 +268,25 @@ loop:
 	return drive;
 }
 
-long
+Devsize
 wormsize(Device *d)
 {
 	Side *v;
 	Juke *w;
-	long size;
+	Devsize size;
 
 	w = d->private;
-	if(w->fixedsize) {
+	if(w->fixedsize)
 		size = w->fixedsize;
-		goto out;
+	else {
+		v = wormunit(d);
+		if(v == 0)
+			return 0;
+		size = v->max;
+		qunlock(v);
+		if(FIXEDSIZE) // TODO? push FIXEDSIZE into Device or Juke struct
+			w->fixedsize = size;
 	}
-
-	v = wormunit(d);
-	if(v == 0)
-		return 0;
-	size = v->max;
-	qunlock(v);
-	if(FIXEDSIZE)	// TODO? push FIXEDSIZE into Device or Juke struct
-		w->fixedsize = size;
-out:
 	if(d->type == Devlworm)
 		return size-1;
 	return size;
@@ -397,10 +395,10 @@ typedef struct {
  * the main complication is mcats and the like with Devjukes in them.
  * use Devjuke's d->private as Juke* and see sides.
  */
-static long
+static Off
 visitsides(Device *d, Device *parentj, Visit *vp)
 {
-	long size = 0;
+	Off size = 0;
 	Device *x;
 	Juke *w;
 
@@ -472,10 +470,10 @@ visitsides(Device *d, Device *parentj, Visit *vp)
  * etc. if called from chk.c Ctouch code.  Note too that the worm part of
  * the Devcw might be other than a Devjuke.
  */
-long
+Devsize
 wormsizeside(Device *d, int side)
 {
-	long size;
+	Devsize size;
 	Visit visit;
 
 	memset(&visit, 0, sizeof visit);
@@ -497,7 +495,7 @@ void
 wormsidestarts(Device *dev, int side, Sidestarts *stp)
 {
 	int s;
-	long dstart;
+	Devsize dstart;
 
 	for (dstart = s = 0; s < side; s++)
 		dstart += wormsizeside(dev, s);
@@ -507,12 +505,13 @@ wormsidestarts(Device *dev, int side, Sidestarts *stp)
 
 static
 int
-wormiocmd(Device *d, int io, long b, void *c)
+wormiocmd(Device *d, int io, Off b, void *c)
 {
 	Side *v;
 	Juke *w;
-	long l, m;
+	Off l;
 	int s;
+	long m;
 	uchar cmd[10];
 
 	w = d->private;
@@ -521,7 +520,7 @@ wormiocmd(Device *d, int io, long b, void *c)
 		return 0x71;
 	if(b >= v->max) {
 		qunlock(v);
-		print("worm: wormiocmd out of range %Z(%ld)\n", d, b);
+		print("worm: wormiocmd out of range %Z(%lld)\n", d, (Wideoff)b);
 		return 0x071;
 	}
 
@@ -546,13 +545,13 @@ wormiocmd(Device *d, int io, long b, void *c)
 }
 
 int
-wormread(Device *d, long b, void *c)
+wormread(Device *d, Off b, void *c)
 {
 	int s;
 
 	s = wormiocmd(d, SCSIread, b, c);
 	if(s) {
-		print("wormread: %Z(%ld) bad status #%x\n", d, b, s);
+		print("wormread: %Z(%lld) bad status #%x\n", d, (Wideoff)b, s);
 		cons.nwormre++;
 		return s;
 	}
@@ -560,13 +559,13 @@ wormread(Device *d, long b, void *c)
 }
 
 int
-wormwrite(Device *d, long b, void *c)
+wormwrite(Device *d, Off b, void *c)
 {
 	int s;
 
 	s = wormiocmd(d, SCSIwrite, b, c);
 	if(s) {
-		print("wormwrite: %Z(%ld) bad status #%x\n", d, b, s);
+		print("wormwrite: %Z(%lld) bad status #%x\n", d, (Wideoff)b, s);
 		cons.nwormwe++;
 		return s;
 	}
@@ -666,7 +665,6 @@ element(Juke *w, int e)
 	uchar cmd[12], buf[8+8+88];
 	int s, t;
 
-//loop:
 	memset(cmd, 0, sizeof(cmd));
 	memset(buf, 0, sizeof(buf));
 	cmd[0] = 0xb8;		/* read element status */
@@ -770,10 +768,8 @@ element(Juke *w, int e)
 		break;
 	}
 	return;
-
 bad:
-//	panic("element");
-	return;
+	/* panic("element") */ ;
 }
 
 static
@@ -820,7 +816,7 @@ jinit(Juke *w, Device *d, int o)
 	switch(d->type) {
 	default:
 		print("juke platter not (devmcat of) dev(l)worm: %Z\n", d);
-		goto bad;
+		panic("jinit: type");
 
 	case Devmcat:
 		/*
@@ -842,15 +838,11 @@ jinit(Juke *w, Device *d, int o)
 		if(d->private) {
 			print("juke platter private pointer set %p\n",
 				d->private);
-			goto bad;
+			panic("jinit: private");
 		}
 		d->private = w;
 		break;
 	}
-	return;
-
-bad:
-	panic("jinit");
 }
 
 Side*
@@ -1127,12 +1119,6 @@ bad:
 	panic("juke init");
 }
 
-int
-dowcp(void)
-{
-	return 0;
-}
-
 /*
  * called periodically
  */
@@ -1140,11 +1126,10 @@ void
 wormprobe(void)
 {
 	int i, drive;
-	long t;
+	Timet t;
 	Side *v;
 	Juke *w;
 
-
 	t = toytime() - TWORM;
 	for(w=jukelist; w; w=w->link) {
 		if(w->probeok == 0 || !canqlock(w))

+ 28 - 28
sys/src/fs/dev/mworm.c

@@ -22,11 +22,11 @@ mcatinit(Device *d)
 	}
 }
 
-long
+Devsize
 mcatsize(Device *d)
 {
 	Device *x;
-	long l, m;
+	Devsize l, m;
 
 	l = 0;
 	for(x=d->cat.first; x; x=x->link) {
@@ -41,10 +41,10 @@ mcatsize(Device *d)
 }
 
 int
-mcatread(Device *d, long b, void *c)
+mcatread(Device *d, Off b, void *c)
 {
 	Device *x;
-	long l, m;
+	Devsize l, m;
 
 	l = 0;
 	for(x=d->cat.first; x; x=x->link) {
@@ -57,15 +57,15 @@ mcatread(Device *d, long b, void *c)
 			return devread(x, b-l, c);
 		l += m;
 	}
-	print("mcatread %ld %ld\n", b, l);
+	print("mcatread %lld %lld\n", (Wideoff)b, (Wideoff)l);
 	return 1;
 }
 
 int
-mcatwrite(Device *d, long b, void *c)
+mcatwrite(Device *d, Off b, void *c)
 {
 	Device *x;
-	long l, m;
+	Devsize l, m;
 
 	l = 0;
 	for(x=d->cat.first; x; x=x->link) {
@@ -78,7 +78,7 @@ mcatwrite(Device *d, long b, void *c)
 			return devwrite(x, b-l, c);
 		l += m;
 	}
-	print("mcatwrite %ld %ld\n", b, l);
+	print("mcatwrite %lld %lld\n", (Wideoff)b, (Wideoff)l);
 	return 1;
 }
 
@@ -95,12 +95,12 @@ mlevinit(Device *d)
 		x->size = devsize(x);
 }
 
-long
+Devsize
 mlevsize(Device *d)
 {
 	Device *x;
 	int n;
-	long m, min;
+	Devsize m, min;
 
 	min = 0;
 	n = 0;
@@ -118,7 +118,7 @@ mlevsize(Device *d)
 }
 
 int
-mlevread(Device *d, long b, void *c)
+mlevread(Device *d, Off b, void *c)
 {
 	int n;
 	Device **list;
@@ -129,7 +129,7 @@ mlevread(Device *d, long b, void *c)
 }
 
 int
-mlevwrite(Device *d, long b, void *c)
+mlevwrite(Device *d, Off b, void *c)
 {
 	int n;
 	Device **list;
@@ -150,10 +150,10 @@ partinit(Device *d)
 	d->part.d->size = devsize(d->part.d);
 }
 
-long
+Devsize
 partsize(Device *d)
 {
-	long size, l;
+	Devsize size, l;
 
 	l = d->part.d->size / 100;
 	size = d->part.size * l;
@@ -163,9 +163,9 @@ partsize(Device *d)
 }
 
 int
-partread(Device *d, long b, void *c)
+partread(Device *d, Off b, void *c)
 {
-	long base, size, l;
+	Devsize base, size, l;
 
 	l = d->part.d->size / 100;
 	base = d->part.base * l;
@@ -174,14 +174,14 @@ partread(Device *d, long b, void *c)
 		size = l*100;
 	if(b < size)
 		return devread(d->part.d, base+b, c);
-	print("partread %ld %ld\n", b, size);
+	print("partread %lld %lld\n", (Wideoff)b, (Wideoff)size);
 	return 1;
 }
 
 int
-partwrite(Device *d, long b, void *c)
+partwrite(Device *d, Off b, void *c)
 {
-	long base, size, l;
+	Devsize base, size, l;
 
 	l = d->part.d->size / 100;
 	base = d->part.base * l;
@@ -190,7 +190,7 @@ partwrite(Device *d, long b, void *c)
 		size = l*100;
 	if(b < size)
 		return devwrite(d->part.d, base+b, c);
-	print("partwrite %ld %ld\n", b, size);
+	print("partwrite %lld %lld\n", (Wideoff)b, (Wideoff)size);
 	return 1;
 }
 
@@ -207,12 +207,12 @@ mirrinit(Device *d)
 		x->size = devsize(x);
 }
 
-long
+Devsize
 mirrsize(Device *d)
 {
 	Device *x;
 	int n;
-	long m, min;
+	Devsize m, min;
 
 	min = 0;
 	n = 0;
@@ -230,7 +230,7 @@ mirrsize(Device *d)
 }
 
 int
-mirrread(Device *d, long b, void *c)
+mirrread(Device *d, Off b, void *c)
 {
 	Device *x;
 
@@ -241,7 +241,7 @@ mirrread(Device *d, long b, void *c)
 			return 0;
 	}
 	// DANGER WILL ROBINSON - all copies of this block were bad
-	print("mirrread %Z error at block %ld\n", d, b);
+	print("mirrread %Z error at block %lld\n", d, (Wideoff)b);
 	return 1;
 }
 
@@ -254,19 +254,19 @@ mirrread(Device *d, long b, void *c)
  * implemented at higher levels.
  */
 static int
-ewrite(Device *x, long b, void *c)
+ewrite(Device *x, Off b, void *c)
 {
 	if(x->size == 0)
 		x->size = devsize(x);
 	if (devwrite(x, b, c) != 0) {
-		print("mirrwrite %Z error at block %ld\n", x, b);
+		print("mirrwrite %Z error at block %lld\n", x, (Wideoff)b);
 		return 1;
 	}
 	return 0;
 }
 
 static int
-wrmirrs1st(Device *x, long b, void *c)	// write any mirrors of x, then x
+wrmirrs1st(Device *x, Off b, void *c)	// write any mirrors of x, then x
 {
 	int e;
 
@@ -277,7 +277,7 @@ wrmirrs1st(Device *x, long b, void *c)	// write any mirrors of x, then x
 }
 
 int
-mirrwrite(Device *d, long b, void *c)
+mirrwrite(Device *d, Off b, void *c)
 {
 	return wrmirrs1st(d->cat.first, b, c);
 }

+ 0 - 961
sys/src/fs/dev/sony.c

@@ -1,961 +0,0 @@
-#include	"all.h"
-
-#define	SCSInone	SCSIread
-#define	MAXDRIVE	10
-#define	MAXSIDE		300
-
-#define	TWORM		MINUTE(10)
-#define	THYSTER		SECOND(10)
-
-#define	EXIT	0
-
-typedef	struct	Side	Side;
-struct	Side
-{
-	QLock;			/* protects loading/unloading */
-	int	elem;		/* element number */
-	int	drive;		/* if loaded, where */
-	uchar	status;		/* Sunload, etc */
-	uchar	rot;		/* if backside */
-	int	ord;		/* ordinal number for labeling */
-
-	long	time;		/* time since last access, to unspin */
-	long	stime;		/* time since last spinup, for hysteresis */
-	long	nblock;		/* number of native blocks */
-	long	block;		/* bytes per native block */
-	long	mult;		/* multiplier to get plan9 blocks */
-	long	max;		/* max size in plan9 blocks */
-};
-
-typedef	struct	Juke	Juke;
-struct	Juke
-{
-	QLock;			/* protects drive mechanism */
-	Side	side[MAXSIDE];
-	int	nside;		/* how many storage elements (*2 if rev) */
-	int	ndrive;		/* number of transfer elements */
-	int	swab;		/* data is swabbed */
-	Device*	juke;		/* devworm of changer */
-	Device*	drive[MAXDRIVE];/* devworm for i/o */
-	long	fixedsize;	/* one size fits all */
-	int	probeok;	/* wait for init to probe */
-
-	/* geometry returned by mode sense */
-	int	mt0,	nmt;
-	int	se0,	nse;
-	int	ie0,	nie;
-	int	dt0,	ndt;
-	int	rot;
-
-	Juke*	link;
-};
-static	Juke*	jukelist;
-
-enum
-{
-	Sempty = 0,	/* does not exist */
-	Sunload,	/* on the shelf */
-	Sstart,		/* loaded and spinning */
-};
-
-extern int FIXEDSIZE;
-
-static	Side*	wormunit(Device*);
-static	void	shelves(void);
-static	void	mmove(Juke*, int, int, int, int);
-static	int	bestdrive(Juke*, int);
-static	void	waitready(Device*);
-static	int	wormsense(Juke*, int);
-
-/*
- * mounts and spins up the device
- *	locks the structure
- */
-static
-Side*
-wormunit(Device *d)
-{
-	int p, s, drive;
-	Side *v;
-	Juke *w;
-	Device *dev;
-	uchar cmd[10], buf[8];
-
-	w = d->private;
-	p = d->wren.targ;
-	if(p < 0 || p >= w->nside)
-		panic("wormunit partition %Z\n", d);
-
-	/*
-	 * if disk is unloaded, must load it
-	 * into next (circular) logical unit
-	 */
-	v = &w->side[p];
-	qlock(v);
-	if(v->status == Sunload) {
-		for(;;) {
-			qlock(w);
-			drive = bestdrive(w, p);
-			if(drive >= 0)
-				break;
-			qunlock(w);
-			waitsec(100);
-		}
-		print("	load  r%ld drive %Z\n", v-w->side, w->drive[drive]);
-		mmove(w, w->mt0, v->elem, w->dt0+w->drive[drive]->wren.lun, v->rot);
-		v->drive = drive;
-		v->status = Sstart;
-		v->stime = toytime();
-		qunlock(w);
-		waitready(w->drive[drive]);
-		v->stime = toytime();
-	}
-	if(v->status != Sstart) {
-		if(v->status == Sempty)
-			print("worm: unit empty %Z\n", d);
-		else
-			print("worm: not started %Z\n", d);
-		goto sbad;
-	}
-
-	v->time = toytime();
-	if(v->block)
-		return v;
-
-	/*
-	 * capacity command
-	 */
-	dev = w->drive[v->drive];
-	memset(cmd, 0, sizeof(cmd));
-	memset(buf, 0, sizeof(buf));
-	cmd[0] = 0x25;	/* read capacity */
-	cmd[1] = (dev->wren.lun&0x7) << 5;
-	s = scsiio(w->drive[v->drive], SCSIread,
-		cmd, sizeof(cmd), buf, sizeof(buf));
-	if(s)
-		goto sbad;
-
-	v->nblock =
-		(buf[0]<<24) |
-		(buf[1]<<16) |
-		(buf[2]<<8) |
-		(buf[3]<<0);
-	v->block =
-		(buf[4]<<24) |
-		(buf[5]<<16) |
-		(buf[6]<<8) |
-		(buf[7]<<0);
-	v->mult =
-		(RBUFSIZE + v->block - 1) /
-		v->block;
-	v->max =
-		(v->nblock + 1) / v->mult;
-
-	print("	worm %Z: drive %Z\n", d, w->drive[v->drive]);
-	print("		%ld blocks at %ld bytes each\n",
-		v->nblock, v->block);
-	print("		%ld logical blocks at %d bytes each\n",
-		v->max, RBUFSIZE);
-	print("		%ld multiplier\n",
-		v->mult);
-	if(d->type != Devlworm)
-		return v;
-	/* check for label */
-	print("label %Z ordinal %d\n", d, v->ord);
-	qunlock(v);
-	return wormunit(d);
-
-sbad:
-	qunlock(v);
-	panic("wormunit sbad");
-	return 0;
-}
-
-static
-void
-waitready(Device *d)
-{
-	uchar cmd[6];
-	int s, e;
-
-	for(e=0;e<100;e++) {
-		memset(cmd, 0, sizeof(cmd));
-		cmd[1] = (d->wren.lun&0x7) << 5;
-		s = scsiio(d, SCSInone, cmd, sizeof(cmd), cmd, 0);
-		if(s == 0)
-			break;
-		waitsec(100);
-	}
-}
-
-static
-int
-bestdrive(Juke *w, int side)
-{
-	Side *v, *bv[MAXDRIVE];
-	int i, s, e, drive;
-	long t, t0;
-
-loop:
-	/* build table of what platters on what drives */
-	for(i=0; i<w->ndt; i++)
-		bv[i] = 0;
-
-	v = &w->side[0];
-	for(i=0; i<w->nside; i++, v++) {
-		s = v->status;
-		if(s == Sstart) {
-			drive = v->drive;
-			if(drive >= 0 && drive < w->ndt)
-				bv[drive] = v;
-		}
-	}
-
-	/*
-	 * find oldest drive, but must be
-	 * at least THYSTER old.
-	 */
-	e = w->side[side].elem;
-	t0 = toytime() - THYSTER;
-	t = t0;
-	drive = -1;
-	for(i=0; i<w->ndt; i++) {
-		v = bv[i];
-		if(v == 0) {		/* 2nd priority: empty drive */
-			if(w->drive[i] != devnone) {
-				drive = i;
-				t = 0;
-			}
-			continue;
-		}
-		if(v->elem == e) {	/* 1st priority: other side */
-			drive = -1;
-			if(v->stime < t0)
-				drive = i;
-			break;
-		}
-		if(v->stime < t) {	/* 3rd priority: by time */
-			drive = i;
-			t = v->stime;
-		}
-	}
-
-	if(drive >= 0) {
-		v = bv[drive];
-		if(v) {
-			qlock(v);
-			if(v->status != Sstart) {
-				qunlock(v);
-				goto loop;
-			}
-			print("	unload r%ld drive %Z\n",
-				v-w->side, w->drive[drive]);
-			mmove(w, w->mt0, w->dt0+w->drive[drive]->wren.lun,
-				v->elem, v->rot);
-			v->status = Sunload;
-			qunlock(v);
-		}
-	}
-	return drive;
-}
-
-long
-wormsize(Device *d)
-{
-	Side *v;
-	Juke *w;
-	long size;
-
-	w = d->private;
-	if(w->fixedsize) {
-		size = w->fixedsize;
-		goto out;
-	}
-
-	v = wormunit(d);
-	if(v == 0)
-		return 0;
-	size = v->max;
-	qunlock(v);
-	if(FIXEDSIZE)
-		w->fixedsize = size;
-out:
-	if(d->type == Devlworm)
-		return size-1;
-	return size;
-}
-
-long
-wormsizeside(Device *d, int side)
-{
-	USED(d, side);
-	return DSIZE;
-}
-
-/* returns starts of side #side and #(side+1) in *stp */
-void
-wormsidestarts(Device *dev, int side, Sidestarts *stp)
-{
-	USED(dev, side);
-	stp->sstart = side*DSIZE;
-	stp->s1start = stp->sstart + DSIZE;
-}
-
-static
-int
-wormiocmd(Device *d, int io, long b, void *c)
-{
-	Side *v;
-	Juke *w;
-	long l, m;
-	int s;
-	Device *dev;
-	uchar cmd[10];
-
-	w = d->private;
-	v = wormunit(d);
-	if(v == 0) {
-		qunlock(v);
-		return 0x71;
-	}
-	if(b >= v->max) {
-		qunlock(v);
-		print("worm: wormiocmd out of range %Z(%ld)\n", d, b);
-		return 0x071;
-	}
-
-	dev = w->drive[v->drive];
-	memset(cmd, 0, sizeof(cmd));
-	cmd[0] = 0x28;		/* extended read */
-	if(io != SCSIread) {
-		cmd[0] = 0x2a;	/* extended write */
-		if(w->swab)
-			swab(c, 1);
-	}
-	cmd[1] = (dev->wren.lun&0x7) << 5;
-
-	m = v->mult;
-	l = b * m;
-	cmd[2] = l>>24;
-	cmd[3] = l>>16;
-	cmd[4] = l>>8;
-	cmd[5] = l;
-
-	cmd[7] = m>>8;
-	cmd[8] = m;
-
-	s = scsiio(dev, io, cmd, sizeof(cmd), c, RBUFSIZE);
-	if(w->swab)
-		swab(c, 0);
-	qunlock(v);
-	return s;
-}
-
-int
-wormread(Device *d, long b, void *c)
-{
-	int s;
-
-	s = wormiocmd(d, SCSIread, b, c);
-	if(s) {
-		print("wormread: %Z(%ld) bad status #%x\n", d, b, s);
-		cons.nwormre++;
-		return s;
-	}
-	return 0;
-}
-
-int
-wormwrite(Device *d, long b, void *c)
-{
-	int s;
-
-	s = wormiocmd(d, SCSIwrite, b, c);
-	if(s) {
-		print("wormwrite: %Z(%ld) bad status #%x\n", d, b, s);
-		cons.nwormwe++;
-		return s;
-	}
-	return 0;
-}
-
-static
-void
-mmove(Juke *w, int trans, int from, int to, int rot)
-{
-	uchar cmd[10], buf[8];
-	int s, u, p;
-
-	s = 0;
-	if(trans != w->mt0)
-		goto bad;
-
-	if(from >= w->se0 && from <= w->se0+w->nse && 
-	   to >= w->dt0 && to < w->dt0+w->ndt) {
-		p = from - w->se0;
-		u = to - w->dt0;
-
-		print("worm: set %d; lun %d\n", p, u);
-		memset(cmd, 0, 6);
-		cmd[0] = 0xd6;	/* disk set */
-		cmd[1] = u << 5;
-		cmd[3] = (p << 1) | rot;
-		s = scsiio(w->juke, SCSInone, cmd, 6, buf, 0);
-		if(s)
-			goto bad;
-
-		memset(cmd, 0, 6);
-		cmd[0] = 0x15;	/* mode select */
-		cmd[1] = u << 5;
-		cmd[4] = 6;
-		memset(buf, 0, 6);
-		buf[2] = 1;	/* ebc */
-		buf[4] = 7;	/* ealt,evrf,edtre */
-		s = scsiio(w->juke, SCSIwrite, cmd, 6, buf, 6);
-		if(s)
-			goto bad;
-
-		print("worm: start %d; lun %d\n", p, u);
-		memset(cmd, 0, 6);
-		cmd[0] = 0x1b;	/* disk start */
-		cmd[1] = u << 5;
-		cmd[4] = 1;
-		s = scsiio(w->juke, SCSInone, cmd, 6, buf, 0);
-		if(s)
-			goto bad;
-		return;
-	}
-	if(from >= w->dt0 && from <= w->dt0+w->ndt && 
-	   to >= w->se0 && to < w->se0+w->nse) {
-		p = to - w->se0;
-		u = from - w->dt0;
-
-		print("worm: stop %d; lun %d\n", p, u);
-		memset(cmd, 0, 6);
-		cmd[0] = 0x1b;	/* disk stop */
-		cmd[1] = u << 5;
-		s = scsiio(w->juke, SCSInone, cmd, 6, buf, 0);
-		if(s)
-			goto bad;
-
-		print("worm: release %d; lun %d\n", p, u);
-		memset(cmd, 0, 6);
-		cmd[0] = 0xd7;	/* disk release */
-		cmd[1] = u << 5;
-		s = scsiio(w->juke, SCSInone, cmd, 6, buf, 0);
-		if(s)
-			goto bad;
-		return;
-	}
-	print("not load or unload\n");
-
-bad:
-	print("scsio status #%x\n", s);
-	print("move meduim t=%d fr=%d to=%d rot=%d", trans, from, to, rot);
-	panic("mmove");
-
-}
-
-static
-void
-geometry(Juke *w)
-{
-	int s;
-
-	s = wormsense(w, 0);
-	while(s == 0x70) {
-		delay(3000);
-		s = wormsense(w, 0);
-	}
-
-	w->se0 = 0;
-	w->nse = 50;
-	w->dt0 = 110;
-	w->ndt = 2;		/* 1-8 */
-	w->mt0 = 110;
-	w->nmt = 1;
-	w->ie0 = 111;
-	w->nie = 1;
-
-
-	w->rot = 1;
-
-	print("	mt %d %d\n", w->mt0, w->nmt);
-	print("	se %d %d\n", w->se0, w->nse);
-	print("	ie %d %d\n", w->ie0, w->nie);
-	print("	dt %d %d\n", w->dt0, w->ndt);
-	print("	rot %d\n", w->rot);
-	prflush();	/**/
-
-}
-
-static
-int
-wormsense(Juke *w, int lun)
-{
-	uchar cmd[6], buf[4];
-	int s;
-	char *sc;
-
-	memset(cmd, 0, 6);
-	cmd[0] = 0x03;	/* request sense */
-	cmd[1] = lun<<5;
-	memset(buf, 0, 4);
-	s = 0x70;	/* robs fault */
-	if(!scsiio(w->juke, SCSIread, cmd, 6, buf, 4))
-		s = buf[0] & 0x7f;
-	sc = wormscode[s];
-	if(!sc)
-		sc = "unknown";
-	print("	sense code %.2x %s\n", s, sc);
-	return s;
-}
-
-static
-void
-element(Juke *w, int e)
-{
-	uchar cmd[6], buf[128], *p, *q;
-	int s, i, j, pass, flag;
-
-	if(e != 0)
-		return;
-	pass = 0;
-
-loop:
-	flag = 0;
-	memset(cmd, 0, 6);
-	cmd[0] = 0x1d;	/* send diagnostics */
-	cmd[4] = 10;
-	memset(buf, 0, 10);
-	buf[0] = 0xe2;	/* read internal status */
-	s = scsiio(w->juke, SCSIwrite, cmd, 6, buf, 10);
-	if(s) {
-		print("element: read internal status %Z: %d\n", w->juke, s);
-		goto sbad;
-	}
-
-	memset(cmd, 0, 6);
-	cmd[0] = 0x1c;	/* recv diagnostics */
-	cmd[4] = 128;
-	memset(buf, 0, 128);
-	s = scsiio(w->juke, SCSIread, cmd, 6, buf, 128);
-	if(s)
-		goto sbad;
-
-	for(i=0; i<8; i++) {
-		p = buf+16+ 4*i;
-		if(p[0] & 0x80) {
-			print("	lun %d drive power off\n", i);
-			goto bad;
-		}
-		if(p[0] & 0x40) {
-			if(!(p[2] & 0x80)) {
-				for(j=0; j<w->nse; j++) {
-					q = buf+48+j;
-					if(q[0] & 0x80)		/* empty */
-						continue;
-					memset(cmd, 0, 6);
-					cmd[0] = 0xd7;	/* disk release */
-					cmd[1] = (i << 5) | 1;
-					cmd[3] = j<<1;
-					print("	lun %d drive loaded, assigning shelf %d\n", i, j);
-					s = scsiio(w->juke, SCSInone, cmd, 6, buf, 0);
-					if(s)
-						goto bad;
-					q[0] = 0x40;		/* not empty and assigned */
-					goto cont1;
-				}
-				print("	lun %d drive loaded, no return shelf\n", i);
-				goto bad;
-			}
-			print("	lun %d drive loaded, unloading\n", i);
-			flag++;
-
-			memset(cmd, 0, 6);
-			cmd[0] = 0xd7;	/* disk release */
-			cmd[1] = i << 5;
-			s = scsiio(w->juke, SCSInone, cmd, 6, buf, 0);
-			if(s)
-				goto bad;
-		}
-	cont1:;
-	}
-	for(i=0; i<w->nse; i++) {
-		p = buf+48+i;
-		if(!(p[0] & 0x80))		/* empty */
-			continue;
-		if(p[0] & 0x40)
-			continue;		/* assigned */
-		print("	shelf %2d unassigned\n", i);
-		flag++;
-
-		memset(cmd, 0, 6);
-		cmd[0] = 0xd6;			/* disk set */
-		cmd[3] = (127<<1);
-		s = scsiio(w->juke, SCSInone, cmd, 6, buf, 0);
-		if(s)
-			goto sbad;
-	
-		memset(cmd, 0, 6);
-		cmd[0] = 0xd7;	/* disk release */
-		cmd[1] = 1;
-		cmd[3] = i << 1;
-		s = scsiio(w->juke, SCSInone, cmd, 6, buf, 0);
-		if(s)
-			goto sbad;
-	}
-	if(flag) {
-		if(pass) {
-			print("	too many passes\n");
-			goto bad;
-		}
-		pass++;
-		goto loop;
-	}
-	if(buf[98])
-		print("	i/o unit status: %.2x\n", buf[98]);
-	if(buf[99])
-		print("	carrier  status: %.2x\n", buf[99]);
-	if(buf[100])
-		print("	upper dr status: %.2x\n", buf[100]);
-	if(buf[101])
-		print("	lower dr status: %.2x\n", buf[101]);
-	for(i=0; i<w->nse; i++) {
-		w->side[i].status = Sempty;
-		if(buf[i+48] & 0x80)
-			w->side[i].status = Sunload;
-		w->side[w->nse+i].status = w->side[i].status;
-	}
-	return;
-
-sbad:
-	wormsense(w, 0);
-bad:
-	panic("cant fix worm shelves");
-}
-
-static
-void
-positions(Juke *w)
-{
-	int i, f;
-
-	/* mark empty shelves */
-	for(i=0; i<w->nse; i++)
-		element(w, w->se0+i);
-	for(i=0; i<w->nmt; i++)
-		element(w, w->mt0+i);
-	for(i=0; i<w->nie; i++)
-		element(w, w->ie0+i);
-	for(i=0; i<w->ndt; i++)
-		element(w, w->dt0+i);
-
-	f = 0;
-	for(i=0; i<w->nse; i++) {
-		if(w->side[i].status == Sempty) {
-			if(f) {
-				print("r%d\n", i-1);
-				f = 0;
-			}
-		} else {
-			if(!f) {
-				print("	shelves r%d-", i);
-				f = 1;
-			}
-		}
-	}
-	if(f)
-		print("r%d\n", i-1);
-	if(EXIT)
-		panic("exit");
-}
-
-static
-void
-jinit(Juke *w, Device *d, int o)
-{
-	int p;
-
-	switch(d->type) {
-	default:
-		print("juke platter not devworm: %Z\n", d);
-		goto bad;
-
-	case Devmcat:
-		for(d=d->cat.first; d; d=d->link)
-			jinit(w, d, o++);
-		break;
-
-	case Devlworm:
-		p = d->wren.targ;
-		if(p < 0 || p >= w->nside)
-			panic("jinit partition %Z\n", d);
-		w->side[p].ord = o;
-
-	case Devworm:
-		if(d->private) {
-			print("juke platter private pointer set %p\n",
-				d->private);
-			goto bad;
-		}
-		d->private = w;
-		break;
-	}
-	return;
-
-bad:
-	panic("jinit");
-}
-
-Side*
-wormi(char *arg)
-{
-	int i, j;
-	Juke *w;
-	Side *v;
-
-	w = jukelist;
-	i = number(arg, -1, 10) - 1;
-	if(i < 0 || i >= w->nside) {
-		print("bad unit number %s (%d)\n", arg, i+1);
-		return 0;
-	}
-	j = i;
-	if(j >= w->nse)
-		j -= w->nse;
-	if(j < w->nside) {
-		v = &w->side[j];
-		qlock(v);
-		if(v->status == Sstart) {
-			mmove(w, w->mt0, w->dt0+v->drive, v->elem, v->rot);
-			v->status = Sunload;
-		}
-		qunlock(v);
-	}
-	j += w->nse;
-	if(j < w->nside) {
-		v = &w->side[j];
-		qlock(v);
-		if(v->status == Sstart) {
-			mmove(w, w->mt0, w->dt0+v->drive, v->elem, v->rot);
-			v->status = Sunload;
-		}
-		qunlock(v);
-	}
-	v = &w->side[i];
-	if(v->status != Sunload) {
-		print("unit not on shelve %s (%d)\n", arg, i+1);
-		return 0;
-	}
-	return v;
-}
-
-static
-void
-cmd_wormeject(int argc, char *argv[])
-{
-	Juke *w;
-	Side *v;
-
-	if(argc <= 1) {
-		print("usage: wormeject unit\n");
-		return;
-	}
-	w = jukelist;
-	v = wormi(argv[1]);
-	if(v == 0)
-		return;
-	qlock(v);
-	mmove(w, w->mt0, v->elem, w->ie0, 0);
-	qunlock(v);
-}
-
-static
-void
-cmd_wormingest(int argc, char *argv[])
-{
-	Juke *w;
-	Side *v;
-
-	w = jukelist;
-	if(argc <= 1) {
-		print("usage: wormingest unit\n");
-		return;
-	}
-	v = wormi(argv[1]);
-	if(v == 0)
-		return;
-	qlock(v);
-	mmove(w, w->mt0, w->ie0, v->elem, 0);
-	qunlock(v);
-}
-
-void
-jukeinit(Device *d)
-{
-	Juke *w;
-	Device *xdev;
-	Side *v;
-	int i;
-
-	/* j(w<changer>w<station0>...)(r<platters>) */
-	xdev = d->j.j;
-	if(xdev->type != Devmcat) {
-		print("juke union not mcat\n");
-		goto bad;
-	}
-
-	/*
-	 * pick up the changer device
-	 */
-	xdev = xdev->cat.first;
-	if(xdev->type != Devwren) {
-		print("juke changer not wren %Z\n", xdev);
-		goto bad;
-	}
-	for(w=jukelist; w; w=w->link)
-		if(xdev == w->juke)
-			goto found;
-
-	/*
-	 * allocate a juke structure
-	 * no locking problems.
-	 */
-	w = ialloc(sizeof(Juke), 0);
-	w->link = jukelist;
-	jukelist = w;
-
-	print("alloc juke %Z\n", xdev);
-	qlock(w);
-	qunlock(w);
-	w->name = "juke";
-	w->juke = xdev;
-	geometry(w);
-
-	/*
-	 * pick up each side
-	 */
-	w->nside = w->nse;
-	if(w->rot)
-		w->nside += w->nside;
-	if(w->nside > MAXSIDE) {
-		print("too many sides: %d max %d\n", w->nside, MAXSIDE);
-		goto bad;
-	}
-	for(i=0; i<w->nse; i++) {
-		v = &w->side[i];
-		qlock(v);
-		qunlock(v);
-		v->name = "shelf";
-		v->elem = w->se0 + i;
-		v->rot = 0;
-		v->status = Sempty;
-		v->time = toytime();
-		if(w->rot) {
-			v += w->nse;
-			qlock(v);
-			qunlock(v);
-			v->name = "shelf";
-			v->elem = w->se0 + i;
-			v->rot = 1;
-			v->status = Sempty;
-			v->time = toytime();
-		}
-	}
-	positions(w);
-
-	w->ndrive = w->ndt;
-	if(w->ndrive > MAXDRIVE) {
-		print("ndrives truncated to %d\n", MAXDRIVE);
-		w->ndrive = MAXDRIVE;
-	}
-
-	/*
-	 * pick up each drive
-	 */
-	for(i=0; i<w->ndrive; i++)
-		w->drive[i] = devnone;
-	w->swab = 0;
-
-	cmd_install("wormeject", "unit -- shelf to outside", cmd_wormeject);
-	cmd_install("wormingest", "unit -- outside to shelf", cmd_wormingest);
-
-found:
-	i = 0;
-	while(xdev = xdev->link) {
-		if(i >= w->ndrive) {
-			print("too many drives %Z\n", xdev);
-			goto bad;
-		}
-		if(xdev->type == Devswab) {
-			xdev = xdev->swab.d;
-			w->swab = 1;
-		}
-		if(xdev->type != Devwren) {
-			print("drive not devwren: %Z\n", xdev);
-			goto bad;
-		}
-		if(w->drive[i]->type != Devnone &&
-		   xdev != w->drive[i]) {
-			print("double init drive %d %Z %Z\n", i, w->drive[i], xdev);
-			goto bad;
-		}
-		w->drive[i++] = xdev;
-	}
-
-	if(i <= 0) {
-		print("no drives\n");
-		goto bad;
-	}
-
-	/*
-	 * put w pointer in each platter
-	 */
-	d->private = w;
-	jinit(w, d->j.m, 0);
-	w->probeok = 1;
-	return;
-
-bad:
-	panic("juke init");
-}
-
-int
-dowcp(void)
-{
-	return 0;
-}
-
-/*
- * called periodically
- */
-void
-wormprobe(void)
-{
-	int i, drive;
-	long t;
-	Side *v;
-	Juke *w;
-
-	t = toytime() - TWORM;
-	for(w=jukelist; w; w=w->link) {
-		if(w->probeok == 0 || !canqlock(w))
-			continue;
-		for(i=0; i<w->nside; i++) {
-			v = &w->side[i];
-			if(!canqlock(v))
-				continue;
-			if(v->status == Sstart && t > v->time) {
-				drive = v->drive;
-				print("	time   r%ld drive %Z\n",
-					v-w->side, w->drive[drive]);
-				mmove(w, w->mt0, w->dt0+w->drive[drive]->wren.lun,
-					v->elem, v->rot);
-				v->status = Sunload;
-			}
-			qunlock(v);
-		}
-		qunlock(w);
-	}
-}

+ 15 - 15
sys/src/fs/dev/wren.c

@@ -4,9 +4,9 @@ typedef	struct	Wren	Wren;
 struct	Wren
 {
 	long	block;			/* size of a block -- from config */
-	long	nblock;			/* number of blocks -- from config */
+	Devsize	nblock;			/* number of blocks -- from config */
 	long	mult;			/* multiplier to get physical blocks */
-	long	max;			/* number of logical blocks */
+	Devsize	max;			/* number of logical blocks */
 };
 
 void
@@ -51,15 +51,15 @@ loop:
 	dr->max =
 		(dr->nblock + 1) / dr->mult;
 	print("	drive %Z:\n", d);
-	print("		%ld blocks at %ld bytes each\n",
-		dr->nblock, dr->block);
-	print("		%ld logical blocks at %d bytes each\n",
-		dr->max, RBUFSIZE);
+	print("		%lld blocks at %ld bytes each\n",
+		(Wideoff)dr->nblock, dr->block);
+	print("		%lld logical blocks at %d bytes each\n",
+		(Wideoff)dr->max, RBUFSIZE);
 	print("		%ld multiplier\n",
 		dr->mult);
 }
 
-long
+Devsize
 wrensize(Device *d)
 {
 	Wren *dr;
@@ -69,19 +69,19 @@ wrensize(Device *d)
 }
 
 int
-wreniocmd(Device *d, int io, long b, void *c)
+wreniocmd(Device *d, int io, Off b, void *c)
 {
-	long l, m;
+	Off l, m;
 	uchar cmd[10];
 	Wren *dr;
 
 	dr = d->private;
 	if(d == 0) {
-		print("wreniocmd: no drive - a=%Z b=%ld\n", d, b);
+		print("wreniocmd: no drive - a=%Z b=%lld\n", d, (Wideoff)b);
 		return 0x40;
 	}
 	if(b >= dr->max) {
-		print("wreniocmd out of range a=%Z b=%ld\n", d, b);
+		print("wreniocmd out of range a=%Z b=%lld\n", d, (Wideoff)b);
 		return 0x40;
 	}
 
@@ -104,13 +104,13 @@ wreniocmd(Device *d, int io, long b, void *c)
 }
 
 int
-wrenread(Device *d, long b, void *c)
+wrenread(Device *d, Off b, void *c)
 {
 	int s;
 
 	s = wreniocmd(d, SCSIread, b, c);
 	if(s) {
-		print("wrenread: %Z(%ld) bad status %.4x\n", d, b, s);
+		print("wrenread: %Z(%lld) bad status %.4x\n", d, (Wideoff)b, s);
 		cons.nwormre++;
 		return 1;
 	}
@@ -118,13 +118,13 @@ wrenread(Device *d, long b, void *c)
 }
 
 int
-wrenwrite(Device *d, long b, void *c)
+wrenwrite(Device *d, Off b, void *c)
 {
 	int s;
 
 	s = wreniocmd(d, SCSIwrite, b, c);
 	if(s) {
-		print("wrenwrite: %Z(%ld) bad status %.4x\n", d, b, s);
+		print("wrenwrite: %Z(%lld) bad status %.4x\n", d, (Wideoff)b, s);
 		cons.nwormwe++;
 		return 1;
 	}

+ 68 - 0
sys/src/fs/doc/changes

@@ -0,0 +1,68 @@
+	changes to Ken's file server to make this 63-bit file server
+
+		Geoff Collyer
+		July—October 2004
+
+note: 2⁶⁳=9,223,372,036,854,775,808 or 8EB (9.2×10ⁱ⁸)
+
+• identified longs that refer to offsets, sizes and block numbers, and
+changed them to type Off (vlong); fixed all print formats to match.
+fixed byte-swapping for the 'x' config to match.
+
+• fixed VLONG 9p1 message packing and unpacking macros to actually
+handle 64-bit offsets and sizes.
+
+• implemented triple-indirect blocks.  affected code in
+	dev/cw.c	port/con.c	port/dentry.c	port/sub.c
+	port/chk.c	port/console.c	port/portdat.h
+
+• Fri Aug  6 16:50:59 PDT 2004
+	; ./sizes
+	Plan 9 v4 63-bit file server
+		sizeof(Dentry) = 124
+		sizeof(Cache)  =  88
+
+• added long(er) file name components (56 bytes), long enough for all but one
+  name in my /.longnames file (68-byte .xml name).
+
+• Fri Aug  6 21:43:41 PDT 2004
+	; ./sizes
+	Plan 9 v4 63-bit file server sizes
+		sizeof(Dentry) = 160
+		sizeof(Cache)  =  88
+
+• touched up lib.h (from libc.h) to bring it up to date with formatting
+  functions, verbs & flags.
+• check now reports stack usage: 320 bytes upon entry to fsck first time,
+  92 bytes of stack per recursion.  given 16000 bytes of stack,
+  that's 170 recursions maximum.
+• booted xtc (terminal) from fs64 (used fs64 as main file system)
+
+note: current file server with triple-indirect blocks at 4k block size
+	has a maximum file size of ~505GB (5.42×10ⁱⁱ).
+	with quadruple-indirect blocks, max would be ~275TB @ 4k block size.
+
+• got igbe fs driver working (a couple small changes)
+• eliminated some gotos (started with 580, down to 454)
+• added quadruple indirect blocks: lets us reach 2⁶⁳ with a 32kB block size
+• got igbe boot driver & pxe booting working
+• on-disk qid paths are now Offs, but 9p1 qids on the wire are still ulongs
+• generalised & parameterised indirect block implementation
+• tested with plain w0 fs, cached fake worm on w0, cw jukebox (hp 160fx)
+• ip directories in fs & fs64 are identical except for whitespace and
+  goto-elimination
+• replaced most of nemo's ide code with newer ide code lifted from 9load,
+  then from cpu kernel (sdata.c & support).  this brings us dma, rwm & lba48,
+  finds ide controllers by itself, even pci ones, & copes with dead drives
+  (i.e., doesn't panic).
+• fixed long-standing bug that caused a 5-second delay before each console
+  prompt on systems without a serial console.
+• further type parameterisation: Userid (short), Timet (long), Devsize (vlong).
+    Comment on v7 kernel portability work, quoting scj & dmr from BSTJ v57
+    #6 part 2., p. 2038: ``The important data types used within the
+    system were identified and specified using typedef: disk offsets,
+    absolute times, internal device names, and the like.  This effort was
+    carried out by K. Thompson.''
+• corrected compat.h dependencies in mkfiles
+• eliminated all warnings
+• implemented truncation via wstat

+ 15 - 0
sys/src/fs/doc/words

@@ -0,0 +1,15 @@
+'emelie' is for any PC with supported hardware excluding the SONY
+jukebox, and will make an object '9emeliefs' and use a 16KB block size.
+
+To spin-off a new version to play with, say 'test':
+
+	cd /sys/src/fs
+	mkdir test
+	cp emelie/9emeliefs.c test/9testfs.c
+	cp emelie/dat.h emelie/fns.h emelie/io.h emelie/mem.h test
+	sed '1s/emelie/test/' <emelie/mkfile >test/mkfile
+
+and hack as appropriate.
+
+The mkfiles aren't quite right yet to make this as automatic as it
+should be.  There are a lot of rough edges.

+ 128 - 0
sys/src/fs/doc/worm.fs

@@ -0,0 +1,128 @@
+# fs: new main file server on 633MHz machine with 4 IDE disks & 4K blocks
+# was called rosewood at first
+config h0
+service fs
+ip0     66.120.90.177
+ipgw0   66.120.90.186
+ipmask0 255.255.255.0
+ipsntp  66.120.90.185
+filsys main c{p(h0)0.25p(h2)0.25}f{p(h0)25.75p(h2)25.75}
+filsys dump o
+filsys other h3
+# later added: filsys spare h1
+# ream other
+# ream main
+# recover main
+allow
+end
+
+h0 20GB "main":  25% cache and 75% fake-worm;
+h1 40GB "spare": holds test venti used for backup
+h2 20GB "main":  (h1.0.0) a mirror of h0.
+h3 40GB "other": (h1.1.0) ``scratch'' space
+
+---
+halted. press a key to reboot.
+
+ether#0: i82557: port 0x9400 irq 10: 00A0C9E02756
+dev A0 port 1F0 config 427A capabilities 2F00 mwdma 0007 udma 103F
+dev A0 port 170 config 427A capabilities 2F00 mwdma 0007 udma 043F
+dev B0 port 170 config 045A capabilities 0F00 mwdma 0007 udma 043F
+found 9PCFSIMR.    attr 0x0 start 0x423 len 391193
+.239705.............................+41712.....+181524=462941
+entry: 0x80100020
+cpu0: 635MHz GenuineIntel PentiumIII/Xeon (cpuid: AX 0x0686 DX 0x383f9ff)
+ether0: i82557: 100Mbps port 0x9400 irq 10: 00a0c9e02756
+
+iobufinit
+        114253 buffers; 14281 hashes
+        mem left = 44040191
+                out of = 528482304
+nvr read
+found plan9.nvr attr 0x0 start 0x18c len 677
+for config mode hit a key within 5 seconds
+
+config: filsys main c{p(h0)0.25p(h2)0.25}f{p(h0)25.75p(h2)25.75}
+config: filsys dump o
+config: filsys other h3
+config: recover main 
+config: ream other
+config: allow
+config: end
+ysinits
+config h0
+        devinit h0
+i hd0: DW CDW02E0-B00HB0F    lba/atapi/drqintr: 1/0/0  C/H/S: 0/0/0  CAP: 39102336
+hd0: LBA 39102336 sectors
+ideinit(device 9ce00948 ctrl 0 targ 0) driveno 0 dp 802eff24
+config block written
+config h0
+        devinit h0
+ideinit(device 9ce00988 ctrl 0 targ 0) driveno 0 dp 802eff24
+service    rosewood
+ipauth  0.0.0.0
+ipsntp  66.120.90.185
+ip0     66.120.90.194
+ipgw0   66.120.90.186
+ipmask0 255.255.255.0
+filsys main c{p(h0)0.25p(h2)0.25}f{p(h0)25.75p(h2)25.75}
+filsys dump o
+filsys other h3
+sysinit: main
+recover: c{p(h0)0.25p(h2)0.25}f{p(h0)25.75p(h2)25.75}
+        devinit {p(h0)0.25p(h2)0.25}
+        devinit p(h0)0.25
+        devinit h0
+ideinit(device 9ce00a68 ctrl 0 targ 0) driveno 0 dp 802eff24
+atasize(9ce00a68):  39102336 -> 4887552
+        devinit p(h2)0.25
+        devinit h2
+i hd2: DW CDW02E0-B00HB0F    lba/atapi/drqintr: 1/0/0  C/H/S: 0/0/0  CAP: 39102336
+hd2: LBA 39102336 sectors
+ideinit(device 9ce00ae8 ctrl 0 targ 2) driveno 2 dp 802f4324
+atasize(9ce00ae8):  39102336 -> 4887552
+        devinit f{p(h0)25.75p(h2)25.75}
+fworm init
+        devinit {p(h0)25.75p(h2)25.75}
+        devinit p(h0)25.75
+        devinit h0
+ideinit(device 9ce00bc8 ctrl 0 targ 0) driveno 0 dp 802eff24
+atasize(9ce00bc8):  39102336 -> 4887552
+        devinit p(h2)25.75
+        devinit h2
+ideinit(device 9ce00c48 ctrl 0 targ 2) driveno 2 dp 802f4324
+atasize(9ce00c48):  39102336 -> 4887552
+dump 2 is good; 5 next
+dump 5 is good; 494408 next
+dump 494408 is good; 495193 next
+dump 495193 is good; 495224 next
+dump 495224 is good; 496007 next
+dump 496007 is good; 496062 next
+dump 496062 is good; 496089 next
+dump 496089 is good; 496096 next
+dump 496096 is good; 496118 next
+dump 496118 is good; 496882 next
+fworm: read 496882
+cache init c{p(h0)0.25p(h2)0.25}f{p(h0)25.75p(h2)25.75}
+done cacheinit
+done recover
+        devinit c{p(h0)0.25p(h2)0.25}f{p(h0)25.75p(h2)25.75}
+sysinit: dump
+        devinit o{p(h0)0.25p(h2)0.25}f{p(h0)25.75p(h2)25.75}
+sysinit: other
+        devream: h3 1
+        devinit h3
+i hd3: AMTXRO4 0K042H        lba/atapi/drqintr: 1/0/0  C/H/S: 0/0/0  CAP: 78198750
+hd3: LBA 78198750 sectors
+ideinit(device 9ce00ca8 ctrl 0 targ 3) driveno 3 dp 802f44f0
+atasize(9ce00ca8):  78198750 -> 9774592
+ether0o: 00a0c9e02756 66.120.90.194
+ether0i: 00a0c9e02756 66.120.90.194
+next dump at Mon Sep 10 05:00:00 2001
+current fs is "main"
+il: allocating il!66.120.90.189!23230
+41 uids read, 21 groups used
+rosewood as of Sun Sep  9 16:27:27 2001
+        last boot Mon Sep 10 00:56:10 2001
+touch superblock 496118
+rosewood: 

+ 81 - 0
sys/src/fs/doc/worm.fs64

@@ -0,0 +1,81 @@
+# 4k blocks
+fs64: printconf
+config w0
+service fs64
+filsys main w0
+ipauth 0.0.0.0
+ipsntp 216.240.55.164
+ip0 216.240.55.167
+ipmask0 255.255.255.224
+end
+fs64: 
+
+# 4k blocks
+Wed Sep  1 17:46:10 PDT 2004
+fs64: printconf
+config w0
+service fs64
+filsys main cp(w0)0.20fp(w0)20.80
+filsys dump o
+ipauth 0.0.0.0
+ipsntp 216.240.55.164
+ip0 216.240.55.167
+ipmask0 255.255.255.224
+end
+fs64: 
+
+# 8k blocks, preparing for worm jukebox
+Thu Sep  2 00:17:19 PDT 2004
+fs64: printconf
+config w0
+service fs64
+filsys main cp(w0)0.20fp(w0)20.80
+filsys dump o
+ipauth 0.0.0.0
+ipsntp 216.240.55.164
+ip0 216.240.55.167
+ipmask0 255.255.255.224
+end
+fs64: 
+
+# 8k blocks with hp 160fx worm jukebox
+# only 1 MO disc inside currently
+Fri Sep  3 23:06:30 PDT 2004
+fs64: printconf
+config w0
+service fs64
+filsys main cp(w0)1.99j(w1.<1-5>.0)(l<0-15>l<16-31>)
+filsys dump o
+ipauth 0.0.0.0
+ipsntp 216.240.55.164
+ip0 216.240.55.167
+ipmask0 255.255.255.224
+end
+
+# add two ide disks: one is the juke's mirror, the other is new other
+Thu Sep 30 00:38:38 PDT 2004
+fs64: printconf
+config w0
+service fs64
+filsys main cp(w0)1.99{h0j(w1.<1-5>.0)(l<0-15>l<16-31>)}
+filsys dump o
+filsys other h2
+ipauth 0.0.0.0
+ipsntp 216.240.55.164
+ip0 216.240.55.167
+ipmask0 255.255.255.224
+end
+
+# now has 8 WO disks and 1 RW disk currently (25 nov 2004),
+# treat 8 WO disks as one set of disks.
+fs64: printconf
+config w0
+service fs64
+filsys main cp(w0)1.99{h0j(w1.<1-5>.0)(l<0-7>l<64-71>l<8-62>l<72-126>)}
+filsys dump o
+filsys other h2
+ipauth 0.0.0.0
+ipsntp 216.240.55.164
+ip0 216.240.55.167
+ipmask0 255.255.255.224
+end

+ 0 - 0
sys/src/fs/worms → sys/src/fs/doc/worms.32-bit


+ 11 - 12
sys/src/fs/emelie/9pcfs.c → sys/src/fs/emelie/9emeliefs.c

@@ -13,10 +13,10 @@
 int FIXEDSIZE = 1;
 
 #ifndef	DATE
-#define	DATE	568011600L+4*3600
+#define	DATE	1094098624L
 #endif
 
-ulong	mktime		= DATE;				/* set by mkfile */
+Timet	mktime		= DATE;				/* set by mkfile */
 Startsb	startsb[] =
 {
 	"main",		2,
@@ -25,24 +25,22 @@ Startsb	startsb[] =
 
 Dos dos;
 
-static
-struct
+static struct
 {
 	char	*name;
-	long	(*read)(int, void*, long);
-	vlong	(*seek)(int, vlong);
-	long	(*write)(int, void*, long);
+	Off	(*read)(int, void*, long);
+	Devsize	(*seek)(int, Devsize);
+	Off	(*write)(int, void*, long);
 	int	(*part)(int, char*);
-} nvrdevs[] =
-{
+} nvrdevs[] = {
 	{ "fd", floppyread, floppyseek, floppywrite, 0, },
 	{ "hd", ataread,   ataseek,   atawrite,   setatapart, },
-	/*
-	{ "sd", scsiread,   scsiseek,   scsiwrite,   setscsipart, },
-	 */
+	/* { "sd", scsiread,   scsiseek,   scsiwrite,   setscsipart, },  */
 	{ 0, },
 };
 
+void apcinit(void);
+
 void
 otherinit(void)
 {
@@ -53,6 +51,7 @@ otherinit(void)
 	printcpufreq();
 	etherinit();
 	scsiinit();
+	apcinit();
 
 	s = spllo();
 	nhd = atainit();

+ 6 - 4
sys/src/fs/emelie/dat.h

@@ -1,9 +1,11 @@
-#define RBUFSIZE	(16*1024)	/* raw buffer size */
 /*
- * verify that the kernel prints this size when you
- * first boot this kernel.
- * #define	DSIZE		157933
+ * The most fundamental constant.
+ * The code will not compile with RBUFSIZE made a variable;
+ * for one thing, RBUFSIZE determines FEPERBUF, which determines
+ * the number of elements in a free-list-block array.
  */
+#define RBUFSIZE	(16*1024)	/* raw buffer size */
+
 #include "../port/portdat.h"
 
 extern	Mach	mach0;

+ 15 - 15
sys/src/fs/emelie/fns.h

@@ -12,9 +12,9 @@ void	etherinit(void);
 void	etherstart(void);
 int	floppyinit(void);
 void	floppyproc(void);
-long	floppyread(int, void*, long);
-vlong	floppyseek(int, vlong);
-long	floppywrite(int, void*, long);
+Off	floppyread(int, void*, long);
+Devsize	floppyseek(int, Devsize);
+Off	floppywrite(int, void*, long);
 void	fpinit(void);
 char*	getconf(char*);
 ulong	getcr0(void);
@@ -23,12 +23,11 @@ ulong	getcr4(void);
 int	getfields(char*, char**, int, char);
 ulong	getstatus(void);
 int	atainit(void);
-long	ataread(int, void*, long);
-vlong	ataseek(int, vlong);
-long	atawrite(int, void*, long);
+Off	ataread(int, void*, long);
+Devsize	ataseek(int, Devsize);
+Off	atawrite(int, void*, long);
 void	i8042a20(void);
 void	i8042reset(void);
-void	idecheck(Device*);
 int	inb(int);
 void	insb(int, void*, int);
 ushort	ins(int);
@@ -55,11 +54,12 @@ void	putcr3(ulong);
 void	putcr4(ulong);
 void	puttr(ulong);
 void	rdmsr(int, vlong*);
-void	rdtsc(uvlong*);
+void	wrmsr(int, vlong);
+void	(*cycles)(uvlong*);
 void	scsiinit(void);
-long	scsiread(int, void*, long);
-long	scsiseek(int, long);
-long	scsiwrite(int, void*, long);
+Off	scsiread(int, void*, long);
+Devsize	scsiseek(int, Devsize);
+Off	scsiwrite(int, void*, long);
 int	setatapart(int, char*);
 int	setscsipart(int, char*);
 void	setvec(int, void (*)(Ureg*, void*), void*);
@@ -70,10 +70,10 @@ int	uartgetc(void);
 void	uartputc(int);
 void	wbflush(void);
 void	cpuid(char*, int*, int*);
+
 #define PADDR(a)	((ulong)(a)&~KZERO)
 
 void	ideinit(Device *d);
-int	ideread(Device *d, long,  void*);
-int idewrite(Device *d, long, void*);
-long	atasize(Device *d);
-void	atainitstub(Device *d);
+Devsize	idesize(Device *d);
+int	ideread(Device *d,  Devsize, void*);
+int	idewrite(Device *d, Devsize, void*);

+ 6 - 2
sys/src/fs/emelie/io.h

@@ -161,9 +161,12 @@ typedef struct Pcidev {
 		int	size;
 	} mem[6];
 
+	uchar	rid;
+	uchar	ccrp;
+	uchar	ccrb;
 	uchar	intl;			/* interrupt line */
-	ushort	ccru;
-
+	ushort	ccru;			/* is uchar in cpu kernel */
+	ulong	pcr;
 
 	Pcidev*	list;
 	Pcidev*	bridge;			/* down a bus */
@@ -181,6 +184,7 @@ extern Pcidev* pcimatch(Pcidev*, int, int);
 extern Pcidev* pcimatchtbdf(int);
 extern void pcireset(void);
 extern void pcisetbme(Pcidev*);
+extern void pciclrbme(Pcidev*);
 
 /*
  *  a parsed plan9.ini line

+ 1 - 1
sys/src/fs/emelie/mem.h

@@ -82,4 +82,4 @@
 
 #define MACHADDR	((ulong)&mach0)		/* hack number 1 */
 
-#define IFLAG		0x200
+#define IFLAG		0x200	/* psw: interrupt enable, to be accurate */

+ 20 - 15
sys/src/fs/emelie/mkfile

@@ -1,4 +1,4 @@
-CONF=pc
+CONF=emelie
 p=9
 
 objtype=386
@@ -7,6 +7,7 @@ objtype=386
 TARG=$p$CONF'fs'
 
 DEV=\
+	apc.$O\
 	cw.$O\
 	fworm.$O\
 	juke.$O\
@@ -51,7 +52,9 @@ PC=\
 	8250.$O\
 	8253.$O\
 	cga.$O\
-	devata.$O\
+	devsd.$O\
+	sdscsi.$O\
+	sdata.$O\
 	dosfs.$O\
 	floppy.$O\
 	kbd.$O\
@@ -65,16 +68,17 @@ PC=\
 	trap.$O\
 
 ETHER=\
-	etherif.$O\
+	compat.$O\
 	ether2114x.$O\
-	etherelnk3.$O\
+	ether8139.$O\
 	ether82557.$O\
-	compat.$O\
-	ethermii.$O\
 	ether83815.$O\
 	etherdp83820.$O\
-	ether8139.$O\
+	etherelnk3.$O\
 	etherga620.$O\
+	etherif.$O\
+	etherigbe.$O\
+	ethermii.$O\
 
 SCSI=\
 	scsi.$O\
@@ -88,7 +92,7 @@ OBJ=\
 	$IP\
 	$ETHER\
 	$SCSI\
-	
+
 HFILES=\
 	../port/all.h\
 	../port/lib.h\
@@ -102,12 +106,13 @@ HFILES=\
 	/$objtype/include/ureg.h\
 
 LIB=\
-	/$objtype/lib/libauthsrv.a\
-	/$objtype/lib/libc.a\
-	/$objtype/lib/libsec.a\
+	-lauthsrv\
+	-lc\
+	-lsec\
 
 # -I../pc & -DFS are for compat.h
-CFLAGS=-FTVw -I. -I../port -I../pc -DFS
+# -DOLD uses 32-bit file offsets instead of 64-bit ones
+CFLAGS=-FTVw -I. -I../port -I../pc -DFS -DOLD
 
 all:V:	$TARG
 
@@ -116,15 +121,15 @@ all:V:	$TARG
 <../dev/mkfile
 <../ip/mkfile
 
-$TARG:	$TARG.c $OBJ $LIB
+$TARG:	$TARG.c $OBJ
 	$CC $CFLAGS -DDATE'='`{date -n} $TARG.c
 	$LD -o $target -l -T0x80100020 $OBJ $TARG.$O $LIB
 	size $target
 
 install:V: $TARG
 	cp $TARG /$objtype/
-	#import lookout / /n/lookout && cp $TARG /n/lookout/$objtype/
-	#import boundary / /n/boundary && cp $TARG /n/boundary/$objtype/
+	import lookout / /n/lookout && cp $TARG /n/lookout/$objtype/
+	import boundary / /n/boundary && cp $TARG /n/boundary/$objtype/
 
 $TARG.$O:	../pc/dosfs.h
 

+ 181 - 0
sys/src/fs/fs/9fsfs.c

@@ -0,0 +1,181 @@
+#include "all.h"
+#include "mem.h"
+#include "io.h"
+#include "ureg.h"
+
+#include "../pc/dosfs.h"
+
+/*
+ * setting this to zero permits the use of discs of different sizes, but
+ * can make jukeinit() quite slow while the robotics work through each disc
+ * twice (once per side).
+ */
+int FIXEDSIZE = 1;
+
+#ifndef	DATE
+#define	DATE	1094098624L
+#endif
+
+Timet	mktime		= DATE;				/* set by mkfile */
+Startsb	startsb[] =
+{
+/*	"main",		2,	*/
+	"main",		810988,	/* discontinuity before sb @ 696262 */
+	0
+};
+
+Dos dos;
+
+static struct
+{
+	char	*name;
+	Off	(*read)(int, void*, long);
+	Devsize	(*seek)(int, Devsize);
+	Off	(*write)(int, void*, long);
+	int	(*part)(int, char*);
+} nvrdevs[] = {
+	{ "fd", floppyread, floppyseek, floppywrite, 0, },
+	{ "hd", ataread,   ataseek,   atawrite,   setatapart, },
+	/* { "sd", scsiread,   scsiseek,   scsiwrite,   setscsipart, },  */
+	{ 0, },
+};
+
+void apcinit(void);
+
+void
+otherinit(void)
+{
+	int dev, i, nfd, nhd, s;
+	char *p, *q, buf[sizeof(nvrfile)+8];
+
+	kbdinit();
+	printcpufreq();
+	etherinit();
+	scsiinit();
+	apcinit();
+
+	s = spllo();
+	nhd = atainit();
+	nfd = floppyinit();
+	dev = 0;
+	if(p = getconf("nvr")){
+		strncpy(buf, p, sizeof(buf)-2);
+		buf[sizeof(buf)-1] = 0;
+		p = strchr(buf, '!');
+		q = strrchr(buf, '!');
+		if(p == 0 || q == 0 || strchr(p+1, '!') != q)
+			panic("malformed nvrfile: %s\n", buf);
+		*p++ = 0;
+		*q++ = 0;
+		dev = strtoul(p, 0, 0);
+		strcpy(nvrfile, q);
+		p = buf;
+	} else
+	if(p = getconf("bootfile")){
+		strncpy(buf, p, sizeof(buf)-2);
+		buf[sizeof(buf)-1] = 0;
+		p = strchr(buf, '!');
+		q = strrchr(buf, '!');
+		if(p == 0 || q == 0 || strchr(p+1, '!') != q)
+			panic("malformed bootfile: %s\n", buf);
+		*p++ = 0;
+		*q = 0;
+		dev = strtoul(p, 0, 0);
+		p = buf;
+	} else
+	if(nfd)
+		p = "fd";
+	else
+	if(nhd)
+		p = "hd";
+	else
+		p = "sd";
+
+	for(i = 0; nvrdevs[i].name; i++){
+		if(strcmp(p, nvrdevs[i].name) == 0){
+			dos.dev = dev;
+			if(nvrdevs[i].part && (*nvrdevs[i].part)(dos.dev, "disk") == 0)
+				break;
+			dos.read = nvrdevs[i].read;
+			dos.seek = nvrdevs[i].seek;
+			dos.write = nvrdevs[i].write;
+			break;
+		}
+	}
+	if(dos.read == 0)
+		panic("no device for nvram\n");
+	if(dosinit(&dos) < 0)
+		panic("can't init dos dosfs on %s\n", p);
+	splx(s);
+}
+
+void
+touser(void)
+{
+	int i;
+
+	settime(rtctime());
+	boottime = time();
+
+	print("sysinit\n");
+	sysinit();
+
+	userinit(floppyproc, 0, "floppyproc");
+	/*
+	 * Ethernet i/o processes
+	 */
+	etherstart();
+
+
+	/*
+	 * read ahead processes
+	 */
+	userinit(rahead, 0, "rah");
+
+	/*
+	 * server processes
+	 */
+	for(i=0; i<conf.nserve; i++)
+		userinit(serve, 0, "srv");
+
+	/*
+	 * worm "dump" copy process
+	 */
+	userinit(wormcopy, 0, "wcp");
+
+	/*
+	 * processes to read the console
+	 */
+	consserve();
+
+	/*
+	 * "sync" copy process
+	 * this doesn't return.
+	 */
+	u->text = "scp";
+	synccopy();
+}
+
+void
+localconfinit(void)
+{
+	/* conf.nfile = 60000; */	/* from emelie */
+	conf.nodump = 0;
+	conf.dumpreread = 1;
+	if (0)
+		conf.idedma = 0; /* for old machines */
+	conf.firstsb = 0;	/* time- & jukebox-dependent optimisation */
+	conf.recovsb = 0;	/* 971531 is 24 june 2003, before w3 died */
+	conf.ripoff = 1;
+	conf.nlgmsg = 1100;	/* @8576 bytes, for packets */
+	conf.nsmmsg = 500;	/* @128 bytes */
+
+	conf.minuteswest = 8*60;
+	conf.dsttime = 1;
+}
+
+int (*fsprotocol[])(Msgbuf*) = {
+	serve9p1,		/* do we still need 9P1? */
+	serve9p2,
+	nil,
+};

+ 35 - 0
sys/src/fs/fs/dat.h

@@ -0,0 +1,35 @@
+/*
+ * The most fundamental constant.
+ * The code will not compile with RBUFSIZE made a variable;
+ * for one thing, RBUFSIZE determines FEPERBUF, which determines
+ * the number of elements in a free-list-block array.
+ */
+#define RBUFSIZE	(4*1024)	/* raw buffer size */
+
+#include "../port/portdat.h"
+
+extern	Mach	mach0;
+
+typedef struct Segdesc	Segdesc;
+struct Segdesc
+{
+	ulong	d0;
+	ulong	d1;
+};
+
+typedef struct Mbank {
+	ulong	base;
+	ulong	limit;
+} Mbank;
+
+#define MAXBANK		8
+
+typedef struct Mconf {
+	Lock;
+	Mbank	bank[MAXBANK];
+	int	nbank;
+	ulong	topofmem;
+} Mconf;
+extern Mconf mconf;
+
+extern char nvrfile[128];

+ 79 - 0
sys/src/fs/fs/fns.h

@@ -0,0 +1,79 @@
+ulong	strtoul(char*, char**, int);
+
+#include "../port/portfns.h"
+
+void	aamloop(int);
+void	cgaputc(int);
+void	cgaputs(char*, int);
+int	cistrcmp(char*, char*);
+int	cistrncmp(char*, char*, int);
+void	(*coherence)(void);
+void	etherinit(void);
+void	etherstart(void);
+int	floppyinit(void);
+void	floppyproc(void);
+Off	floppyread(int, void*, long);
+Devsize	floppyseek(int, Devsize);
+Off	floppywrite(int, void*, long);
+void	fpinit(void);
+char*	getconf(char*);
+ulong	getcr0(void);
+ulong	getcr2(void);
+ulong	getcr4(void);
+int	getfields(char*, char**, int, char);
+ulong	getstatus(void);
+int	atainit(void);
+Off	ataread(int, void*, long);
+Devsize	ataseek(int, Devsize);
+Off	atawrite(int, void*, long);
+void	i8042a20(void);
+void	i8042reset(void);
+int	inb(int);
+void	insb(int, void*, int);
+ushort	ins(int);
+void	inss(int, void*, int);
+ulong	inl(int);
+void	insl(int, void*, int);
+void	kbdinit(void);
+int	kbdintr0(void);
+int	kbdgetc(void);
+long*	mapaddr(ulong);
+void	microdelay(int);
+void	mmuinit(void);
+uchar	nvramread(int);
+void	outb(int, int);
+void	outsb(int, void*, int);
+void	outs(int, ushort);
+void	outss(int, void*, int);
+void	outl(int, ulong);
+void	outsl(int, void*, int);
+void	printcpufreq(void);
+void	putgdt(Segdesc*, int);
+void	putidt(Segdesc*, int);
+void	putcr3(ulong);
+void	putcr4(ulong);
+void	puttr(ulong);
+void	rdmsr(int, vlong*);
+void	wrmsr(int, vlong);
+void	(*cycles)(uvlong*);
+void	scsiinit(void);
+Off	scsiread(int, void*, long);
+Devsize	scsiseek(int, Devsize);
+Off	scsiwrite(int, void*, long);
+int	setatapart(int, char*);
+int	setscsipart(int, char*);
+void	setvec(int, void (*)(Ureg*, void*), void*);
+int	tas(Lock*);
+void	trapinit(void);
+void	uartspecial(int, void (*)(int), int (*)(void), int);
+int	uartgetc(void);
+void	uartputc(int);
+void	wbflush(void);
+void	cpuid(char*, int*, int*);
+
+#define PADDR(a)	((ulong)(a)&~KZERO)
+
+void	ideinit(Device *d);
+Devsize	idesize(Device *d);
+int	ideread(Device *d,  Devsize, void*);
+int	idewrite(Device *d, Devsize, void*);

+ 246 - 0
sys/src/fs/fs/io.h

@@ -0,0 +1,246 @@
+/*
+ *  programmable interrupt vectors (for the 8259's)
+ */
+enum
+{
+	Bptvec=		3,		/* breakpoints */
+	Mathemuvec=	7,		/* math coprocessor emulation interrupt */
+	Mathovervec=	9,		/* math coprocessor overrun interrupt */
+	Matherr1vec=	16,		/* math coprocessor error interrupt */
+	Faultvec=	14,		/* page fault */
+
+	Int0vec=	24,		/* first 8259 */
+	 Clockvec=	Int0vec+0,	/*  clock interrupts */
+	 Kbdvec=	Int0vec+1,	/*  keyboard interrupts */
+	 Uart1vec=	Int0vec+3,	/*  modem line */
+	 Uart0vec=	Int0vec+4,	/*  serial line */
+	 PCMCIAvec=	Int0vec+5,	/*  PCMCIA card change */
+	 Floppyvec=	Int0vec+6,	/*  floppy interrupts */
+	 Parallelvec=	Int0vec+7,	/*  parallel port interrupts */
+	Int1vec=	Int0vec+8,
+	 Ethervec=	Int0vec+10,	/*  ethernet interrupt */
+	 Mousevec=	Int0vec+12,	/*  mouse interrupt */
+	 Matherr2vec=	Int0vec+13,	/*  math coprocessor */
+	 ATA0vec=	Int0vec+14,	/*  hard disk */
+
+	Syscallvec=	64,
+};
+
+/*
+ *  8259 interrupt controllers
+ */
+enum
+{
+	Int0ctl=	0x20,		/* control port (ICW1, OCW2, OCW3) */
+	Int0aux=	0x21,		/* everything else (ICW2, ICW3, ICW4, OCW1) */
+	Int1ctl=	0xA0,		/* control port */
+	Int1aux=	0xA1,		/* everything else (ICW2, ICW3, ICW4, OCW1) */
+
+	Icw1=		0x10,		/* select bit in ctl register */
+	Ocw2=		0x00,
+	Ocw3=		0x08,
+
+	EOI=		0x20,		/* non-specific end of interrupt */
+
+	Elcr1=		0x4D0,		/* Edge/Level Triggered Register */
+	Elcr2=		0x4D1,
+};
+
+extern int	int0mask;		/* interrupts enabled for first 8259 */
+extern int	int1mask;		/* interrupts enabled for second 8259 */
+
+#define NVRAUTHADDR	0
+#define LINESIZE	0
+
+enum {
+	MaxEISA		= 16,
+	EISAconfig	= 0xC80,
+
+	MaxScsi		= 4,
+	NTarget		= 16,
+
+	MaxEther	= 4,
+};
+
+#define DMAOK(x, l)	((ulong)(((ulong)(x))+(l)) < (ulong)(KZERO|16*1024*1024))
+
+enum {
+	BusCBUS		= 0,		/* Corollary CBUS */
+	BusCBUSII,			/* Corollary CBUS II */
+	BusEISA,			/* Extended ISA */
+	BusFUTURE,			/* IEEE Futurebus */
+	BusINTERN,			/* Internal bus */
+	BusISA,				/* Industry Standard Architecture */
+	BusMBI,				/* Multibus I */
+	BusMBII,			/* Multibus II */
+	BusMCA,				/* Micro Channel Architecture */
+	BusMPI,				/* MPI */
+	BusMPSA,			/* MPSA */
+	BusNUBUS,			/* Apple Macintosh NuBus */
+	BusPCI,				/* Peripheral Component Interconnect */
+	BusPCMCIA,			/* PC Memory Card International Association */
+	BusTC,				/* DEC TurboChannel */
+	BusVL,				/* VESA Local bus */
+	BusVME,				/* VMEbus */
+	BusXPRESS,			/* Express System Bus */
+};
+
+#define MKBUS(t,b,d,f)	(((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8))
+#define BUSFNO(tbdf)	(((tbdf)>>8)&0x07)
+#define BUSDNO(tbdf)	(((tbdf)>>11)&0x1F)
+#define BUSBNO(tbdf)	(((tbdf)>>16)&0xFF)
+#define BUSTYPE(tbdf)	((tbdf)>>24)
+#define BUSBDF(tbdf)	((tbdf)&0x00FFFF00)
+#define BUSUNKNOWN	(-1)
+
+/*
+ * PCI support code.
+ */
+enum {					/* type 0 and type 1 pre-defined header */
+	PciVID		= 0x00,		/* vendor ID */
+	PciDID		= 0x02,		/* device ID */
+	PciPCR		= 0x04,		/* command */
+	PciPSR		= 0x06,		/* status */
+	PciRID		= 0x08,		/* revision ID */
+	PciCCRp		= 0x09,		/* programming interface class code */
+	PciCCRu		= 0x0A,		/* sub-class code */
+	PciCCRb		= 0x0B,		/* base class code */
+	PciCLS		= 0x0C,		/* cache line size */
+	PciLTR		= 0x0D,		/* latency timer */
+	PciHDT		= 0x0E,		/* header type */
+	PciBST		= 0x0F,		/* BIST */
+
+	PciBAR0		= 0x10,		/* base address */
+	PciBAR1		= 0x14,
+
+	PciINTL		= 0x3C,		/* interrupt line */
+	PciINTP		= 0x3D,		/* interrupt pin */
+};
+
+enum {					/* type 0 pre-defined header */
+	PciBAR2		= 0x18,
+	PciBAR3		= 0x1C,
+	PciBAR4		= 0x20,
+	PciBAR5		= 0x24,
+	PciCIS		= 0x28,		/* cardbus CIS pointer */
+	PciSVID		= 0x2C,		/* subsystem vendor ID */
+	PciSID		= 0x2E,		/* cardbus CIS pointer */
+	PciEBAR0	= 0x30,		/* expansion ROM base address */
+	PciMGNT		= 0x3E,		/* burst period length */
+	PciMLT		= 0x3F,		/* maximum latency between bursts */
+};
+
+enum {					/* type 1 pre-defined header */
+	PciPBN		= 0x18,		/* primary bus number */
+	PciSBN		= 0x19,		/* secondary bus number */
+	PciUBN		= 0x1A,		/* subordinate bus number */
+	PciSLTR		= 0x1B,		/* secondary latency timer */
+	PciIBR		= 0x1C,		/* I/O base */
+	PciILR		= 0x1D,		/* I/O limit */
+	PciSPSR		= 0x1E,		/* secondary status */
+	PciMBR		= 0x20,		/* memory base */
+	PciMLR		= 0x22,		/* memory limit */
+	PciPMBR		= 0x24,		/* prefetchable memory base */
+	PciPMLR		= 0x26,		/* prefetchable memory limit */
+	PciPUBR		= 0x28,		/* prefetchable base upper 32 bits */
+	PciPULR		= 0x2C,		/* prefetchable limit upper 32 bits */
+	PciIUBR		= 0x30,		/* I/O base upper 16 bits */
+	PciIULR		= 0x32,		/* I/O limit upper 16 bits */
+	PciEBAR1	= 0x28,		/* expansion ROM base address */
+	PciBCR		= 0x3E,		/* bridge control register */
+};
+
+typedef struct Pcidev Pcidev;
+typedef struct Pcidev {
+	int	tbdf;			/* type+bus+device+function */
+	ushort	vid;			/* vendor ID */
+	ushort	did;			/* device ID */
+
+	struct {
+		ulong	bar;		/* base address */
+		int	size;
+	} mem[6];
+
+	uchar	rid;
+	uchar	ccrp;
+	uchar	ccrb;
+	uchar	intl;			/* interrupt line */
+	ushort	ccru;			/* is uchar in cpu kernel */
+	ulong	pcr;
+
+	Pcidev*	list;
+	Pcidev*	bridge;			/* down a bus */
+	Pcidev*	link;			/* next device on this bno */
+} Pcidev;
+
+extern int pcicfgr8(Pcidev*, int);
+extern int pcicfgr16(Pcidev*, int);
+extern int pcicfgr32(Pcidev*, int);
+extern void pcicfgw8(Pcidev*, int, int);
+extern void pcicfgw16(Pcidev*, int, int);
+extern void pcicfgw32(Pcidev*, int, int);
+extern void pcihinv(Pcidev*, ulong);
+extern Pcidev* pcimatch(Pcidev*, int, int);
+extern Pcidev* pcimatchtbdf(int);
+extern void pcireset(void);
+extern void pcisetbme(Pcidev*);
+extern void pciclrbme(Pcidev*);
+
+/*
+ *  a parsed plan9.ini line
+ */
+#define ISAOPTLEN	16
+#define NISAOPT		8
+
+typedef struct ISAConf {
+	char	type[NAMELEN];
+	ulong	port;
+	ulong	irq;
+	ulong	dma;
+	ulong	mem;
+	ulong	size;
+	ulong	freq;
+
+	int	nopt;
+	char	opt[NISAOPT][ISAOPTLEN];
+} ISAConf;
+
+extern int isaconfig(char*, int, ISAConf*);
+
+/*
+ * SCSI support code.
+ */
+enum {
+	STblank		=-6,		/* blank block */
+	STnomem		=-5,		/* buffer allocation failed */
+	STtimeout	=-4,		/* bus timeout */
+	STownid		=-3,		/* playing with myself */
+	STharderr	=-2,		/* controller error of some kind */
+	STinit		=-1,		/* */
+	STok		= 0,		/* good */
+	STcheck		= 0x02,		/* check condition */
+	STcondmet	= 0x04,		/* condition met/good */
+	STbusy		= 0x08,		/* busy */
+	STintok		= 0x10,		/* intermediate/good */
+	STintcondmet	= 0x14,		/* intermediate/condition met/good */
+	STresconf	= 0x18,		/* reservation conflict */
+	STterminated	= 0x22,		/* command terminated */
+	STqfull		= 0x28,		/* queue full */
+};
+
+typedef struct Target {
+	int	ctlrno;
+	int	targetno;
+	uchar*	inquiry;
+	uchar*	sense;
+
+	QLock;
+	char	id[NAMELEN];
+	int	ok;
+
+	char	fflag;
+	Filter	work[3];
+	Filter	rate[3];
+} Target;
+
+typedef int (*Scsiio)(Target*, int, uchar*, int, void*, int*);

+ 1 - 1
sys/src/fs/sony/mem.h → sys/src/fs/fs/mem.h

@@ -82,4 +82,4 @@
 
 #define MACHADDR	((ulong)&mach0)		/* hack number 1 */
 
-#define IFLAG		0x200
+#define IFLAG		0x200	/* psw: interrupt enable, to be accurate */

+ 144 - 0
sys/src/fs/fs/mkfile

@@ -0,0 +1,144 @@
+CONF=fs
+p=9
+
+objtype=386
+</$objtype/mkfile
+
+TARG=$p$CONF'fs'
+
+DEV=\
+	apc.$O\
+	cw.$O\
+	fworm.$O\
+	juke.$O\
+	mworm.$O\
+	wren.$O\
+
+IP=\
+	arp.$O\
+	icmp.$O\
+	il.$O\
+	ip.$O\
+	ipaux.$O\
+	iproute.$O\
+	sntp.$O\
+	udp.$O\
+
+PORT=\
+	9p1.$O\
+	9p1lib.$O\
+	9p2.$O\
+	auth.$O\
+	chk.$O\
+	clock.$O\
+	con.$O\
+	config.$O\
+	console.$O\
+	data.$O\
+	dentry.$O\
+	devcons.$O\
+	fcmd.$O\
+	iobuf.$O\
+	lrand.$O\
+	main.$O\
+	print.$O\
+	proc.$O\
+	sub.$O\
+	time.$O\
+	uidgid.$O\
+
+PC=\
+	l.$O\
+	8250.$O\
+	8253.$O\
+	cga.$O\
+	devsd.$O\
+	sdscsi.$O\
+	sdata.$O\
+	dosfs.$O\
+	floppy.$O\
+	kbd.$O\
+	lock.$O\
+	malloc.$O\
+	mmu.$O\
+	nvr.$O\
+	pc.$O\
+	pci.$O\
+	toy.$O\
+	trap.$O\
+
+ETHER=\
+	compat.$O\
+	ether2114x.$O\
+	ether8139.$O\
+	ether82557.$O\
+	ether83815.$O\
+	etherdp83820.$O\
+	etherelnk3.$O\
+	etherga620.$O\
+	etherif.$O\
+	etherigbe.$O\
+	ethermii.$O\
+
+SCSI=\
+	scsi.$O\
+	scsibuslogic.$O\
+	scsincr53c8xx.$O\
+
+OBJ=\
+	$PC\
+	$PORT\
+	$DEV\
+	$IP\
+	$ETHER\
+	$SCSI\
+
+HFILES=\
+	../port/all.h\
+	../port/lib.h\
+	../port/portdat.h\
+	../port/portfns.h\
+	dat.h\
+	fns.h\
+	io.h\
+	mem.h\
+	/$objtype/include/u.h\
+	/$objtype/include/ureg.h\
+
+LIB=\
+	-lauthsrv\
+	-lc\
+	-lsec\
+
+# -I../pc & -DFS are for compat.h
+# -DOLD uses 32-bit file offsets instead of 64-bit ones
+CFLAGS=-FTVw -I. -I../port -I../pc -DFS -DOLD
+
+all:V:	$TARG
+
+<../pc/mkfile
+<../port/mkfile
+<../dev/mkfile
+<../ip/mkfile
+
+$TARG:	$TARG.c $OBJ
+	$CC $CFLAGS -DDATE'='`{date -n} $TARG.c
+	$LD -o $target -l -T0x80100020 $OBJ $TARG.$O $LIB
+	size $target
+
+install:V: $TARG
+	cp $TARG /$objtype/
+	9fs stand && cp -x $TARG /n/stand/$objtype
+
+$TARG.$O:	../pc/dosfs.h
+
+%.$O:	%.c
+	$CC $CFLAGS $stem.c
+
+%.$O:	%.s
+	$AS $stem.s
+
+%.$O:	$HFILES
+
+clean:V:
+	rm -f *.[$OS] [$OS].out $TARG

+ 179 - 0
sys/src/fs/fs64/9fsfs64.c

@@ -0,0 +1,179 @@
+#include "all.h"
+#include "mem.h"
+#include "io.h"
+#include "ureg.h"
+
+#include "../pc/dosfs.h"
+
+/*
+ * setting this to zero permits the use of discs of different sizes, but
+ * can make jukeinit() quite slow while the robotics work through each disc
+ * twice (once per side).
+ */
+int FIXEDSIZE = 1;
+
+#ifndef	DATE
+#define	DATE	1094098624L
+#endif
+
+Timet	mktime		= DATE;				/* set by mkfile */
+Startsb	startsb[] =
+{
+	"main",		2,	/* */
+/*	"main",		810988,	/* discontinuity before sb @ 696262 */
+	0
+};
+
+Dos dos;
+
+static struct
+{
+	char	*name;
+	Off	(*read)(int, void*, long);
+	Devsize	(*seek)(int, Devsize);
+	Off	(*write)(int, void*, long);
+	int	(*part)(int, char*);
+} nvrdevs[] = {
+	{ "fd", floppyread, floppyseek, floppywrite, 0, },
+	{ "hd", ataread,   ataseek,   atawrite,   setatapart, },
+	/* { "sd", scsiread,   scsiseek,   scsiwrite,   setscsipart, },  */
+	{ 0, },
+};
+
+void apcinit(void);
+
+void
+otherinit(void)
+{
+	int dev, i, nfd, nhd, s;
+	char *p, *q, buf[sizeof(nvrfile)+8];
+
+	kbdinit();
+	printcpufreq();
+	etherinit();
+	scsiinit();
+	apcinit();
+
+	s = spllo();
+	nhd = atainit();
+	nfd = floppyinit();
+	dev = 0;
+	if(p = getconf("nvr")){
+		strncpy(buf, p, sizeof(buf)-2);
+		buf[sizeof(buf)-1] = 0;
+		p = strchr(buf, '!');
+		q = strrchr(buf, '!');
+		if(p == 0 || q == 0 || strchr(p+1, '!') != q)
+			panic("malformed nvrfile: %s\n", buf);
+		*p++ = 0;
+		*q++ = 0;
+		dev = strtoul(p, 0, 0);
+		strcpy(nvrfile, q);
+		p = buf;
+	} else
+	if(p = getconf("bootfile")){
+		strncpy(buf, p, sizeof(buf)-2);
+		buf[sizeof(buf)-1] = 0;
+		p = strchr(buf, '!');
+		q = strrchr(buf, '!');
+		if(p == 0 || q == 0 || strchr(p+1, '!') != q)
+			panic("malformed bootfile: %s\n", buf);
+		*p++ = 0;
+		*q = 0;
+		dev = strtoul(p, 0, 0);
+		p = buf;
+	} else
+	if(nfd)
+		p = "fd";
+	else
+	if(nhd)
+		p = "hd";
+	else
+		p = "sd";
+
+	for(i = 0; nvrdevs[i].name; i++){
+		if(strcmp(p, nvrdevs[i].name) == 0){
+			dos.dev = dev;
+			if(nvrdevs[i].part && (*nvrdevs[i].part)(dos.dev, "disk") == 0)
+				break;
+			dos.read = nvrdevs[i].read;
+			dos.seek = nvrdevs[i].seek;
+			dos.write = nvrdevs[i].write;
+			break;
+		}
+	}
+	if(dos.read == 0)
+		panic("no device for nvram\n");
+	if(dosinit(&dos) < 0)
+		panic("can't init dos dosfs on %s\n", p);
+	splx(s);
+}
+
+void
+touser(void)
+{
+	int i;
+
+	settime(rtctime());
+	boottime = time();
+
+	print("sysinit\n");
+	sysinit();
+
+	userinit(floppyproc, 0, "floppyproc");
+	/*
+	 * Ethernet i/o processes
+	 */
+	etherstart();
+
+
+	/*
+	 * read ahead processes
+	 */
+	userinit(rahead, 0, "rah");
+
+	/*
+	 * server processes
+	 */
+	for(i=0; i<conf.nserve; i++)
+		userinit(serve, 0, "srv");
+
+	/*
+	 * worm "dump" copy process
+	 */
+	userinit(wormcopy, 0, "wcp");
+
+	/*
+	 * processes to read the console
+	 */
+	consserve();
+
+	/*
+	 * "sync" copy process
+	 * this doesn't return.
+	 */
+	u->text = "scp";
+	synccopy();
+}
+
+void
+localconfinit(void)
+{
+	/* conf.nfile = 60000; */	/* from emelie */
+	conf.nodump = 0;
+	conf.dumpreread = 1;
+	conf.firstsb = 0;	/* time- & jukebox-dependent optimisation */
+	conf.recovsb = 0;	/* 971531 is 24 june 2003, before w3 died */
+	conf.ripoff = 1;
+	conf.nlgmsg = 1100;	/* @8576 bytes, for packets */
+	conf.nsmmsg = 500;	/* @128 bytes */
+
+	conf.minuteswest = 8*60;
+	conf.dsttime = 1;
+}
+
+int (*fsprotocol[])(Msgbuf*) = {
+	/* 64-bit file servers can't serve 9P1 correctly: NAMELEN is too big */
+	serve9p2,
+	nil,
+};

+ 35 - 0
sys/src/fs/fs64/dat.h

@@ -0,0 +1,35 @@
+/*
+ * The most fundamental constant.
+ * The code will not compile with RBUFSIZE made a variable;
+ * for one thing, RBUFSIZE determines FEPERBUF, which determines
+ * the number of elements in a free-list-block array.
+ */
+#define RBUFSIZE	(8*1024)	/* raw buffer size */
+
+#include "../port/portdat.h"
+
+extern	Mach	mach0;
+
+typedef struct Segdesc	Segdesc;
+struct Segdesc
+{
+	ulong	d0;
+	ulong	d1;
+};
+
+typedef struct Mbank {
+	ulong	base;
+	ulong	limit;
+} Mbank;
+
+#define MAXBANK		8
+
+typedef struct Mconf {
+	Lock;
+	Mbank	bank[MAXBANK];
+	int	nbank;
+	ulong	topofmem;
+} Mconf;
+extern Mconf mconf;
+
+extern char nvrfile[128];

+ 79 - 0
sys/src/fs/fs64/fns.h

@@ -0,0 +1,79 @@
+ulong	strtoul(char*, char**, int);
+
+#include "../port/portfns.h"
+
+void	aamloop(int);
+void	cgaputc(int);
+void	cgaputs(char*, int);
+int	cistrcmp(char*, char*);
+int	cistrncmp(char*, char*, int);
+void	(*coherence)(void);
+void	etherinit(void);
+void	etherstart(void);
+int	floppyinit(void);
+void	floppyproc(void);
+Off	floppyread(int, void*, long);
+Devsize	floppyseek(int, Devsize);
+Off	floppywrite(int, void*, long);
+void	fpinit(void);
+char*	getconf(char*);
+ulong	getcr0(void);
+ulong	getcr2(void);
+ulong	getcr4(void);
+int	getfields(char*, char**, int, char);
+ulong	getstatus(void);
+int	atainit(void);
+Off	ataread(int, void*, long);
+Devsize	ataseek(int, Devsize);
+Off	atawrite(int, void*, long);
+void	i8042a20(void);
+void	i8042reset(void);
+int	inb(int);
+void	insb(int, void*, int);
+ushort	ins(int);
+void	inss(int, void*, int);
+ulong	inl(int);
+void	insl(int, void*, int);
+void	kbdinit(void);
+int	kbdintr0(void);
+int	kbdgetc(void);
+long*	mapaddr(ulong);
+void	microdelay(int);
+void	mmuinit(void);
+uchar	nvramread(int);
+void	outb(int, int);
+void	outsb(int, void*, int);
+void	outs(int, ushort);
+void	outss(int, void*, int);
+void	outl(int, ulong);
+void	outsl(int, void*, int);
+void	printcpufreq(void);
+void	putgdt(Segdesc*, int);
+void	putidt(Segdesc*, int);
+void	putcr3(ulong);
+void	putcr4(ulong);
+void	puttr(ulong);
+void	rdmsr(int, vlong*);
+void	wrmsr(int, vlong);
+void	(*cycles)(uvlong*);
+void	scsiinit(void);
+Off	scsiread(int, void*, long);
+Devsize	scsiseek(int, Devsize);
+Off	scsiwrite(int, void*, long);
+int	setatapart(int, char*);
+int	setscsipart(int, char*);
+void	setvec(int, void (*)(Ureg*, void*), void*);
+int	tas(Lock*);
+void	trapinit(void);
+void	uartspecial(int, void (*)(int), int (*)(void), int);
+int	uartgetc(void);
+void	uartputc(int);
+void	wbflush(void);
+void	cpuid(char*, int*, int*);
+
+#define PADDR(a)	((ulong)(a)&~KZERO)
+
+void	ideinit(Device *d);
+Devsize	idesize(Device *d);
+int	ideread(Device *d,  Devsize, void*);
+int	idewrite(Device *d, Devsize, void*);

+ 246 - 0
sys/src/fs/fs64/io.h

@@ -0,0 +1,246 @@
+/*
+ *  programmable interrupt vectors (for the 8259's)
+ */
+enum
+{
+	Bptvec=		3,		/* breakpoints */
+	Mathemuvec=	7,		/* math coprocessor emulation interrupt */
+	Mathovervec=	9,		/* math coprocessor overrun interrupt */
+	Matherr1vec=	16,		/* math coprocessor error interrupt */
+	Faultvec=	14,		/* page fault */
+
+	Int0vec=	24,		/* first 8259 */
+	 Clockvec=	Int0vec+0,	/*  clock interrupts */
+	 Kbdvec=	Int0vec+1,	/*  keyboard interrupts */
+	 Uart1vec=	Int0vec+3,	/*  modem line */
+	 Uart0vec=	Int0vec+4,	/*  serial line */
+	 PCMCIAvec=	Int0vec+5,	/*  PCMCIA card change */
+	 Floppyvec=	Int0vec+6,	/*  floppy interrupts */
+	 Parallelvec=	Int0vec+7,	/*  parallel port interrupts */
+	Int1vec=	Int0vec+8,
+	 Ethervec=	Int0vec+10,	/*  ethernet interrupt */
+	 Mousevec=	Int0vec+12,	/*  mouse interrupt */
+	 Matherr2vec=	Int0vec+13,	/*  math coprocessor */
+	 ATA0vec=	Int0vec+14,	/*  hard disk */
+
+	Syscallvec=	64,
+};
+
+/*
+ *  8259 interrupt controllers
+ */
+enum
+{
+	Int0ctl=	0x20,		/* control port (ICW1, OCW2, OCW3) */
+	Int0aux=	0x21,		/* everything else (ICW2, ICW3, ICW4, OCW1) */
+	Int1ctl=	0xA0,		/* control port */
+	Int1aux=	0xA1,		/* everything else (ICW2, ICW3, ICW4, OCW1) */
+
+	Icw1=		0x10,		/* select bit in ctl register */
+	Ocw2=		0x00,
+	Ocw3=		0x08,
+
+	EOI=		0x20,		/* non-specific end of interrupt */
+
+	Elcr1=		0x4D0,		/* Edge/Level Triggered Register */
+	Elcr2=		0x4D1,
+};
+
+extern int	int0mask;		/* interrupts enabled for first 8259 */
+extern int	int1mask;		/* interrupts enabled for second 8259 */
+
+#define NVRAUTHADDR	0
+#define LINESIZE	0
+
+enum {
+	MaxEISA		= 16,
+	EISAconfig	= 0xC80,
+
+	MaxScsi		= 4,
+	NTarget		= 16,
+
+	MaxEther	= 4,
+};
+
+#define DMAOK(x, l)	((ulong)(((ulong)(x))+(l)) < (ulong)(KZERO|16*1024*1024))
+
+enum {
+	BusCBUS		= 0,		/* Corollary CBUS */
+	BusCBUSII,			/* Corollary CBUS II */
+	BusEISA,			/* Extended ISA */
+	BusFUTURE,			/* IEEE Futurebus */
+	BusINTERN,			/* Internal bus */
+	BusISA,				/* Industry Standard Architecture */
+	BusMBI,				/* Multibus I */
+	BusMBII,			/* Multibus II */
+	BusMCA,				/* Micro Channel Architecture */
+	BusMPI,				/* MPI */
+	BusMPSA,			/* MPSA */
+	BusNUBUS,			/* Apple Macintosh NuBus */
+	BusPCI,				/* Peripheral Component Interconnect */
+	BusPCMCIA,			/* PC Memory Card International Association */
+	BusTC,				/* DEC TurboChannel */
+	BusVL,				/* VESA Local bus */
+	BusVME,				/* VMEbus */
+	BusXPRESS,			/* Express System Bus */
+};
+
+#define MKBUS(t,b,d,f)	(((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8))
+#define BUSFNO(tbdf)	(((tbdf)>>8)&0x07)
+#define BUSDNO(tbdf)	(((tbdf)>>11)&0x1F)
+#define BUSBNO(tbdf)	(((tbdf)>>16)&0xFF)
+#define BUSTYPE(tbdf)	((tbdf)>>24)
+#define BUSBDF(tbdf)	((tbdf)&0x00FFFF00)
+#define BUSUNKNOWN	(-1)
+
+/*
+ * PCI support code.
+ */
+enum {					/* type 0 and type 1 pre-defined header */
+	PciVID		= 0x00,		/* vendor ID */
+	PciDID		= 0x02,		/* device ID */
+	PciPCR		= 0x04,		/* command */
+	PciPSR		= 0x06,		/* status */
+	PciRID		= 0x08,		/* revision ID */
+	PciCCRp		= 0x09,		/* programming interface class code */
+	PciCCRu		= 0x0A,		/* sub-class code */
+	PciCCRb		= 0x0B,		/* base class code */
+	PciCLS		= 0x0C,		/* cache line size */
+	PciLTR		= 0x0D,		/* latency timer */
+	PciHDT		= 0x0E,		/* header type */
+	PciBST		= 0x0F,		/* BIST */
+
+	PciBAR0		= 0x10,		/* base address */
+	PciBAR1		= 0x14,
+
+	PciINTL		= 0x3C,		/* interrupt line */
+	PciINTP		= 0x3D,		/* interrupt pin */
+};
+
+enum {					/* type 0 pre-defined header */
+	PciBAR2		= 0x18,
+	PciBAR3		= 0x1C,
+	PciBAR4		= 0x20,
+	PciBAR5		= 0x24,
+	PciCIS		= 0x28,		/* cardbus CIS pointer */
+	PciSVID		= 0x2C,		/* subsystem vendor ID */
+	PciSID		= 0x2E,		/* cardbus CIS pointer */
+	PciEBAR0	= 0x30,		/* expansion ROM base address */
+	PciMGNT		= 0x3E,		/* burst period length */
+	PciMLT		= 0x3F,		/* maximum latency between bursts */
+};
+
+enum {					/* type 1 pre-defined header */
+	PciPBN		= 0x18,		/* primary bus number */
+	PciSBN		= 0x19,		/* secondary bus number */
+	PciUBN		= 0x1A,		/* subordinate bus number */
+	PciSLTR		= 0x1B,		/* secondary latency timer */
+	PciIBR		= 0x1C,		/* I/O base */
+	PciILR		= 0x1D,		/* I/O limit */
+	PciSPSR		= 0x1E,		/* secondary status */
+	PciMBR		= 0x20,		/* memory base */
+	PciMLR		= 0x22,		/* memory limit */
+	PciPMBR		= 0x24,		/* prefetchable memory base */
+	PciPMLR		= 0x26,		/* prefetchable memory limit */
+	PciPUBR		= 0x28,		/* prefetchable base upper 32 bits */
+	PciPULR		= 0x2C,		/* prefetchable limit upper 32 bits */
+	PciIUBR		= 0x30,		/* I/O base upper 16 bits */
+	PciIULR		= 0x32,		/* I/O limit upper 16 bits */
+	PciEBAR1	= 0x28,		/* expansion ROM base address */
+	PciBCR		= 0x3E,		/* bridge control register */
+};
+
+typedef struct Pcidev Pcidev;
+typedef struct Pcidev {
+	int	tbdf;			/* type+bus+device+function */
+	ushort	vid;			/* vendor ID */
+	ushort	did;			/* device ID */
+
+	struct {
+		ulong	bar;		/* base address */
+		int	size;
+	} mem[6];
+
+	uchar	rid;
+	uchar	ccrp;
+	uchar	ccrb;
+	uchar	intl;			/* interrupt line */
+	ushort	ccru;			/* is uchar in cpu kernel */
+	ulong	pcr;
+
+	Pcidev*	list;
+	Pcidev*	bridge;			/* down a bus */
+	Pcidev*	link;			/* next device on this bno */
+} Pcidev;
+
+extern int pcicfgr8(Pcidev*, int);
+extern int pcicfgr16(Pcidev*, int);
+extern int pcicfgr32(Pcidev*, int);
+extern void pcicfgw8(Pcidev*, int, int);
+extern void pcicfgw16(Pcidev*, int, int);
+extern void pcicfgw32(Pcidev*, int, int);
+extern void pcihinv(Pcidev*, ulong);
+extern Pcidev* pcimatch(Pcidev*, int, int);
+extern Pcidev* pcimatchtbdf(int);
+extern void pcireset(void);
+extern void pcisetbme(Pcidev*);
+extern void pciclrbme(Pcidev*);
+
+/*
+ *  a parsed plan9.ini line
+ */
+#define ISAOPTLEN	16
+#define NISAOPT		8
+
+typedef struct ISAConf {
+	char	type[NAMELEN];
+	ulong	port;
+	ulong	irq;
+	ulong	dma;
+	ulong	mem;
+	ulong	size;
+	ulong	freq;
+
+	int	nopt;
+	char	opt[NISAOPT][ISAOPTLEN];
+} ISAConf;
+
+extern int isaconfig(char*, int, ISAConf*);
+
+/*
+ * SCSI support code.
+ */
+enum {
+	STblank		=-6,		/* blank block */
+	STnomem		=-5,		/* buffer allocation failed */
+	STtimeout	=-4,		/* bus timeout */
+	STownid		=-3,		/* playing with myself */
+	STharderr	=-2,		/* controller error of some kind */
+	STinit		=-1,		/* */
+	STok		= 0,		/* good */
+	STcheck		= 0x02,		/* check condition */
+	STcondmet	= 0x04,		/* condition met/good */
+	STbusy		= 0x08,		/* busy */
+	STintok		= 0x10,		/* intermediate/good */
+	STintcondmet	= 0x14,		/* intermediate/condition met/good */
+	STresconf	= 0x18,		/* reservation conflict */
+	STterminated	= 0x22,		/* command terminated */
+	STqfull		= 0x28,		/* queue full */
+};
+
+typedef struct Target {
+	int	ctlrno;
+	int	targetno;
+	uchar*	inquiry;
+	uchar*	sense;
+
+	QLock;
+	char	id[NAMELEN];
+	int	ok;
+
+	char	fflag;
+	Filter	work[3];
+	Filter	rate[3];
+} Target;
+
+typedef int (*Scsiio)(Target*, int, uchar*, int, void*, int*);

+ 1 - 1
sys/src/fs/roro/mem.h → sys/src/fs/fs64/mem.h

@@ -82,4 +82,4 @@
 
 #define MACHADDR	((ulong)&mach0)		/* hack number 1 */
 
-#define IFLAG		0x200
+#define IFLAG		0x200	/* psw: interrupt enable, to be accurate */

+ 143 - 0
sys/src/fs/fs64/mkfile

@@ -0,0 +1,143 @@
+CONF=fs
+p=9
+
+objtype=386
+</$objtype/mkfile
+
+TARG=$p$CONF'fs64'
+
+DEV=\
+	apc.$O\
+	cw.$O\
+	fworm.$O\
+	juke.$O\
+	mworm.$O\
+	wren.$O\
+
+IP=\
+	arp.$O\
+	icmp.$O\
+	il.$O\
+	ip.$O\
+	ipaux.$O\
+	iproute.$O\
+	sntp.$O\
+	udp.$O\
+
+PORT=\
+	9p1.$O\
+	9p1lib.$O\
+	9p2.$O\
+	auth.$O\
+	chk.$O\
+	clock.$O\
+	con.$O\
+	config.$O\
+	console.$O\
+	data.$O\
+	dentry.$O\
+	devcons.$O\
+	fcmd.$O\
+	iobuf.$O\
+	lrand.$O\
+	main.$O\
+	print.$O\
+	proc.$O\
+	sub.$O\
+	time.$O\
+	uidgid.$O\
+
+PC=\
+	l.$O\
+	8250.$O\
+	8253.$O\
+	cga.$O\
+	devsd.$O\
+	sdscsi.$O\
+	sdata.$O\
+	dosfs.$O\
+	floppy.$O\
+	kbd.$O\
+	lock.$O\
+	malloc.$O\
+	mmu.$O\
+	nvr.$O\
+	pc.$O\
+	pci.$O\
+	toy.$O\
+	trap.$O\
+
+ETHER=\
+	compat.$O\
+	ether2114x.$O\
+	ether8139.$O\
+	ether82557.$O\
+	ether83815.$O\
+	etherdp83820.$O\
+	etherelnk3.$O\
+	etherga620.$O\
+	etherif.$O\
+	etherigbe.$O\
+	ethermii.$O\
+
+SCSI=\
+	scsi.$O\
+	scsibuslogic.$O\
+	scsincr53c8xx.$O\
+
+OBJ=\
+	$PC\
+	$PORT\
+	$DEV\
+	$IP\
+	$ETHER\
+	$SCSI\
+
+HFILES=\
+	../port/all.h\
+	../port/lib.h\
+	../port/portdat.h\
+	../port/portfns.h\
+	dat.h\
+	fns.h\
+	io.h\
+	mem.h\
+	/$objtype/include/u.h\
+	/$objtype/include/ureg.h\
+
+LIB=\
+	-lauthsrv\
+	-lc\
+	-lsec\
+
+# -I../pc & -DFS are for compat.h
+CFLAGS=-FTVw -I. -I../port -I../pc -DFS
+
+all:V:	$TARG
+
+<../pc/mkfile
+<../port/mkfile
+<../dev/mkfile
+<../ip/mkfile
+
+$TARG:	$TARG.c $OBJ
+	$CC $CFLAGS -DDATE'='`{date -n} $TARG.c
+	$LD -o $target -l -T0x80100020 $OBJ $TARG.$O $LIB
+	size $target
+
+install:V: $TARG
+	cp $TARG /$objtype/
+	9fs stand && cp -x $TARG /n/stand/$objtype
+
+$TARG.$O:	../pc/dosfs.h
+
+%.$O:	%.c
+	$CC $CFLAGS $stem.c
+
+%.$O:	%.s
+	$AS $stem.s
+
+%.$O:	$HFILES
+
+clean:V:
+	rm -f *.[$OS] [$OS].out $TARG

+ 2 - 2
sys/src/fs/ip/arp.c

@@ -86,7 +86,7 @@ arpreceive(Enpkt *ep, int l, Ifc *ifc)
 	Arpe *a;
 	uchar *tpa;
 	int type, i, h;
-	ulong t;
+	Timet t;
 
 	if(l < Ensize+Arpsize)
 		return;
@@ -208,7 +208,7 @@ ipsend1(Msgbuf *mb, Ifc *ifc, uchar *ipgate)
 	Arppkt *q;
 	Arpe *a;
 	int i, id, len, dlen, off;
-	ulong t;
+	Timet t;
 
 	p = (Ippkt*)mb->data;
 

+ 78 - 79
sys/src/fs/ip/il.c

@@ -26,10 +26,10 @@ enum
 
 	Seconds		= 1000,
 	Iltickms 	= 50,			/* time base */
-	AckDelay	= (ulong)(2*Iltickms),	/* max time twixt message rcvd & ack sent */
-	MaxTimeout 	= (ulong)(4*Seconds),	/* max time between rexmit */
-	QueryTime	= (ulong)(10*Seconds),	/* time between subsequent queries */
-	DeathTime	= (ulong)(30*QueryTime),
+	AckDelay	= (Timet)(2*Iltickms),	/* max time twixt message rcvd & ack sent */
+	MaxTimeout 	= (Timet)(4*Seconds),	/* max time between rexmit */
+	QueryTime	= (Timet)(10*Seconds),	/* time between subsequent queries */
+	DeathTime	= (Timet)(30*QueryTime),
 
 	MaxRexmit 	= 16,		/* max retransmissions before hangup */
 	DefWin		= 20,
@@ -97,7 +97,7 @@ static void
 ilwhoprint(Chan* cp)
 {
 	Ilp *ilp;
-	ulong t;
+	Timet t;
 
 	if(cp->type != Devil)
 		return;
@@ -192,7 +192,7 @@ getchan(Ifc *ifc, Ilpkt *p, Msgbuf *mb)
 		ilp->chan = il.chan;
 		il.chan = cp;
 	}
-	
+
 
 	cp->ifc = ifc;
 	ilp = cp->pdata;
@@ -281,77 +281,76 @@ ilout(void)
 	int dlen;
 	ulong id, ack;
 
-loop:
-	mb = recv(il.reply, 0);
-	if(mb == 0)
-		goto loop;
-
-	cp = mb->chan;
-	ilp = cp->pdata;
+	for (;;) {
+		while ((mb = recv(il.reply, 0)) == nil)
+			continue;
 
-	switch(ilp->state) {
-	case Ilclosed:
-	case Illistening:
-	case Ilclosing:
-		goto err;
-	}
+		cp = mb->chan;
+		ilp = cp->pdata;
 
-	dlen = mb->count;
-	mb->data -= Ensize+Ipsize+Ilsize;	/* make room for header */
-	mb->count += Ensize+Ipsize+Ilsize;
-	if(mb->data < mb->xdata)
-		panic("ilout: no room for header");
-	ih = (Ilpkt*)mb->data;
+		switch(ilp->state) {
+		case Ilclosed:
+		case Illistening:
+		case Ilclosing:
+			print("ilout: error\n");
+			mbfree(mb);
+			continue;
+		}
 
-	/*
-	 * Ip fields
-	 */
-	ifc = cp->ifc;
-	memmove(ih->src, ifc->ipa, Pasize);
-	memmove(ih->dst, ilp->iphis, Pasize);
-	ih->proto = Ilproto;
+		dlen = mb->count;
+		mb->data -= Ensize+Ipsize+Ilsize;    /* make room for header */
+		mb->count += Ensize+Ipsize+Ilsize;
+		if(mb->data < mb->xdata)
+			panic("ilout: no room for header");
+		ih = (Ilpkt*)mb->data;
 
-	/*
-	 * Il fields
-	 */
-	hnputs(ih->illen, Ilsize+dlen);
-	hnputs(ih->ilsrc, ilp->dstp);
-	hnputs(ih->ildst, ilp->srcp);
-	id = ilp->next++;
-	hnputl(ih->ilid, id);
-	ack = ilp->recvd;
-	hnputl(ih->ilack, ack);
-	ilp->acksent = ack;
-	ilp->acktime = msec + AckDelay;
-	ih->iltype = Ildata;
-	ih->ilspec = 0;
-	ih->ilsum[0] = 0;
-	ih->ilsum[1] = 0;
-
-	/*
-	 * checksum
-	 */
-	hnputs(ih->ilsum, ptclcsum((uchar*)ih+(Ensize+Ipsize), dlen+Ilsize));
+		/*
+		 * Ip fields
+		 */
+		ifc = cp->ifc;
+		memmove(ih->src, ifc->ipa, Pasize);
+		memmove(ih->dst, ilp->iphis, Pasize);
+		ih->proto = Ilproto;
 
-	ilackq(cp, mb);
+		/*
+		 * Il fields
+		 */
+		hnputs(ih->illen, Ilsize+dlen);
+		hnputs(ih->ilsrc, ilp->dstp);
+		hnputs(ih->ildst, ilp->srcp);
+		id = ilp->next++;
+		hnputl(ih->ilid, id);
+		ack = ilp->recvd;
+		hnputl(ih->ilack, ack);
+		ilp->acksent = ack;
+		ilp->acktime = msec + AckDelay;
+		ih->iltype = Ildata;
+		ih->ilspec = 0;
+		ih->ilsum[0] = 0;
+		ih->ilsum[1] = 0;
 
-	/* Start the round trip timer for this packet if the timer is free */
-	if(ilp->rttack == 0) {
-		ilp->rttack = id;
-		ilp->rttstart = msec;
-		ilp->rttlen = dlen+Ipsize+Ilsize;
-	}
+		/*
+		 * checksum
+		 */
+		hnputs(ih->ilsum, ptclcsum((uchar*)ih+(Ensize+Ipsize),
+			dlen+Ilsize));
 
-	if(ilp->timeout <= msec)
-		ilsettimeout(ilp);
-	ipsend(mb);
-	goto loop;
+		ilackq(cp, mb);
 
-err:
-	print("ilout: error\n");
-	mbfree(mb);
-	goto loop;
+		/*
+		 * Start the round trip timer for this packet if the timer
+		 * is free.
+		 */
+		if(ilp->rttack == 0) {
+			ilp->rttack = id;
+			ilp->rttstart = msec;
+			ilp->rttlen = dlen+Ipsize+Ilsize;
+		}
 
+		if(ilp->timeout <= msec)
+			ilsettimeout(ilp);
+		ipsend(mb);
+	}
 }
 
 static
@@ -373,7 +372,7 @@ ilackq(Chan *cp, Msgbuf *mb)
 	lock(ilp);
 	if(ilp->unacked)
 		ilp->unackedtail->next = nmb;
-	else 
+	else
 		ilp->unacked = nmb;
 	ilp->unackedtail = nmb;
 	ilp->unackedbytes += nmb->count;
@@ -797,7 +796,7 @@ ilrexmit(Ilp *ilp)
 	mb = mballoc(omb->count, omb->chan, Mbil4);
 	memmove(mb->data, omb->data, omb->count);
 	unlock(ilp);
-	
+
 	h = (Ilpkt*)mb->data;
 
 	h->iltype = Ildataquery;
@@ -839,7 +838,7 @@ static
 void
 ilsettimeout(Ilp *ilp)
 {
-	ulong pt;
+	Timet pt;
 
 	pt = (ilp->delay>>LogAGain)
 		+ ilp->unackedbytes/(ilp->rate>>LogAGain)
@@ -854,7 +853,7 @@ static
 void
 ilbackoff(Ilp *ilp)
 {
-	ulong pt;
+	Timet pt;
 	int i;
 
 	pt = (ilp->delay>>LogAGain)
@@ -880,26 +879,26 @@ static
 void
 callil(Alarm *a, void *)
 {
-
 	cancel(a);
 	wakeup(&ilt);
 }
 
 // complain if two numbers not within an hour of each other
 #define Tfuture (1000*60*60)
+
 int
-later(ulong t1, ulong t2, char *x)
+later(Timet t1, Timet t2, char *x)
 {
-	int dt;
+	Timet dt;
 
 	dt = t1 - t2;
 	if(dt > 0) {
 		if(dt > Tfuture)
-			print("%s: way future %d\n", x, dt);
+			print("%s: way future %ld\n", x, dt);
 		return 1;
 	}
 	if(dt < -Tfuture) {
-		print("%s: way past %d\n", x, -dt);
+		print("%s: way past %ld\n", x, -dt);
 		return 1;
 	}
 	return 0;
@@ -927,7 +926,7 @@ loop:
 
 		case Ilclosing:
 			if(later(msec, ilp->timeout, "timeout")){
-				if(ilp->rexmit > MaxRexmit){ 
+				if(ilp->rexmit > MaxRexmit){
 					ilp->state = Ilclosed;
 					ilhangup(cp, "connection timed out-0", 0);
 					break;
@@ -940,7 +939,7 @@ loop:
 		case Ilsyncee:
 		case Ilsyncer:
 			if(later(msec, ilp->timeout, "timeout")){
-				if(ilp->rexmit > MaxRexmit){ 
+				if(ilp->rexmit > MaxRexmit){
 					ilp->state = Ilclosed;
 					ilhangup(cp, "connection timed out-1", 0);
 					break;

+ 2 - 2
sys/src/fs/ip/ip.c

@@ -19,7 +19,7 @@ struct	Rock
 	uchar	dst[Pasize];
 	int	id;		/* src,dst,id are address of the rock */
 	Msgbuf*	mb;		/* reassembly. if 0, the rock is empty */
-	ulong	age;		/* timeout to throw away */
+	Timet	age;		/* timeout to throw away */
 	int	last;		/* set to data size when last frag arrives */
 	int	nfrag;
 	Frag	frag[Nfrag];
@@ -41,7 +41,7 @@ ipreceive(Enpkt *ep, int l, Ifc *ifc)
 	Frag *f;
 	int len, id, frag, off, loff, i, n;
 	Ippkt pkt;
-	ulong t;
+	Timet t;
 
 	p = (Ippkt*)ep;
 	if(l < Ensize+Ipsize) {

+ 5 - 5
sys/src/fs/ip/ip.h

@@ -43,11 +43,11 @@ struct	Ilp
 	ulong	rstart;		/* remote start id */
 	ulong	acksent;	/* Last packet acked */
 
-	ulong	lastxmit;	/* time of last xmit */
-	ulong	lastrecv;	/* time of last recv */
-	ulong	timeout;	/* time out counter */
-	ulong	acktime;	/* acknowledge timer */
-	ulong	querytime;	/* Query timer */
+	Timet	lastxmit;	/* time of last xmit */
+	Timet	lastrecv;	/* time of last recv */
+	Timet	timeout;	/* time out counter */
+	Timet	acktime;	/* acknowledge timer */
+	Timet	querytime;	/* Query timer */
 
 	ulong	delay;		/* Average of the fixed rtt delay */
 	ulong	rate;		/* Average byte rate */

+ 3 - 7
sys/src/fs/ip/iproute.c

@@ -73,7 +73,7 @@ struct Route
 	uchar	gate[Pasize];
 	int	metric;
 	int	inuse;
-	long	time;
+	Timet	time;
 };
 struct
 {
@@ -147,18 +147,14 @@ usage:
 				goto usage;
 			break;
 		}
-		if(chartoip(r.dest, argv[2]))
-			goto usage;
-		if(chartoip(r.gate, argv[3]))
+		if(chartoip(r.dest, argv[2]) || chartoip(r.gate, argv[3]))
 			goto usage;
 		r.metric = 0;			/* rip can't nuke these */
 		deleteroute(&r);
 		considerroute(&r);
 	} else
 	if(strcmp(argv[1], "delete") == 0) {
-		if(argc != 3)
-			goto usage;
-		if(chartoip(r.dest, argv[2]))
+		if(argc != 3 || chartoip(r.dest, argv[2]))
 			goto usage;
 		deleteroute(&r);
 	} else

+ 4 - 1
sys/src/fs/mkfile

@@ -1,7 +1,10 @@
 ARCH=\
+	fs\
+	fs64\
+	9netics32.16k\
+	9netics64.8k\
 	choline\
 	emelie\
-	roro\
 
 all:V:
 	for(i in $ARCH)@{

+ 17 - 9
sys/src/fs/pc/8250.c

@@ -3,6 +3,12 @@
 #include "ureg.h"
 #include "io.h"
 
+enum {
+	Development = 1,		/* i.e., debugging */
+	DLE = 0x10,			/* ^p == DLE */
+	Asciimask = 0x7f,
+};
+
 /*
  *  INS8250 uart
  */
@@ -74,6 +80,9 @@ struct Uart
 	ulong	overrun;
 };
 
+/* externally-visible console-on-a-uart flag */
+int	uartcons;
+
 Uart	uart[2];
 
 #define UartFREQ 1843200
@@ -146,7 +155,7 @@ uartfifo(Uart *up, int n)
 		uartrdreg(up, Istat);
 		uartrdreg(up, Data);
 	}
-  
+
 	/* turn on fifo */
 	if(n){
 		uartwrreg(up, Fifoctl, Fena|Ftrig);
@@ -180,18 +189,16 @@ uartintr(Ureg *ur, void *arg)
 			if(l & Oerror)
 				up->overrun++;
 			break;
-	
+
 		case 4:	/* received data available */
 		case 12:
 			ch = inb(up->port+Data);
-#ifndef nohacks
-			if((ch & 0x7F) == 0x10)
+			if (Development && (ch & Asciimask) == DLE)
 				firmware();
-#endif /* nohacks */
 			if(up->rx)
-				(*up->rx)(ch & 0x7F);
+				(*up->rx)(ch & Asciimask);
 			break;
-	
+
 		case 2:	/* transmitter empty */
 			ch = -1;
 			if(up->tx)
@@ -199,11 +206,11 @@ uartintr(Ureg *ur, void *arg)
 			if(ch != -1)
 				outb(up->port+Data, ch);
 			break;
-	
+
 		case 0:	/* modem status */
 			uartrdreg(up, Mstat);
 			break;
-	
+
 		default:
 			if(s&1)
 				return;
@@ -280,6 +287,7 @@ uartspecial(int port, void (*rx)(int), int (*tx)(void), int baud)
 	uartenable(up);
 	if(baud)
 		uartsetbaud(up, baud);
+	uartcons = 1;
 }
 
 int

+ 66 - 18
sys/src/fs/pc/8253.c

@@ -24,12 +24,13 @@ enum
 	Freq	= 1193182,	/* Real clock frequency */
 };
 
-static int cpufreq	= 66000000;
-static int cpuhz;
+static uvlong cpufreq	= 66000000;
+static uvlong cpuhz;
 static int cpumhz	= 66;
 static int loopconst	= 100;
 /*static*/ int cpuidax, cpuiddx;
 static char cpuidid[16];
+static int havetsc;
 
 static void
 clockintr(Ureg *ur, void *v)
@@ -81,13 +82,17 @@ static X86type x86intel[] =
 	{ 6,	6,	16,	"Celeron", },
 	{ 6,	7,	16,	"PentiumIII/Xeon", },
 	{ 6,	8,	16,	"PentiumIII/Xeon", },
+	{ 6,	0xB,	16,	"PentiumIII/Xeon", },
+	{ 0xF,	1,	16,	"P4", },	/* P4 */
+	{ 0xF,	2,	16,	"PentiumIV/Xeon", },
 
 	{ 3,	-1,	32,	"386", },	/* family defaults */
 	{ 4,	-1,	22,	"486", },
 	{ 5,	-1,	23,	"P5", },
 	{ 6,	-1,	16,	"P6", },
+	{ 0xF,	-1,	16,	"P4", },	/* P4 */
 
-	{ -1,	-1,	23,	"unknown", },	/* total default */
+	{ -1,	-1,	16,	"unknown", },	/* total default */
 };
 
 /*
@@ -117,12 +122,42 @@ static X86type x86amd[] =
 	{ 4,	-1,	22,	"Am486", },	/* guesswork */
 	{ 5,	-1,	23,	"AMD-K5/K6", },	/* guesswork */
 	{ 6,	-1,	11,	"AMD-Athlon", },/* guesswork */
+	{ 0xF,	-1,	11,	"AMD64", },	/* guesswork */
+
+	{ -1,	-1,	23,	"unknown", },	/* total default */
+};
 
+/*
+ * WinChip 240MHz
+ */
+static X86type x86winchip[] =
+{
+	{5,	4,	23,	"Winchip",},	/* guesswork */
+	{6,	7,	23,	"Via C3 Samuel 2 or Ezra",},
+	{6,	8,	23,	"Via C3 Ezra-T",},
+	{ -1,	-1,	23,	"unknown", },	/* total default */
+};
+
+/*
+ * SiS 55x
+ */
+static X86type x86sis[] =
+{
+	{5,	0,	23,	"SiS 55x",},	/* guesswork */
 	{ -1,	-1,	23,	"unknown", },	/* total default */
 };
 
 static X86type	*cputype;
 
+static void
+simplecycles(uvlong*x)
+{
+	*x = m->ticks;
+}
+
+void	(*cycles)(uvlong*) = simplecycles;
+void	_cycles(uvlong*);	/* in l.s */
+
 static void
 nop(void)
 {
@@ -174,7 +209,7 @@ void
 clockinit(void)
 {
 	int x, y;	/* change in counter */
-	int family, model, loops, incr, havecycleclock;
+	int family, model, loops, incr;
 	X86type *t;
 	uvlong a, b;
 
@@ -189,6 +224,10 @@ clockinit(void)
 	cpuid(cpuidid, &cpuidax, &cpuiddx);
 	if(strncmp(cpuidid, "AuthenticAMD", 12) == 0)
 		t = x86amd;
+	else if(strncmp(cpuidid, "CentaurHauls", 12) == 0)
+		t = x86winchip;
+	else if(strncmp(cpuidid, "SiS SiS SiS ", 12) == 0)
+		t = x86sis;
 	else
 		t = x86intel;
 	family = FAMILY(cpuidax);
@@ -202,12 +241,18 @@ clockinit(void)
 	}
 	cputype = t;
 
-	if(family >= 5){
-		havecycleclock = 1;
+	if(family >= 5)
 		coherence = wbflush;
+
+	/*
+	 *  if there is one, set tsc to a known value
+	 */
+	if(cpuiddx & 0x10){
+		havetsc = 1;
+		cycles = _cycles;
+		if(cpuiddx & 0x20)
+			wrmsr(0x10, 0);
 	}
-	else
-		havecycleclock = 0;
 
 	/*
 	 *  set clock for 1/HZ seconds
@@ -235,7 +280,7 @@ clockinit(void)
 	incr = 16000000/(t->aalcycles*HZ*2);
 	x = 2000;
 	for(loops = incr; loops < 64*1024; loops += incr) {
-	
+
 		/*
 		 *  measure time for the loop
 		 *
@@ -249,18 +294,16 @@ clockinit(void)
 		 *
 		 */
 		outb(Tmode, Latch0);
-		if(havecycleclock)
-			rdtsc(&a);
+		cycles(&a);
 		x = inb(T0cntr);
 		x |= inb(T0cntr)<<8;
 		aamloop(loops);
 		outb(Tmode, Latch0);
-		if(havecycleclock)
-			rdtsc(&b);
+		cycles(&b);
 		y = inb(T0cntr);
 		y |= inb(T0cntr)<<8;
 		x -= y;
-	
+
 		if(x < 0)
 			x += Freq/HZ;
 
@@ -272,11 +315,12 @@ clockinit(void)
  	 *  figure out clock frequency and a loop multiplier for delay().
 	 *  n.b. counter goes up by 2*Freq
 	 */
-	cpufreq = loops*((t->aalcycles*2*Freq)/x);
+	cpufreq = (vlong)loops*((t->aalcycles*2*Freq)/x);
 	loopconst = (cpufreq/1000)/t->aalcycles;	/* AAM+LOOP's for 1 ms */
+	if (0)
+		print("loops %d x %d cpufreq %,lld loopconst %d\n", loops, x, cpufreq, loopconst);
 
-	if(havecycleclock){
-
+	if(havetsc){
 		/* counter goes up by 2*Freq */
 		b = (b-a)<<1;
 		b *= Freq;
@@ -294,10 +338,14 @@ clockinit(void)
 		cpumhz = (cpufreq + cpufreq/200)/1000000;
 		cpuhz = cpufreq;
 	}
+	if (0) {
+		print("cpuhz %,lld cpumhz %d\n", cpuhz, cpumhz);
+		delay(10*1000);
+	}
 }
 
 void
-clockreload(ulong n)
+clockreload(Timet n)
 {
 	USED(n);
 }

+ 63 - 0
sys/src/fs/pc/compat.c

@@ -4,8 +4,14 @@
 #include "all.h"
 #include "io.h"
 #include "mem.h"
+#include "../ip/ip.h"	/* for Ether */
+#include "etherif.h"	/* for Ether */
 #include "compat.h"
 
+enum {
+	VectorPIC	= 24,		/* external [A]PIC interrupts */
+};
+
 void
 free(void *p)		/* there's a struct member named "free".  sigh. */
 {
@@ -42,3 +48,60 @@ freeblist(Block *b)
 		freeb(b);
 	}
 }
+
+int
+readstr(vlong, void *, int, char *)
+{
+	return 0;
+}
+
+void
+addethercard(char *, int (*)(struct Ether *))
+{
+}
+
+void
+kproc(char *name, void (*f)(void), void *arg)
+{
+	userinit(f, arg, strdup(name));
+}
+
+void
+intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
+{
+	setvec(irq+VectorPIC, f, a);
+	USED(tbdf, name);
+}
+
+int
+intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name)
+{
+	USED(irq, f, a, tbdf, name);
+	return -1;
+}
+
+/*
+ * Atomically replace *p with copy of s
+ */
+void
+kstrdup(char **p, char *s)
+{
+	int n;
+	char *t, *prev;
+	static Lock l;
+
+	n = strlen(s)+1;
+	/* if it's a user, we can wait for memory; if not, something's very wrong */
+	if(0 && u){
+		t = /* s */malloc(n);
+		// setmalloctag(t, getcallerpc(&p));
+	}else{
+		t = malloc(n);
+		if(t == nil)
+			panic("kstrdup: no memory");
+	}
+	memmove(t, s, n);
+	prev = *p;
+	*p = t;
+	free(prev);
+}

+ 32 - 1
sys/src/fs/pc/compat.h

@@ -16,20 +16,37 @@
 #define INCRPTR(bp, incr)	(bp)->count += (incr)
 #define ENDDATA(bp)		((bp)->data + (bp)->count)
 
+#define	ROUND(s, sz)	(((s)+((sz)-1))&~((sz)-1))
+
 #define Block	Msgbuf
 #define rp	data			/* Block member → Msgbuf member */
 #define Etherpkt Enpkt
 #define Eaddrlen Easize
+#define ETHERHDRSIZE Ensize
+
+#ifndef CACHELINESZ
+#define CACHELINESZ	32		/* pentium & later */
+#endif
+
+#define KNAMELEN NAMELEN
+#define READSTR 128
 
 #define KADDR(a)	((void*)((ulong)(a)|KZERO))
 #define PCIWINDOW	0
 #define PCIWADDR(va)	(PADDR(va)+PCIWINDOW)
 
 #define iprint print
+
+/* buffers */
 #define allocb(sz)  mballoc((sz), 0, Maeth1)
 #define iallocb(sz) mballoc((sz), 0, Mbeth1)
+
+/* other memory */
 #define malloc(sz)  ialloc((sz), 0)
 #define xspanalloc(sz, align, span) ialloc((sz)+(align)+(span), (align))
+/* sleazy hacks; really need better allocators */
+#define xalloc(sz) malloc(sz)
+#define xfree(p)
 
 #define waserror() 0
 #define poperror()
@@ -38,14 +55,28 @@
 
 #define qsetlimit(q, lim)
 #define ioalloc(a, b, c, d)	0
+#define iofree(p)
 #define strtol strtoul
-#define kproc(name, f, arg)	userinit(f, arg, name)
+#define PROCARG(arg)
+#define GETARG(arg) getarg()
+
+#define vmap(bar, size) upamalloc(bar, size, 0)
 
 /* see portdat.h for Msgbuf flags */
 void	freeb(Block *b);
 void	freeblist(Block *b);
 void	free(void *p);
 void	*mallocz(ulong sz, int clr);
+char	*strdup(char *);			/* port/config.c */
+void	kstrdup(char **p, char *s);
 
 /* header files mysteriously fail to declare this */
 ulong	upamalloc(ulong addr, int size, int align);
+
+int	readstr(vlong, void *, int, char *);
+void	addethercard(char *, int (*)(struct Ether *));
+void	kproc(char *text, void (*f)(void), void *arg);
+
+/* pc-specific? */
+int	intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int, char *);
+void	intrenable(int irq, void (*f)(Ureg*, void*), void* a, int, char *name);

+ 0 - 1289
sys/src/fs/pc/devata.c

@@ -1,1289 +0,0 @@
-#include "all.h"
-#include "io.h"
-#include "mem.h"
-
-enum {
-	DEBUG =  0,
-	IDEBUG = 0,
-	Sectsize = 512,
-};
-#define DPRINT	if(DEBUG)print
-#define IDPRINT if(IDEBUG)print
-
-typedef	struct Drive		Drive;
-typedef	struct Ident		Ident;
-typedef	struct Controller	Controller;
-
-enum
-{
-	/* ports */
-	Pbase0=		0x1F0,	/* primary */
-	Pbase1=		0x170,	/* secondary */
-	Pbase2=		0x1E8,	/* tertiary */
-	Pbase3=		0x168,	/* quaternary */
-	Pdata=		0,	/* data port (16 bits) */
-	Perror=		1,	/* error port (read) */
-	 Eabort=	(1<<2),
-	Pfeature=	1,	/* buffer mode port (write) */
-	Pcount=		2,	/* sector count port */
-	Psector=	3,	/* sector number port */
-	Pcyllsb=	4,	/* least significant byte cylinder # */
-	Pcylmsb=	5,	/* most significant byte cylinder # */
-	Pdh=		6,	/* drive/head port */
-	 DHmagic=	0xA0,
-	 DHslave=	0x10,
-	Pstatus=	7,	/* status port (read) */
-	 Sbusy=		 (1<<7),
-	 Sready=	 (1<<6),
-	 Sdrq=		 (1<<3),
-	 Serr=		 (1<<0),
-	Pcmd=		7,	/* cmd port (write) */
-
-	/* commands */
-	Crecal=		0x10,
-	Cread=		0x20,
-	Cwrite=		0x30,
-	Cident=		0xEC,
-	Cident2=	0xFF,	/* pseudo command for post Cident interrupt */
-	Csetbuf=	0xEF,
-
-	Cpktcmd=	0xA0,
-	Cidentd=	0xA1,
-	Ctur=		0x00,
-	Creqsense=	0x03,
-	Ccapacity=	0x25,
-	Cread2=		0x28,
-	Cwrite2=	0x2A,
-
-	ATAtimo=	6000,		/* ms to wait for things to complete */
-	ATAPItimo=	10000,
-
-	NCtlr=		4,
-	NDrive=		NCtlr*2,
-
-	Maxloop=	1000000,
-};
-
-/*
- *  ident sector from drive.  this is from ANSI X3.221-1994
- */
-struct Ident
-{
-	ushort	config;		/* general configuration info */
-	ushort	cyls;		/* # of cylinders (default) */
-	ushort	reserved0;
-	ushort	heads;		/* # of heads (default) */
-	ushort	b2t;		/* unformatted bytes/track */
-	ushort	b2s;		/* unformated bytes/sector */
-	ushort	s2t;		/* sectors/track (default) */
-	ushort	reserved1[3];
-/* 10 */
-	ushort	serial[10];	/* serial number */
-	ushort	type;		/* buffer type */
-	ushort	bsize;		/* buffer size/Sectsize */
-	ushort	ecc;		/* ecc bytes returned by read long */
-	ushort	firm[4];	/* firmware revision */
-	ushort	model[20];	/* model number */
-/* 47 */
-	ushort	s2i;		/* number of sectors/interrupt */
-	ushort	dwtf;		/* double word transfer flag */
-	ushort	capabilities;
-	ushort	reserved2;
-	ushort	piomode;
-	ushort	dmamode;
-	ushort	cvalid;		/* (cvald&1) if next 4 words are valid */
-	ushort	ccyls;		/* current # cylinders */
-	ushort	cheads;		/* current # heads */
-	ushort	cs2t;		/* current sectors/track */
-	ushort	ccap[2];	/* current capacity in sectors */
-	ushort	cs2i;		/* current number of sectors/interrupt */
-/* 60 */
-	ushort	lbasecs[2];	/* # LBA user addressable sectors */
-	ushort	dmasingle;
-	ushort	dmadouble;
-/* 64 */
-	ushort	reserved3[64];
-	ushort	vendor[32];	/* vendor specific */
-	ushort	reserved4[96];
-};
-
-enum {
-	Maxxfer		= 16*1024,	/* maximum transfer size/cmd */
-	Npart		= 8+2,		/* 8 sub partitions, disk, and partition */
-};
-
-typedef struct {
-	ulong	start;
-	ulong	end;
-	char	name[NAMELEN+1];
-} Partition;
-
-typedef struct {
-	int	online;
-	int	npart;		/* number of real partitions */
-	Partition p[Npart];
-	uvlong	offset;
-	Partition *current;	/* current partition */
-
-	ulong	cap;		/* total sectors */
-	int	bytes;		/* bytes/sector */
-	int	sectors;	/* sectors/track */
-	int	heads;		/* heads/cyl */
-	long	cyl;		/* cylinders/drive */
-
-	char	lba;		/* true if drive has logical block addressing */
-	char	multi;		/* non-zero if drive does multiple block xfers */
-} Disc;
-
-/*
- *  an ata drive
- */
-struct Drive
-{
-	Controller *cp;
-	uchar	driveno;
-	uchar	dh;
-	uchar	atapi;		/* ATAPI */
-	uchar	drqintr;	/* ATAPI */
-	ulong	vers;		/* ATAPI */
-
-	int	partok;
-
-	Disc;
-};
-
-/*
- *  a controller for 2 drives
- */
-struct Controller
-{
-	QLock;
-	int	pbase;		/* base port */
-	uchar	ctlrno;
-
-	/*
-	 *  current operation
-	 */
-	int	cmd;		/* current command */
-	uchar	cmdblk[12];	/* ATAPI */
-	int	len;		/* ATAPI */
-	int	count;		/* ATAPI */
-	uchar	lastcmd;	/* debugging info */
-	uchar	*buf;		/* xfer buffer */
-	int	tcyl;		/* target cylinder */
-	int	thead;		/* target head */
-	int	tsec;		/* target sector */
-	int	tbyte;		/* target byte */
-	int	nsecs;		/* length of transfer (sectors) */
-	int	sofar;		/* sectors transferred so far */
-	int	status;
-	int	error;
-	Drive	*dp;		/* drive being accessed */
-};
-
-static int atactlrmask;
-static Controller *atactlr[NCtlr];
-static int atadrivemask;
-static Drive *atadrive[NDrive];
-static int pbase[NCtlr] = {
-	Pbase0, Pbase1, Pbase2, Pbase3,
-};
-static int defirq[NCtlr] = {
-	14, 15, 0, 0,
-};
-
-static void	ataintr(Ureg*, void*);
-static long	ataxfer(Drive*, Partition*, int, uvlong, long);
-static int	ataident(Drive*);
-static Drive*	atapart(Drive*);
-static int	ataparams(Drive*);
-static void	atarecal(Drive*);
-static int	ataprobe(Drive*, int, int, int);
-static Drive*	atapipart(Drive*);
-static void	atapiintr(Controller*);
-static long	atapiio(Drive*, long);
-
-static void
-atactlrprobe(int ctlrno, int irq)
-{
-	Controller *ctlr;
-	Drive *drive;
-	int driveno, port;
-
-	if(atactlrmask & (1<<ctlrno))
-		return;
-	atactlrmask |= 1<<ctlrno;
-
-	port = pbase[ctlrno];
-	outb(port+Pdh, DHmagic);
-	delay(1);
-	if((inb(port+Pdh) & 0xFF) != DHmagic){
-		DPRINT("ata%d: DHmagic not ok\n", ctlrno);
-		return;
-	}
-	DPRINT("ata%d: DHmagic ok\n", ctlrno);
-
-	atactlr[ctlrno] = ialloc(sizeof(Controller), 0);
-	ctlr = atactlr[ctlrno];
-	ctlr->pbase = port;
-	ctlr->ctlrno = ctlrno;
-	ctlr->buf = ialloc(Maxxfer, 0);
-	inb(ctlr->pbase+Pstatus);
-	setvec(irq, ataintr, ctlr);
-
-	driveno = ctlrno*2;
-	atadrive[driveno] = ialloc(sizeof(Drive), 0);
-	drive = atadrive[driveno];
-	drive->cp = ctlr;
-	drive->driveno = driveno;
-	drive->dh = DHmagic;
-
-	driveno++;
-	atadrive[driveno] = ialloc(sizeof(Drive), 0);
-	drive = atadrive[driveno];
-	drive->cp = ctlr;
-	drive->driveno = driveno;
-	drive->dh = DHmagic|DHslave;
-}
-
-static Drive*
-atadriveprobe(int driveno)
-{
-	Drive *drive;
-	int ctlrno;
-	ISAConf isa;
-
-	ctlrno = driveno/2;
-	if(atactlr[ctlrno] == 0){
-		if(atactlrmask & (1<<ctlrno))
-			return 0;
-		memset(&isa, 0, sizeof(ISAConf));
-		if(isaconfig("ata", ctlrno, &isa) == 0)
-			return 0;
-		if(ctlrno && isa.irq)
-			atactlrprobe(ctlrno, Int0vec+isa.irq);
-		if(atactlr[ctlrno] == 0)
-			return 0;
-	}
-
-	drive = atadrive[driveno];
-	drive->driveno = driveno;
-	if(drive->online == 0){
-		if(atadrivemask & (1<<driveno))
-			return 0;
-		atadrivemask |= 1<<driveno;
-		if(ataparams(drive))
-			return 0;
-		if(drive->lba)
-			print("hd%d: LBA %lud sectors\n",
-				drive->driveno, drive->cap);
-		else
-			print("hd%d: CHS %ld/%d/%d %lud sectors\n",
-				drive->driveno, drive->cyl, drive->heads,
-				drive->sectors, drive->cap);
-		drive->online = 1;
-	}
-
-	return atapart(drive);
-}
-
-int
-atainit(void)
-{
-	ISAConf isa;
-	int ctlrno;
-
-	for(ctlrno = 0; ctlrno < NCtlr; ctlrno++){
-		memset(&isa, 0, sizeof(ISAConf));
-		if(isaconfig("ata", ctlrno, &isa) == 0 && ctlrno > 1)
-			continue;
-		if(isa.irq == 0 && (isa.irq = defirq[ctlrno]) == 0)
-			continue;
-		atactlrprobe(ctlrno, Int0vec+isa.irq);
-	}
-	DPRINT("atainit: mask 0x%ux 0x%ux\n", atactlrmask, atadrivemask);
-
-	return 0xFF;
-}
-
-vlong
-ataseek(int driveno, vlong offset)
-{
-	Drive *drive;
-
-	drive = atadrive[driveno];
-	if (drive == nil || !drive->online)
-		return -1;
-	drive->offset = offset;
-	return offset;
-}
-
-/*
- *  did an interrupt happen?
- */
-static void
-atawait(Controller *cp, int timo)
-{
-	ulong start;
-	int x;
-
-	x = spllo();
-	start = m->ticks;
-	while(TK2MS(m->ticks - start) < timo){
-		if(cp->cmd == 0)
-			break;
-		if(cp->cmd == Cident2 && TK2MS(m->ticks - start) >= 1000)
-			break;
-	}
-	if(TK2MS(m->ticks - start) >= timo){
-		DPRINT("atawait timed out 0x%ux\n", inb(cp->pbase+Pstatus));
-		ataintr(0, cp);
-	}
-	splx(x);
-}
-
-int
-setatapart(int driveno, char *p)
-{
-	Partition *pp;
-	Drive *dp;
-
-	if((dp = atadriveprobe(driveno)) == 0)
-		return 0;
-
-	for(pp = dp->p; pp < &dp->p[dp->npart]; pp++)
-		if(strcmp(pp->name, p) == 0){
-			dp->current = pp;
-			return 1;
-		}
-	return 0;
-}
-
-long
-ataread(int driveno, void *a, long n)
-{
-	Drive *dp;
-	vlong rv, i;
-	int skip;
-	uchar *aa = a;
-	Partition *pp;
-	Controller *cp;
-
-	dp = atadrive[driveno];
-	if(dp == nil || !dp->online)
-		return 0;
-
-	pp = dp->current;
-	//  can ide leave pp as nil? yes
-	if(0 && pp == 0)
-		return -1;
-	cp = dp->cp;
-
-	if (dp->bytes == 0)
-		panic("ataread: zero dp->bytes");
-	skip = dp->offset % dp->bytes;
-	for(rv = 0; rv < n; rv += i){
-		i = ataxfer(dp, pp, Cread, dp->offset+rv-skip, n-rv+skip);
-		if(i == 0)
-			break;
-		if(i < 0)
-			return -1;
-		i -= skip;
-		if(i > n - rv)
-			i = n - rv;
-		memmove(aa+rv, cp->buf + skip, i);
-		skip = 0;
-	}
-	dp->offset += rv;
-
-	return rv;
-}
-
-long
-atawrite(int driveno, void *a, long n)
-{
-	Drive *dp;
-	vlong rv, i, partial;
-	uchar *aa = a;
-	Partition *pp;
-	Controller *cp;
-
-	dp = atadrive[driveno];
-	if(dp == nil || !dp->online)
-		return 0;
-
-	pp = dp->current;
-	// pp can legitimately be nil
-	if(0 && pp == 0)
-		return -1;
-	cp = dp->cp;
-
-	/*
-	 *  if not starting on a sector boundary,
-	 *  read in the first sector before writing
-	 *  it out.
-	 */
-	if (dp->bytes == 0)
-		panic("atawrite: readin zero dp->bytes");
-	partial = dp->offset % dp->bytes;
-	if(partial){
-		if (ataxfer(dp, pp, Cread, dp->offset-partial, dp->bytes) < 0)
-			return -1;
-		if(partial+n > dp->bytes)
-			rv = dp->bytes - partial;
-		else
-			rv = n;
-		memmove(cp->buf+partial, aa, rv);
-		if(ataxfer(dp, pp, Cwrite, dp->offset-partial, dp->bytes) < 0)
-			return -1;
-	} else
-		rv = 0;
-
-	/*
-	 *  write out the full sectors
-	 */
-	if (dp->bytes == 0)
-		panic("atawrite: write full zero dp->bytes");
-	partial = (n - rv) % dp->bytes;
-	n -= partial;
-	for(; rv < n; rv += i){
-		i = n - rv;
-		if(i > Maxxfer)
-			i = Maxxfer;
-		memmove(cp->buf, aa+rv, i);
-		i = ataxfer(dp, pp, Cwrite, dp->offset+rv, i);
-		if(i == 0)
-			break;
-		if(i < 0)
-			return -1;
-	}
-
-	/*
-	 *  if not ending on a sector boundary,
-	 *  read in the last sector before writing
-	 *  it out.
-	 */
-	if(partial){
-		if(ataxfer(dp, pp, Cread, dp->offset+rv, dp->bytes) < 0)
-			return -1;
-		memmove(cp->buf, aa+rv, partial);
-		if(ataxfer(dp, pp, Cwrite, dp->offset+rv, dp->bytes) < 0)
-			return -1;
-		rv += partial;
-	}
-	dp->offset += rv;
-	return rv;
-}
-
-/*
- *  wait for the controller to be ready to accept a command
- */
-static int
-cmdreadywait(Drive *drive)
-{
-	ulong end;
-	uchar dh, status;
-	Controller *ctlr;
-
-	ctlr = drive->cp;
-	end = m->ticks+MS2TK(100)+1;
-	dh = (inb(ctlr->pbase+Pdh) & DHslave)^(drive->dh & DHslave);
-
-	status = 0;
-	while(m->ticks < end){
-		status = inb(ctlr->pbase+Pstatus);
-		if(status & Sbusy)
-			continue;
-		if(dh){
-			outb(ctlr->pbase+Pdh, drive->dh);
-			dh = 0;
-			continue;
-		}
-		if(drive->atapi || (status & Sready))
-			return 0;
-	}
-	USED(status);
-	DPRINT("h%d: cmdreadywait failed 0x%ux\n", drive->driveno, status);
-	outb(ctlr->pbase+Pdh, DHmagic);
-	return -1;
-}
-
-/*
- *  transfer a number of sectors.  ataintr will perform all the iterative
- *  parts.
- */
-static long
-ataxfer(Drive *dp, Partition *pp, int cmd, uvlong start, long len)
-{
-	Controller *cp;
-	long lsec;
-	int loop;
-
-	if(dp->online == 0){
-		DPRINT("hd%d: disk not on line\n", dp->driveno);
-		return -1;
-	}
-
-	/*
-	 *  cut transfer size down to disk buffer size
-	 */
-	if (dp->bytes == 0)
-		panic("ataxfer: zero dp->bytes");
-	start /= dp->bytes;
-	if(len > Maxxfer)
-		len = Maxxfer;
-	len = (len + dp->bytes - 1) / dp->bytes;
-
-	/*
-	 *  calculate physical address
-	 */
-	cp = dp->cp;
-	if (pp) {	// ide did not check; was assumed non-nil by 3rd ed fs.
-		lsec = start + pp->start;
-		if(lsec >= pp->end){
-			print("hd%d: read past end of partition\n", dp->driveno);
-			return 0;
-		}
-	} else
-		lsec = start;
-
-	if(dp->lba) {
-		cp->tsec = lsec & 0xff;
-		cp->tcyl = (lsec>>8) & 0xffff;
-		cp->thead = (lsec>>24) & 0xf;
-	} else {
-		if (dp->sectors == 0)
-			panic("ataxfer: zero dp->sectors");
-		if (dp->heads == 0)
-			panic("ataxfer: zero dp->heads");
-		cp->tcyl = lsec/(dp->sectors*dp->heads);
-		cp->tsec = (lsec % dp->sectors) + 1;
-		cp->thead = (lsec/dp->sectors) % dp->heads;
-	}
-
-	/*
-	 *  can't xfer past end of disk
-	 */
-	if (pp) 		// ide did not check
-	if(lsec+len > pp->end)
-		len = pp->end - lsec;
-	cp->nsecs = len;
-
-	if(dp->atapi){
-		if(cmd == Cread)
-			cp->cmd = Cread2;
-		else
-			cp->cmd = Cwrite2;
-		cp->dp = dp;
-		cp->sofar = 0;
-		cp->status = 0;
-		return atapiio(dp, lsec);
-	}
-
-	if(cmdreadywait(dp) < 0){
-		print("h%d: cmdreadywait failed (%s)\n",
-			dp->driveno, (cmd==Cwrite? "write": "read"));
-		return -1;
-	}
-
-	/*
-	 *  start the transfer
-	 */
-	cp->cmd = cmd;
-	cp->dp = dp;
-	cp->sofar = 0;
-	cp->status = 0;
-	DPRINT("hd%d: xfer:\ttcyl %d, tsec %d, thead %d\n",
-		dp->driveno, cp->tcyl, cp->tsec, cp->thead);
-	DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar);
-	outb(cp->pbase+Pcount, cp->nsecs);
-	outb(cp->pbase+Psector, cp->tsec);
-	outb(cp->pbase+Pdh, dp->dh | (dp->lba<<6) | cp->thead);
-	outb(cp->pbase+Pcyllsb, cp->tcyl);
-	outb(cp->pbase+Pcylmsb, cp->tcyl>>8);
-	outb(cp->pbase+Pcmd, cmd);
-
-	if(cmd == Cwrite){
-		loop = 0;
-		while((inb(cp->pbase+Pstatus) & Sdrq) == 0)
-			if(++loop > 10000)
-				panic("ataxfer");
-		outss(cp->pbase+Pdata, cp->buf, dp->bytes/2);
-	}
-
-	atawait(cp, ATAtimo);
-
-	if(cp->status & Serr){
-		DPRINT("hd%d: err: status 0x%ux, err 0x%ux\n",
-			dp->driveno, cp->status, cp->error);
-		DPRINT("\ttcyl %d, tsec %d, thead %d\n",
-			cp->tcyl, cp->tsec, cp->thead);
-		DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar);
-		return -1;
-	}
-
-	return cp->nsecs*dp->bytes;
-}
-
-static int
-isatapi(Drive *drive)
-{
-	Controller *cp;
-
-	cp = drive->cp;
-	outb(cp->pbase+Pdh, drive->dh);
-	IDPRINT("hd%d: isatapi %d\n", drive->driveno, drive->atapi);
-	outb(cp->pbase+Pcmd, 0x08);
-	drive->atapi = 1;
-	if(cmdreadywait(drive)){
-		drive->atapi = 0;
-		return 0;
-	}
-	drive->atapi = 0;
-	drive->bytes = Sectsize;
-	microdelay(1);
-	if(inb(cp->pbase+Pstatus)){
-		IDPRINT("hd%d: isatapi status 0x%ux\n",
-			drive->driveno, inb(cp->pbase+Pstatus));
-		return 0;
-	}
-	if(inb(cp->pbase+Pcylmsb) != 0xEB || inb(cp->pbase+Pcyllsb) != 0x14){
-		IDPRINT("hd%d: isatapi cyl 0x%ux 0x%ux\n",
-			drive->driveno, inb(cp->pbase+Pcylmsb), inb(cp->pbase+Pcyllsb));
-		return 0;
-	}
-	drive->atapi = 1;
-
-	return 1;
-}
-
-/*
- *  get parameters from the drive
- */
-static int
-ataident(Drive *dp)
-{
-	Controller *cp;
-	Ident *ip;
-	ulong lbasecs;
-	char id[21];
-
-	dp->bytes = Sectsize;
-	cp = dp->cp;
-
-retryatapi:
-	cp->nsecs = 1;
-	cp->sofar = 0;
-	cp->dp = dp;
-	outb(cp->pbase+Pdh, dp->dh);
-	microdelay(1);
-	if(inb(cp->pbase+Pcylmsb) == 0xEB && inb(cp->pbase+Pcyllsb) == 0x14){
-		dp->atapi = 1;
-		cp->cmd = Cidentd;
-	}
-	else
-		cp->cmd = Cident;
-	outb(cp->pbase+Pcmd, cp->cmd);
-
-	IDPRINT("hd%d: ident command 0x%ux sent\n", dp->driveno, cp->cmd);
-	atawait(cp, ATAPItimo);
-
-	if(cp->status & Serr){
-		IDPRINT("hd%d: bad disk ident status\n", dp->driveno);
-		if(cp->error & Eabort){
-			if(isatapi(dp) && cp->cmd != Cidentd)
-				goto retryatapi;
-		}
-		return -1;
-	}
-
-	atawait(cp, ATAtimo);
-
-	ip = (Ident*)cp->buf;
-	memmove(id, ip->model, sizeof(id)-1);
-	id[sizeof(id)-1] = 0;
-
-	IDPRINT("hd%d: config 0x%ux capabilities 0x%ux\n",
-		dp->driveno, ip->config, ip->capabilities);
-	if(dp->atapi){
-		if((ip->config & 0x60) == 0x20)
-			dp->drqintr = 1;
-		if((ip->config & 0x1F00) == 0)
-			dp->atapi = 2;
-	}
-
-	lbasecs = (ip->lbasecs[0]) | (ip->lbasecs[1]<<16);
-	/* NB: the `& 0xf0000000' limits LBA IDE disks to 128GB */
-	if((ip->capabilities & (1<<9)) && (lbasecs & 0xf0000000) == 0){
-		dp->lba = 1;
-		dp->cap = lbasecs;
-//		dp->lba = 0;
-//		dp->cyl = 16383;
-//		dp->heads=16;
-//		dp->sectors=63;
-//		dp->cap = lbasecs;
-//		if (lbasecs > 0xffff * 16 * 63)
-//			dp->cap = 0xffff * 16 * 63;
-	} else {
-		dp->lba = 0;
-
-		if(ip->cvalid&(1<<0)){
-			/* use current settings */
-			dp->cyl = ip->ccyls;
-			dp->heads = ip->cheads;
-			dp->sectors = ip->cs2t;
-		}
-		else{
-			/* use default (unformatted) settings */
-			dp->cyl = ip->cyls;
-			dp->heads = ip->heads;
-			dp->sectors = ip->s2t;
-		}
-
-		/*
-		 * Very strange, but some old non-LBA discs report
-		 * sectors > 32.
-		if(dp->heads >= 64 || dp->sectors >= 32)
-			return -1;
-		 */
-		dp->cap = dp->cyl * dp->heads * dp->sectors;
-	}
-	IDPRINT("hd%d: %s  lba/atapi/drqintr: %d/%d/%d  C/H/S: %ld/%d/%d  CAP: %ld\n",
-		dp->driveno, id,
-		dp->lba, dp->atapi, dp->drqintr,
-		dp->cyl, dp->heads, dp->sectors,
-		dp->cap);
-
-	return 0;
-}
-
-/*
- *  probe the given sector to see if it exists
- */
-static int
-ataprobe(Drive *dp, int cyl, int sec, int head)
-{
-	Controller *cp;
-
-	cp = dp->cp;
-	if(cmdreadywait(dp) < 0)
-		return -1;
-
-	/*
-	 *  start the transfer
-	 */
-	cp->cmd = Cread;
-	cp->dp = dp;
-	cp->sofar = 0;
-	cp->nsecs = 1;
-	cp->status = 0;
-	outb(cp->pbase+Pcount, 1);
-	outb(cp->pbase+Psector, sec+1);
-	outb(cp->pbase+Pdh, dp->dh | (dp->lba<<6) | head);
-	outb(cp->pbase+Pcyllsb, cyl);
-	outb(cp->pbase+Pcylmsb, cyl>>8);
-	outb(cp->pbase+Pcmd, Cread);
-
-	atawait(cp, ATAtimo);
-
-	if(cp->status & Serr)
-		return -1;
-
-	return 0;
-}
-
-/*
- *  figure out the drive parameters
- */
-static int
-ataparams(Drive *dp)
-{
-	int i, hi, lo;
-
-	/*
-	 *  first try the easy way, ask the drive and make sure it
-	 *  isn't lying.
-	 */
-	dp->bytes = Sectsize;
-	if(ataident(dp) < 0)
-		return -1;
-	if(dp->atapi)
-		return 0;
-	if(dp->lba){
-		i = dp->cap - 1;
-		if(ataprobe(dp, (i>>8)&0xffff, (i&0xff)-1, (i>>24)&0xf) == 0)
-			return 0;
-	} else {
-		if(ataprobe(dp, dp->cyl-1, dp->sectors-1, dp->heads-1) == 0)
-			return 0;
-	}
-
-	IDPRINT("hd%d: ataparam: cyl %ld sectors %d heads %d\n",
-		dp->driveno, dp->cyl, dp->sectors, dp->heads);
-	/*
-	 *  the drive lied, determine parameters by seeing which ones
-	 *  work to read sectors.
-	 */
-	dp->lba = 0;
-	for(i = 0; i < 16; i++)
-		if(ataprobe(dp, 0, 0, i) < 0)
-			break;
-	dp->heads = i;
-	for(i = 0; i < 64; i++)
-		if(ataprobe(dp, 0, i, 0) < 0)
-			break;
-	dp->sectors = i;
-	for(i = Sectsize; ; i += Sectsize)
-		if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
-			break;
-	lo = i - Sectsize;
-	hi = i;
-	for(; hi-lo > 1;){
-		i = lo + (hi - lo)/2;
-		if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
-			hi = i;
-		else
-			lo = i;
-	}
-	dp->cyl = lo + 1;
-	dp->cap = dp->cyl * dp->heads * dp->sectors;
-
-	print("hd%d: ataparam: cyl %ld sectors %d heads %d\n",
-		dp->driveno, dp->cyl, dp->sectors, dp->heads);
-	if(dp->cyl == 0 || dp->heads == 0 || dp->sectors == 0)
-		return -1;
-
-	return 0;
-}
-
-static Drive*
-atapart(Drive *dp)
-{
-	Partition *pp;
-
-	if(dp->partok)
-		return dp;
-
-	/*
-	 *  we always have a partition for the whole disk
-	 */
-	pp = &dp->p[0];
-	strcpy(pp->name, "disk");
-	dp->npart = 1;
-	pp->start = 0;
-	if(dp->atapi)
-		return atapipart(dp);
-	pp->end = dp->cap;
-	dp->partok = 1;
-
-	return dp;
-}
-
-/*
- *  we get an interrupt for every sector transferred
- */
-static void
-ataintr(Ureg*, void *arg)
-{
-	Controller *cp;
-	Drive *dp;
-	long loop;
-
-	cp = arg;
-	if((dp = cp->dp) == 0)
-		return;
-
-	loop = 0;
-	while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy)
-		if(++loop > Maxloop){
-			print("hd%d: intr busy: status 0x%ux\n", dp->driveno, cp->status);
-			break;
-		}
-	switch(cp->cmd){
-	case Cwrite:
-		if(cp->status & Serr){
-			cp->error = inb(cp->pbase+Perror);
-			cp->cmd = 0;
-			return;
-		}
-		cp->sofar++;
-		if(cp->sofar < cp->nsecs){
-			loop = 0;
-			while((inb(cp->pbase+Pstatus) & Sdrq) == 0)
-				if(++loop > 10000){
-					print("ataintr 1");
-					cp->cmd = 0;
-					return;
-				}
-			outss(cp->pbase+Pdata, &cp->buf[cp->sofar*dp->bytes],
-				dp->bytes/2);
-		} else{
-			cp->cmd = 0;
-		}
-		break;
-	case Cread:
-	case Cident:
-	case Cidentd:
-		if(cp->status & Serr){
-			cp->cmd = 0;
-			cp->error = inb(cp->pbase+Perror);
-			return;
-		}
-		loop = 0;
-		while((inb(cp->pbase+Pstatus) & Sdrq) == 0)
-			if(++loop > Maxloop){
-				print("hd%d: intr drq: cmd %ux status 0x%ux",
-					dp->driveno, cp->cmd, inb(cp->pbase+Pstatus));
-				cp->cmd = 0;
-				return;
-			}
-		inss(cp->pbase+Pdata, &cp->buf[cp->sofar*dp->bytes],
-			dp->bytes/2);
-		cp->sofar++;
-		if(cp->sofar >= cp->nsecs){
-			if(cp->cmd == Cident && (cp->status & Sready) == 0)
-				cp->cmd = Cident2; /* sometimes we get a second intr */
-			else
-				cp->cmd = 0;
-			inb(cp->pbase+Pstatus);
-		}
-		break;
-	case Csetbuf:
-	case Cident2:
-		cp->cmd = 0;
-		break;
-	case Cpktcmd:
-		atapiintr(cp);
-		break;
-	default:
-		cp->cmd = 0;
-		break;
-	}
-}
-
-static int
-atapiexec(Drive *dp)
-{
-	Controller *cp;
-	int loop, s;
-
-	cp = dp->cp;
-
-	if(cmdreadywait(dp))
-		return -1;
-
-	s = splhi();
-	cp->sofar = 0;
-	cp->error = 0;
-	cp->cmd = Cpktcmd;
-	outb(cp->pbase+Pcount, 0);
-	outb(cp->pbase+Psector, 0);
-	outb(cp->pbase+Pfeature, 0);
-	outb(cp->pbase+Pcyllsb, cp->len);
-	outb(cp->pbase+Pcylmsb, cp->len>>8);
-	outb(cp->pbase+Pdh, dp->dh);
-	outb(cp->pbase+Pcmd, cp->cmd);
-
-	if(dp->drqintr == 0){
-		microdelay(1);
-		for(loop = 0; (inb(cp->pbase+Pstatus) & (Serr|Sdrq)) == 0; loop++){
-			if(loop < 10000)
-				continue;
-			panic("hd%d: cmddrqwait: cmd=0x%ux status=0x%ux\n",
-				dp->driveno, cp->cmd, inb(cp->pbase+Pstatus));
-		}
-		outss(cp->pbase+Pdata, cp->cmdblk, sizeof(cp->cmdblk)/2);
-	}
-	splx(s);
-
-	atawait(cp, ATAPItimo);
-
-	if(cp->status & Serr){
-		DPRINT("hd%d: Bad packet command 0x%ux, error 0x%ux\n",
-			dp->driveno, cp->cmdblk[0], cp->error);
-		return -1;
-	}
-
-	return 0;
-}
-
-static long
-atapiio(Drive *dp, long lba)
-{
-	int n;
-	Controller *cp;
-
-	cp = dp->cp;
-
-	n = cp->nsecs*dp->bytes;
-	cp->len = n;
-	cp->count = 0;
-	memset(cp->cmdblk, 0, 12);
-	cp->cmdblk[0] = cp->cmd;
-	cp->cmdblk[2] = lba >> 24;
-	cp->cmdblk[3] = lba >> 16;
-	cp->cmdblk[4] = lba >> 8;
-	cp->cmdblk[5] = lba;
-	cp->cmdblk[7] = cp->nsecs>>8;
-	cp->cmdblk[8] = cp->nsecs;
-	if(atapiexec(dp))
-		return -1;
-	if(cp->count != n)
-		print("hd%d: short read %d != %d\n", dp->driveno, cp->count, n);
-
-	return n;
-}
-
-static Drive*
-atapipart(Drive *dp)
-{
-	Controller *cp;
-	Partition *pp;
-	int retrycount;
-
-	cp = dp->cp;
-
-	pp = &dp->p[0];
-	pp->end = 0;
-
-	retrycount = 0;
-retry:
-	if(retrycount++){
-		IDPRINT("hd%d: atapipart: cmd 0x%ux error 0x%ux, retry %d\n",
-			dp->driveno, cp->cmdblk[0], cp->error, retrycount);
-		if((cp->status & Serr) && (cp->error & 0xF0) == 0x60){
-			dp->vers++;
-			if(retrycount < 3)
-				goto again;
-		}
-		cp->dp = 0;
-		IDPRINT("hd%d: atapipart: cmd %ux return error %ux, retry %d\n",
-			dp->driveno, cp->cmd, cp->error, retrycount);
-		return 0;
-	}
-again:
-	cp->dp = dp;
-
-	cp->len = 18;
-	cp->count = 0;
-	memset(cp->cmdblk, 0, sizeof(cp->cmdblk));
-	cp->cmdblk[0] = Creqsense;
-	cp->cmdblk[4] = 18;
-	DPRINT("reqsense %d\n", retrycount);
-	atapiexec(dp);
-	//if(atapiexec(dp))
-	//	goto retry;
-	if(cp->count != 18){
-		print("cmd=0x%2.2ux, lastcmd=0x%2.2ux ", cp->cmd, cp->lastcmd);
-		print("cdsize count %d, status 0x%2.2ux, error 0x%2.2ux\n",
-			cp->count, cp->status, cp->error);
-		return 0;
-	}
-
-	cp->len = 8;
-	cp->count = 0;
-	memset(cp->cmdblk, 0, sizeof(cp->cmdblk));
-	cp->cmdblk[0] = Ccapacity;
-	DPRINT("capacity %d\n", retrycount);
-	if(atapiexec(dp))
-		goto retry;
-	if(cp->count != 8){
-		print("cmd=0x%2.2ux, lastcmd=0x%2.2ux ", cp->cmd, cp->lastcmd);
-		print("cdsize count %d, status 0x%2.2ux, error 0x%2.2ux\n",
-			cp->count, cp->status, cp->error);
-		return 0;
-	}
-	dp->sectors = (cp->buf[0]<<24)|(cp->buf[1]<<16)|(cp->buf[2]<<8)|cp->buf[3];
-	dp->bytes = (cp->buf[4]<<24)|(cp->buf[5]<<16)|(cp->buf[6]<<8)|cp->buf[7];
-	if(dp->bytes > 2048 && dp->bytes <= 2352)
-		dp->bytes = 2048;
-	dp->cap = dp->sectors;
-	IDPRINT("hd%d: atapipart secs %ud, bytes %ud, cap %lud\n",
-		dp->driveno, dp->sectors, dp->bytes, dp->cap);
-	cp->dp = 0;
-
-	pp->end = dp->sectors;
-	dp->partok = 1;
-
-	return dp;
-}
-
-static void
-atapiintr(Controller *cp)
-{
-	uchar cause;
-	int count, loop, pbase;
-
-	pbase = cp->pbase;
-	cause = inb(pbase+Pcount) & 0x03;
-	DPRINT("hd%d: atapiintr 0x%ux\n", cp->dp->driveno, cause);
-	switch(cause){
-
-	case 1:						/* command */
-		if(cp->status & Serr){
-			cp->lastcmd = cp->cmd;
-			cp->cmd = 0;
-			cp->error = inb(pbase+Perror);
-			break;
-		}
-		outss(pbase+Pdata, cp->cmdblk, sizeof(cp->cmdblk)/2);
-		break;
-
-	case 0:						/* data out */
-	case 2:						/* data in */
-		if(cp->buf == 0){
-			cp->lastcmd = cp->cmd;
-			cp->cmd = 0;
-			if(cp->status & Serr)
-				cp->error = inb(pbase+Perror);
-			cp->cmd = 0;
-			break;
-		}
-		loop = 0;
-		while((cp->status & (Serr|Sdrq)) == 0){
-			if(++loop > Maxloop){
-				cp->status |= Serr;
-				break;
-			}
-			cp->status = inb(pbase+Pstatus);
-		}
-		if(cp->status & Serr){
-			cp->lastcmd = cp->cmd;
-			cp->cmd = 0;
-			cp->error = inb(pbase+Perror);
-			print("hd%d: Cpktcmd status=0x%ux, error=0x%ux\n",
-				cp->dp->driveno, cp->status, cp->error);
-			break;
-		}
-		count = inb(pbase+Pcyllsb)|(inb(pbase+Pcylmsb)<<8);
-		if(cp->count+count > Maxxfer)
-			panic("hd%d: count %d, already %d\n", count, cp->count);
-		if(cause == 0)
-			outss(pbase+Pdata, cp->buf+cp->count, count/2);
-		else
-			inss(pbase+Pdata, cp->buf+cp->count, count/2);
-		cp->count += count;
-		break;
-
-	case 3:						/* status */
-		cp->lastcmd = cp->cmd;
-		cp->cmd = 0;
-		if(cp->status & Serr)
-			cp->error = inb(cp->pbase+Perror);
-		break;
-	}
-}
-
-static int
-ideblkvfy(Device *d, long b, char *buff)
-{
-	long *lp = (long *)buff;	// lp never changes
-
-	*lp = b;
-	idewrite(d, b, buff);
-	// TODO: invalidate controller & drive caches
-	ideread (d, b, buff);
-	return *lp == b;
-}
-
-void
-idecheck(Device *d)
-{
-	static char buff[RBUFSIZE];
-	long *lp = (long *)buff;	// lp never changes
-	long b, start, max;
-	Drive *dp;
-
-	max = atasize(d);
-	dp = d->private;
-	// write the block # to each block
-	// read it back and check that things match
-	print("Drive check: %lud cap, %d bytes, %d sec %d hd %ld cyl\n",
-		dp->cap, dp->bytes, dp->sectors, dp->heads, dp->cyl);
-	for (start = 0; start < max; start += 1000) {
-		print("Write to %lud - %lud blocks...\n", start, start + 1000);
-		for (b = start; b < start+ 1000 && b < max; b+= 10)
-			if (!ideblkvfy(d, b, buff)) {
-				print("block %lux (%lud) : %lux (%lud)\n",
-					b, b, *lp, *lp);
-				print("immediate check failed\n");
-				if (!ideblkvfy(d, b, buff))
-					print("failed twice\n");
-			}
-		print("Checking block numbers...\n");
-		for(b = start; b < start+1000 && b < max; b+=10) {
-			ideread(d, b, buff);
-			if (*lp != b) {
-				print("block %lux (%lud) : %lux (%lud)\n",
-					b, b, *lp, *lp);
-				print("check failed\n");
-				return;
-			}
-		}
-	}
-}
-
-ideread(Device *d, long b, void *c)
-{
-	int x;
-	Drive *dp;
-	Controller *cp;
-
-	dp = d->private;
-	cp = dp->cp;
-
-	qlock(cp);
-	IDPRINT("ideread(dev %ux, %ld, %ux, %d): %ux\n",
-		(int)d, b, (int)c, dp->driveno, (int)dp);
-	ataseek(dp->driveno, (vlong)b * (vlong)RBUFSIZE);
-	x = ataread(dp->driveno, c, RBUFSIZE) == 0;
-	qunlock(cp);
-	return x;
-}
-
-idewrite(Device *d, long b, void *c)
-{
-	int x;
-	Drive *dp;
-	Controller *cp;
-
-	dp = d->private;
-	cp = dp->cp;
-
-	qlock(cp);
-	IDPRINT("idewrite(%ux, %ld, %ux): driveno %d\n",
-		(int)d, b, (int)c, dp->driveno);
-	ataseek(dp->driveno, (vlong)b * (vlong)RBUFSIZE);
-	x = atawrite(dp->driveno, c, RBUFSIZE) == 0;
-	qunlock(cp);
-	return x;
-}
-
-// result is size of d in blocks of RBUFSIZE bytes
-long
-atasize(Device *d)
-{
-	Drive *dp;
-	long blocks;
-
-	dp = d->private;
-	// dividing first is sloppy but reduces the range of intermediate values
-	blocks = (dp->cap / RBUFSIZE) * Sectsize;
-	print("atasize(%ux):  %lud -> %lud\n", (int)d, dp->cap, blocks);
-	return blocks;
-}
-
-void
-ideinit(Device *d)
-{
-	Drive *dp;
-
-	if (d->private)
-		return;
-	if ((dp = atadriveprobe(d->wren.ctrl * 2 + d->wren.targ))) {
-		print("ideinit(device %ux ctrl %d targ %d) driveno %d dp %ux\n",
-			(int)d, d->wren.ctrl, d->wren.targ, dp->driveno,
-			(int)dp);
-		d->private = dp;
-	}
-}

+ 32 - 29
sys/src/fs/pc/dosfs.c

@@ -24,7 +24,7 @@ struct Clustbuf
 {
 	int	flags;
 	int	age;
-	long	sector;
+	Devsize	sector;
 	uchar *	iobuf;
 	Dos *	dos;
 	int	size;
@@ -50,11 +50,12 @@ static void
 writeclust(Clustbuf *p)
 {
 	Dos *dos;
-	int addr;
+	Off addr;
 
 	dos = p->dos;
 	addr = (p->sector+dos->start)*dos->sectbytes;
-	chat("writeclust @ %ld addr %d...", p->sector, addr);
+	chat("writeclust @ %lld addr %lld...", (Wideoff)p->sector,
+		(Wideoff)addr);
 	if((*dos->seek)(dos->dev, addr) < 0)
 		panic("writeclust: seek");
 	if((*dos->write)(dos->dev, p->iobuf, p->size) != p->size)
@@ -83,11 +84,11 @@ syncclust(void)
  *  get an io buffer, possibly with valid data
  */
 static Clustbuf*
-getclust0(Dos *dos, long sector)
+getclust0(Dos *dos, Off sector)
 {
 	Clustbuf *p, *oldest;
 
-	chat("getclust0 @ %ld\n", sector);
+	chat("getclust0 @ %lld\n", (Wideoff)sector);
 
 	/*
 	 *  if we have it, just return it
@@ -98,7 +99,7 @@ getclust0(Dos *dos, long sector)
 		if(sector == p->sector && dos == p->dos){
 			if(p->flags & LOCKED)
 				panic("getclust0 locked");
-			chat("getclust0 %ld in cache\n", sector);
+			chat("getclust0 %lld in cache\n", (Wideoff)sector);
 			p->flags |= LOCKED;
 			return p;
 		}
@@ -133,10 +134,10 @@ getclust0(Dos *dos, long sector)
  *  get an io block from an io buffer
  */
 static Clustbuf*
-getclust(Dos *dos, long sector)
+getclust(Dos *dos, Off sector)
 {
 	Clustbuf *p;
-	int addr;
+	Off addr;
 
 	p = getclust0(dos, sector);
 	if(p->dos){
@@ -144,12 +145,12 @@ getclust(Dos *dos, long sector)
 		return p;
 	}
 	addr = (sector+dos->start)*dos->sectbytes;
-	chat("getclust seek addr %d\n", addr);
+	chat("getclust seek addr %lld\n", (Wideoff)addr);
 	if((*dos->seek)(dos->dev, addr) < 0){
 		chat("can't seek block\n");
 		return 0;
 	}
-	chat("getclust read addr %d\n", addr);
+	chat("getclust read addr %lld\n", (Wideoff)addr);
 	if((*dos->read)(dos->dev, p->iobuf, p->size) != p->size){
 		chat("can't read block\n");
 		return 0;
@@ -158,7 +159,7 @@ getclust(Dos *dos, long sector)
 	p->age = MACHP(0)->ticks;
 	p->dos = dos;
 	p->sector = sector;
-	chat("getclust %ld read\n", sector);
+	chat("getclust %lld read\n", (Wideoff)sector);
 	return p;
 }
 
@@ -167,7 +168,7 @@ getclust(Dos *dos, long sector)
  *  any current data is discarded.
  */
 static Clustbuf*
-getclustz(Dos *dos, long sector)
+getclustz(Dos *dos, Off sector)
 {
 	Clustbuf *p;
 
@@ -177,7 +178,7 @@ getclustz(Dos *dos, long sector)
 	p->sector = sector;
 	memset(p->iobuf, 0, p->size);
 	p->flags |= MOD;
-	chat("getclustz %ld\n", sector);
+	chat("getclustz %lld\n", (Wideoff)sector);
 	return p;
 }
 
@@ -192,7 +193,7 @@ putclust(Clustbuf *p)
 	if((p->flags & (MOD|IMMED)) == (MOD|IMMED))
 		writeclust(p);
 	p->flags &= ~LOCKED;
-	chat("putclust @ sector %ld...", p->sector);
+	chat("putclust @ sector %lld...", (Wideoff)p->sector);
 }
 
 /*
@@ -252,7 +253,7 @@ fatwalk(Dos *dos, int n)
 static void
 fatwrite(Dos *dos, int n, int val)
 {
-	ulong k, sect;
+	Off k, sect;
 	Clustbuf *p;
 	int i, o;
 
@@ -343,13 +344,13 @@ fatalloc(Dos *dos)
  *  map a file's logical sector address to a physical sector address
  */
 static long
-fileaddr(Dosfile *fp, long ltarget, Clustbuf *pdir)
+fileaddr(Dosfile *fp, Off ltarget, Clustbuf *pdir)
 {
 	Dos *dos = fp->dos;
 	Dosdir *dp;
-	long p;
+	Off p;
 
-	chat("fileaddr %8.8s %ld\n", fp->name, ltarget);
+	chat("fileaddr %8.8s %lld\n", fp->name, (Wideoff)ltarget);
 	/*
 	 *  root directory is contiguous and easy
 	 */
@@ -357,7 +358,7 @@ fileaddr(Dosfile *fp, long ltarget, Clustbuf *pdir)
 		if(ltarget*dos->sectbytes >= dos->rootsize*sizeof(Dosdir))
 			return -1;
 		p = dos->rootaddr + ltarget;
-		chat("fileaddr %ld -> %ld\n", ltarget, p);
+		chat("fileaddr %lld -> %lld\n", (Wideoff)ltarget, (Wideoff)p);
 		return p;
 	}
 	if(fp->pstart == 0){	/* empty file */
@@ -366,7 +367,7 @@ fileaddr(Dosfile *fp, long ltarget, Clustbuf *pdir)
 		p = fatalloc(dos);
 		if(p <= 0)
 			return -1;
-		chat("fileaddr initial alloc %ld\n", p);
+		chat("fileaddr initial alloc %lld\n", (Wideoff)p);
 		dp = (Dosdir *)(pdir->iobuf + fp->odir);
 		puttime(dp);
 		dp->start[0] = p;
@@ -407,7 +408,7 @@ fileaddr(Dosfile *fp, long ltarget, Clustbuf *pdir)
 	 *  clusters start at 2 instead of 0 (why? - presotto)
 	 */
 	p = dos->dataaddr + (fp->pcurrent-2)*dos->clustsize;
-	chat("fileaddr %ld -> %ld\n", ltarget, p);
+	chat("fileaddr %lld -> %lld\n", (Wideoff)ltarget, (Wideoff)p);
 	return p;
 }
 
@@ -456,7 +457,7 @@ doswalk(Dosfile *fp, char *name)
 	char dname[8], dext[3];
 	Clustbuf *p;
 	Dosdir *dp;
-	long o, addr;
+	Off o, addr;
 
 	if((fp->attr & DOSDIR) == 0){
 		chat("walking non-directory!\n");
@@ -635,7 +636,8 @@ dosinit(Dos *dos)
 	dos->root.attr = DOSDIR;
 	dos->root.length = dos->rootsize*sizeof(Dosdir);
 	dos->root.pstart = 0;
-	dos->root.pcurrent = dos->root.lcurrent = 0;
+	dos->root.lcurrent = 0;
+	dos->root.pcurrent = 0;
 	dos->root.offset = 0;
 
 	syncclust();
@@ -665,7 +667,7 @@ nextelem(char *path, char *elem)
 static void
 puttime(Dosdir *d)
 {
-	ulong secs;
+	Timet secs;
 	Rtc rtc;
 	ushort x;
 
@@ -694,8 +696,9 @@ dosopen(Dos *dos, char *path, Dosfile *fp)
 			print("%s not found\n", element);
 			return 0;
 		case 1:
-			print("found %s attr 0x%ux start 0x%lux len %ld\n",
-				element, fp->attr, fp->pstart, fp->length);
+			print("found %s attr 0x%ux start 0x%llux len %lld\n",
+				element, fp->attr, (Wideoff)fp->pstart,
+				(Wideoff)fp->length);
 			break;
 		}
 	}
@@ -710,7 +713,7 @@ dosopen(Dos *dos, char *path, Dosfile *fp)
 long
 dosread(Dosfile *fp, void *a, long n)
 {
-	long addr, k, o;
+	Off addr, k, o;
 	Clustbuf *p;
 	uchar *to;
 
@@ -755,7 +758,7 @@ dosread(Dosfile *fp, void *a, long n)
 long
 doswrite(Dosfile *fp, void *a, long n)
 {
-	long blksize, addr, k, o;
+	Off blksize, addr, k, o;
 	Clustbuf *p, *pdir;
 	Dosdir *dp;
 	uchar *from;
@@ -825,7 +828,7 @@ dostrunc(Dosfile *fp)
 {
 	Clustbuf *pdir;
 	Dosdir *dp;
-	int p, np;
+	Off p, np;
 
 	if(fp->attr & DOSDIR){
 		print("trunc dir\n");

+ 12 - 12
sys/src/fs/pc/dosfs.h

@@ -46,18 +46,18 @@ struct Dosfile{
 	char	name[8];
 	char	ext[3];
 	uchar	attr;
-	long	length;
-	long	pstart;		/* physical start cluster address */
-	long	pcurrent;	/* physical current cluster address */
-	long	lcurrent;	/* logical current cluster address */
-	long	offset;
+	Devsize	length;
+	Devsize	pstart;		/* physical start cluster address */
+	Devsize	pcurrent;	/* physical current cluster address */
+	Devsize	lcurrent;	/* logical current cluster address */
+	Devsize	offset;
 };
 
 struct Dos{
 	int	dev;				/* device id */
-	long	(*read)(int, void*, long);	/* read routine */
-	vlong	(*seek)(int, vlong);		/* seek routine */
-	long	(*write)(int, void*, long);	/* write routine */
+	Off	(*read)(int, void*, long);	/* read routine */
+	Devsize	(*seek)(int, Devsize);		/* seek routine */
+	Off	(*write)(int, void*, long);	/* write routine */
 
 	int	start;		/* start of file system (sector no.) */
 	int	sectbytes;	/* size of a sector */
@@ -72,10 +72,10 @@ struct Dos{
 	int	fatbytes;	/* size of a fat (in bytes) */
 	int	fatclusters;	/* no. of clusters governed by fat */
 	int	fatbits;	/* 12 or 16 */
-	long	fataddr;	/* sector address of first fat */
-	long	rootaddr;	/* sector address of root directory */
-	long	dataaddr;	/* sector address of first data block */
-	long	freeptr;	/* for cluster allocation */
+	Devsize	fataddr;	/* sector address of first fat */
+	Devsize	rootaddr;	/* sector address of root directory */
+	Devsize	dataaddr;	/* sector address of first data block */
+	Devsize	freeptr;	/* for cluster allocation */
 
 	Dosfile	root;
 };

+ 20 - 11
sys/src/fs/pc/ether8139.c

@@ -84,7 +84,7 @@ enum {					/* Imr/Isr */
 	PunLc		= 0x0020,	/* Packet Underrun or Link Change */
 	Fovw		= 0x0040,	/* Receive FIFO Overflow */
 	Clc		= 0x2000,	/* Cable Length Change */
-	Timer		= 0x4000,	/* Timer */
+	Timerbit	= 0x4000,	/* Timer */
 	Serr		= 0x8000,	/* System Error */
 };
 
@@ -226,7 +226,6 @@ static Ctlr* ctlrtail;
 #define csr16w(c, r, w)	(outs((c)->port+(r), (ushort)(w)))
 #define csr32w(c, r, l)	(outl((c)->port+(r), (ulong)(l)))
 
-#ifndef FS
 static void
 rtl8139promiscuous(void* arg, int on)
 {
@@ -285,19 +284,23 @@ rtl8139ifstat(Ether* edev, void* a, long n, ulong offset)
 
 	return n;
 }
-#endif
 
 static int
 rtl8139reset(Ctlr* ctlr)
 {
+	int timeo;
+
 	/*
 	 * Soft reset the controller.
 	 */
 	csr8w(ctlr, Cr, Rst);
-	while(csr8r(ctlr, Cr) & Rst)
-		;
+	for(timeo = 0; timeo < 1000; timeo++){
+		if(!(csr8r(ctlr, Cr) & Rst))
+			return 0;
+		delay(1);
+	}
 
-	return 0;
+	return -1;
 }
 
 static void
@@ -365,7 +368,7 @@ rtl8139init(Ether* edev)
 	 * Interrupts.
 	 */
 	csr32w(ctlr, TimerInt, 0);
-	csr16w(ctlr, Imr, Serr|Timer|Fovw|PunLc|Rxovw|Ter|Tok|Rer|Rok);
+	csr16w(ctlr, Imr, Serr|Timerbit|Fovw|PunLc|Rxovw|Ter|Tok|Rer|Rok);
 	csr32w(ctlr, Mpc, 0);
 
 	/*
@@ -598,7 +601,7 @@ rtl8139interrupt(Ureg*, void* arg)
 		}
 
 		/*
-		 * Only Serr|Timer should be left by now.
+		 * Only Serr|Timerbit should be left by now.
 		 * Should anything be done to tidy up? TimerInt isn't
 		 * used so that can be cleared. A PCI bus error is indicated
 		 * by Serr, that's pretty serious; is there anyhing to do
@@ -607,7 +610,7 @@ rtl8139interrupt(Ureg*, void* arg)
 		if(isr != 0){
 			iprint("rtl8139interrupt: imr %4.4ux isr %4.4ux\n",
 				csr16r(ctlr, Imr), isr);
-			if(isr & Timer)
+			if(isr & Timerbit)
 				csr32w(ctlr, TimerInt, 0);
 			if(isr & Serr)
 				rtl8139init(edev);
@@ -658,6 +661,8 @@ static struct {
 } rtl8139pci[] = {
 	{ "rtl8139",	(0x8139<<16)|0x10EC, },	/* generic */
 	{ "smc1211",	(0x1211<<16)|0x1113, },	/* SMC EZ-Card */
+	{ "dfe-538tx",	(0x1300<<16)|0x1186, }, /* D-Link DFE-538TX */
+	{ "dfe-560txd",	(0x1340<<16)|0x1186, }, /* D-Link DFE-560TXD */
 	{ nil },
 };
 
@@ -757,10 +762,14 @@ rtl8139pnp(Ether* edev)
 	return 0;
 }
 
-#ifndef FS
 void
 ether8139link(void)
 {
 	addethercard("rtl8139", rtl8139pnp);
 }
-#endif
+
+void
+ether8139bothlink(void)
+{
+	ether8139link();
+}

+ 171 - 117
sys/src/fs/pc/ether83815.c

@@ -2,7 +2,8 @@
  * National Semiconductor DP83815
  *
  * Supports only internal PHY and has been tested on:
- *	Netgear FA311TX (using Netgear DS108 10/100 hub)
+ *	Netgear FA311TX (using Netgear DS108 10/100 hub, and using switches)
+ *	SiS 900 within SiS 630 (works under light load only)
  * To do:
  *	check Ethernet address;
  *	test autonegotiation on 10 Mbit, and 100 Mbit full duplex;
@@ -50,13 +51,13 @@ typedef struct Des {
 } Des;
 
 enum {	/* cmdsts */
-	Own		= 1<<31,	/* set by data producer to hand to consumer */
+	Own	= 1<<31,	/* set by data producer to hand to consumer */
 	More	= 1<<30,	/* more of packet in next descriptor */
-	Intr		= 1<<29,	/* interrupt when device is done with it */
+	Intr	= 1<<29,	/* interrupt when device is done with it */
 	Supcrc	= 1<<28,	/* suppress crc on transmit */
 	Inccrc	= 1<<28,	/* crc included on receive (always) */
-	Ok		= 1<<27,	/* packet ok */
-	Size		= 0xFFF,	/* packet size in bytes */
+	Ok	= 1<<27,	/* packet ok */
+	Size	= 0xFFF,	/* packet size in bytes */
 
 	/* transmit */
 	Txa	= 1<<26,	/* transmission aborted */
@@ -74,8 +75,8 @@ enum {	/* cmdsts */
 	Dest	= 3<<23,	/* destination class */
 	  Drej=	0<<23,		/* packet was rejected */
 	  Duni=	1<<23,		/* unicast */
-	  Dmulti=	2<<23,		/* multicast */
-	  Dbroad=	3<<23,		/* broadcast */
+	  Dmulti=2<<23,		/* multicast */
+	  Dbroad=3<<23,		/* broadcast */
 	Long = 1<<22,	/* too long packet received */
 	Runt =  1<<21,	/* packet less than 64 bytes */
 	Ise =	1<<20,	/* invalid symbol */
@@ -92,14 +93,16 @@ enum {
 	SiS		= 0x1039,
 	SiS900		=  (0x900<<16)|SiS,
 	SiS7016		= (0x7016<<16)|SiS,
+	SiS630bridge	= 0x0008,
 
 	// SiS 900 PCI revision codes
 	SiSrev630s =	0x81,
 	SiSrev630e =	0x82,
 	SiSrev630ea1 =	0x83,
 
-	SiSeenodeaddr = 8,
-	Nseenodeaddr = 6,		// in shorts, not bytes
+	SiSeenodeaddr = 8,		// short addr of SiS eeprom mac addr
+	SiS630eenodeaddr = 9,		// likewise for the 630
+	Nseenodeaddr = 6,		// " for NS eeprom
 };
 
 typedef struct Ctlr Ctlr;
@@ -164,7 +167,7 @@ static Ctlr* ctlrtail;
 
 enum {
 	/* registers (could memory map) */
-	Rcr=		0x00,	/* command register */
+	Rcr=	0x00,	/* command register */
 	  Rst=		1<<8,
 	  Rxr=		1<<5,	/* receiver reset */
 	  Txr=		1<<4,	/* transmitter reset */
@@ -173,7 +176,7 @@ enum {
 	  Txd=		1<<1,	/* transmitter disable */
 	  Txe=		1<<0,	/* transmitter enable */
 	Rcfg=	0x04,	/* configuration */
-	  Lnksts=		1<<31,	/* link good */
+	  Lnksts=	1<<31,	/* link good */
 	  Speed100=	1<<30,	/* 100 Mb/s link */
 	  Fdup=		1<<29,	/* full duplex */
 	  Pol=		1<<28,	/* polarity reversal (10baseT) */
@@ -183,74 +186,74 @@ enum {
 	  Paneg_ena=	1<<13,	/* auto negotiation enable */
 	  Paneg_all=	7<<13,	/* auto negotiation enable 10/100 half & full */
 	  Ext_phy=	1<<12,	/* enable MII for external PHY */
-	  Phy_rst=		1<<10,	/* reset internal PHY */
-	  Phy_dis=		1<<9,	/* disable internal PHY (eg, low power) */
+	  Phy_rst=	1<<10,	/* reset internal PHY */
+	  Phy_dis=	1<<9,	/* disable internal PHY (eg, low power) */
 	  Req_alg=	1<<7,	/* PCI bus request: set means less aggressive */
-	  Sb=			1<<6,	/* single slot back-off not random */
+	  Sb=		1<<6,	/* single slot back-off not random */
 	  Pow=		1<<5,	/* out of window timer selection */
 	  Exd=		1<<4,	/* disable excessive deferral timer */
-	  Pesel=		1<<3,	/* parity error algorithm selection */
+	  Pesel=	1<<3,	/* parity error algorithm selection */
 	  Brom_dis=	1<<2,	/* disable boot rom interface */
 	  Bem=		1<<0,	/* big-endian mode */
 	Rmear=	0x08,	/* eeprom access */
 	  Mdc=		1<<6,	/* MII mangement check */
-	  Mddir=		1<<5,	/* MII management direction */
+	  Mddir=	1<<5,	/* MII management direction */
 	  Mdio=		1<<4,	/* MII mangement data */
-	  Eesel=		1<<3,	/* EEPROM chip select */
-	  Eeclk=		1<<2,	/* EEPROM clock */
+	  Eesel=	1<<3,	/* EEPROM chip select */
+	  Eeclk=	1<<2,	/* EEPROM clock */
 	  Eedo=		1<<1,	/* EEPROM data out (from chip) */
 	  Eedi=		1<<0,	/* EEPROM data in (to chip) */
 	Rptscr=	0x0C,	/* pci test control */
-	Risr=		0x10,	/* interrupt status */
+	Risr=	0x10,	/* interrupt status */
 	  Txrcmp=	1<<25,	/* transmit reset complete */
 	  Rxrcmp=	1<<24,	/* receiver reset complete */
-	  Dperr=		1<<23,	/* detected parity error */
-	  Sserr=		1<<22,	/* signalled system error */
-	  Rmabt=		1<<21,	/* received master abort */
-	  Rtabt=		1<<20,	/* received target abort */
-	  Rxsovr=		1<<16,	/* RX status FIFO overrun */
-	  Hiberr=		1<<15,	/* high bits error set (OR of 25-16) */
+	  Dperr=	1<<23,	/* detected parity error */
+	  Sserr=	1<<22,	/* signalled system error */
+	  Rmabt=	1<<21,	/* received master abort */
+	  Rtabt=	1<<20,	/* received target abort */
+	  Rxsovr=	1<<16,	/* RX status FIFO overrun */
+	  Hiberr=	1<<15,	/* high bits error set (OR of 25-16) */
 	  Phy=		1<<14,	/* PHY interrupt */
 	  Pme=		1<<13,	/* power management event (wake online) */
 	  Swi=		1<<12,	/* software interrupt */
 	  Mib=		1<<11,	/* MIB service */
-	  Txurn=		1<<10,	/* TX underrun */
-	  Txidle=		1<<9,	/* TX idle */
-	  Txerr=		1<<8,	/* TX packet error */
-	  Txdesc=		1<<7,	/* TX descriptor (with Intr bit done) */
+	  Txurn=	1<<10,	/* TX underrun */
+	  Txidle=	1<<9,	/* TX idle */
+	  Txerr=	1<<8,	/* TX packet error */
+	  Txdesc=	1<<7,	/* TX descriptor (with Intr bit done) */
 	  Txok=		1<<6,	/* TX ok */
-	  Rxorn=		1<<5,	/* RX overrun */
-	  Rxidle=		1<<4,	/* RX idle */
-	  Rxearly=		1<<3,	/* RX early threshold */
-	  Rxerr=		1<<2,	/* RX packet error */
-	  Rxdesc=		1<<1,	/* RX descriptor (with Intr bit done) */
+	  Rxorn=	1<<5,	/* RX overrun */
+	  Rxidle=	1<<4,	/* RX idle */
+	  Rxearly=	1<<3,	/* RX early threshold */
+	  Rxerr=	1<<2,	/* RX packet error */
+	  Rxdesc=	1<<1,	/* RX descriptor (with Intr bit done) */
 	  Rxok=		1<<0,	/* RX ok */
 	Rimr=	0x14,	/* interrupt mask */
 	Rier=	0x18,	/* interrupt enable */
-	  Ie=			1<<0,	/* interrupt enable */
+	  Ie=		1<<0,	/* interrupt enable */
 	Rtxdp=	0x20,	/* transmit descriptor pointer */
 	Rtxcfg=	0x24,	/* transmit configuration */
 	  Csi=		1<<31,	/* carrier sense ignore (needed for full duplex) */
 	  Hbi=		1<<30,	/* heartbeat ignore (needed for full duplex) */
 	  Atp=		1<<28,	/* automatic padding of runt packets */
-	  Mxdma=		7<<20,	/* maximum dma transfer field */
+	  Mxdma=	7<<20,	/* maximum dma transfer field */
 	  Mxdma32=	4<<20,	/* 4x32-bit words (32 bytes) */
 	  Mxdma64=	5<<20,	/* 8x32-bit words (64 bytes) */
-	  Flth=		0x3F<<8,	/* Tx fill threshold, units of 32 bytes (must be > Mxdma) */
-	  Drth=		0x3F<<0,	/* Tx drain threshold (units of 32 bytes) */
-	  Flth128=		4<<8,	/* fill at 128 bytes */
-	// TODO: different on SiS?
+	  Flth=		0x3F<<8, /* Tx fill threshold, units of 32 bytes (must be > Mxdma) */
+	  Drth=		0x3F<<0, /* Tx drain threshold (units of 32 bytes) */
+	  Flth128=	4<<8,	/* fill at 128 bytes */
+	// seems to be the same on SiS 900; maybe use larger value @ 100Mb/s
 	  Drth512=	16<<0,	/* drain at 512 bytes */
 	Rrxdp=	0x30,	/* receive descriptor pointer */
 	Rrxcfg=	0x34,	/* receive configuration */
 	  Atx=		1<<28,	/* accept transmit packets (needed for full duplex) */
-	  Rdrth=		0x1F<<1,	/* Rx drain threshold (units of 32 bytes) */
+	  Rdrth=	0x1F<<1, /* Rx drain threshold (units of 32 bytes) */
 	  Rdrth64=	2<<1,	/* drain at 64 bytes */
 	Rccsr=	0x3C,	/* CLKRUN control/status */
-	  Pmests=		1<<15,	/* PME status */
+	  Pmests=	1<<15,	/* PME status */
 	Rwcsr=	0x40,	/* wake on lan control/status */
 	Rpcr=	0x44,	/* pause control/status */
-	// TODO: different on SiS, but does it matter?
+	// TODO: different on SiS, but does it matter? Rfen - Aau are same.
 	Rrfcr=	0x48,	/* receive filter/match control */
 	  Rfen=		1<<31,	/* receive filter enable */
 	  Aab=		1<<30,	/* accept all broadcast */
@@ -272,22 +275,22 @@ enum {
 
 	/* PHY registers */
 	Rbmcr=	0x80,	/* basic mode configuration */
-	  Reset=		1<<15,
-	  Sel100=		1<<13,	/* select 100Mb/sec if no auto neg */
-	  Anena=		1<<12,	/* auto negotiation enable */
+	  Reset=	1<<15,
+	  Sel100=	1<<13,	/* select 100Mb/sec if no auto neg */
+	  Anena=	1<<12,	/* auto negotiation enable */
 	  Anrestart=	1<<9,	/* restart auto negotiation */
-	  Selfdx=		1<<8,	/* select full duplex if no auto neg */
+	  Selfdx=	1<<8,	/* select full duplex if no auto neg */
 	Rbmsr=	0x84,	/* basic mode status */
 	  Ancomp=	1<<5,	/* autonegotiation complete */
 	Rphyidr1= 0x88,
 	Rphyidr2= 0x8C,
 	Ranar=	0x90,	/* autonegotiation advertisement */
-	Ranlpar=	0x94,	/* autonegotiation link partner ability */
+	Ranlpar=0x94,	/* autonegotiation link partner ability */
 	Raner=	0x98,	/* autonegotiation expansion */
-	Rannptr=	0x9C,	/* autonegotiation next page TX */
-	Rphysts=	0xC0,	/* PHY status */
+	Rannptr=0x9C,	/* autonegotiation next page TX */
+	Rphysts=0xC0,	/* PHY status */
 	Rmicr=	0xC4,	/* MII control */
-	  Inten=		1<<1,	/* PHY interrupt enable */
+	  Inten=	1<<1,	/* PHY interrupt enable */
 	Rmisr=	0xC8,	/* MII status */
 	Rfcscr=	0xD0,	/* false carrier sense counter */
 	Rrecr=	0xD4,	/* receive error counter */
@@ -331,6 +334,12 @@ promiscuous(void* arg, int on)
 	iunlock(&ctlr->lock);
 }
 
+/* multicast already on, don't need to do anything */
+static void
+multicast(void*, uchar*, int)
+{
+}
+
 static void
 attach(Ether* ether)
 {
@@ -427,11 +436,11 @@ txstart(Ether* ether)
 		des = &ctlr->tdr[ctlr->tdrh];
 		des->bp = bp;
 		des->addr = PADDR(bp->rp);
-		debug("83815: txstart: des->addr %lux\n", des->addr);
+		debug("ns83815: txstart: des->addr %lux\n", des->addr);
 		ctlr->ntq++;
 		coherence();
 		des->cmdsts = Own | BLEN(bp);
-		debug("83815: txstart: des->cmdsts %ux\n", des->cmdsts);
+		debug("ns83815: txstart: des->cmdsts %ux\n", des->cmdsts);
 		ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr);
 		started = 1;
 	}
@@ -532,14 +541,30 @@ interrupt(Ureg*, void* arg)
 				}
 				else if(bp = iallocb(Rbsz)){
 					len = (cmdsts&Size)-4;
-					SETWPCNT(des->bp, len);
-					ETHERIQ(ether, des->bp, 1);
+					if (len <= 0) {
+						print(
+				"ns83815: interrupt: packet len %d <= 0\n",
+							len);
+						freeb(des->bp);	/* toss it */
+					} else {
+						SETWPCNT(des->bp, len);
+						ETHERIQ(ether, des->bp, 1);
+					}
+					/* replace just-queued/freed packet */
 					des->bp = bp;
 					des->addr = PADDR(bp->rp);
 					debug(
-		"83815: interrupt: packet into input q, new des->addr %lux\n",
+		"ns83815: interrupt: packet into input q, new des->addr %lux\n",
 						des->addr);
 					coherence();
+				} else {
+					print(
+		"ns83815: interrupt: iallocb for input buffer failed\n");
+					/*
+					 * prevent accidents & ignore this
+					 * packet.  it will be overwritten.
+					 */
+					des->bp->next = 0;
 				}
 
 				des->cmdsts = Rbsz;
@@ -591,7 +616,7 @@ interrupt(Ureg*, void* arg)
 #endif
 			}
 
-			debug("83815: interrupt: done output for des->addr %lux\n",
+			debug("ns83815: interrupt: done output for des->addr %lux\n",
 				des->addr);
 			freeb(des->bp);
 			des->bp = nil;
@@ -628,14 +653,14 @@ ctlrinit(Ether* ether)
 	 */
 	ctlr->rdr = malloc(ctlr->nrdr*sizeof(Des));
 	if(ctlr->rdr == nil) {
-		print("83815: ctlrinit: iallocb of rcv. descs. failed\n");
+		print("ns83815: ctlrinit: iallocb of rcv. descs. failed\n");
 		return;
 	}
 	last = nil;
 	for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){
 		des->bp = iallocb(Rbsz);
 		if (des->bp == nil) {
-			print("83815: ctlrinit: iallocb(%d) failed\n", Rbsz);
+			print("ns83815: ctlrinit: iallocb(%d) failed\n", Rbsz);
 			return;
 		}
 		des->cmdsts = Rbsz;
@@ -669,7 +694,7 @@ ctlrinit(Ether* ether)
 		Txdesc|Txok|Rxorn|Rxerr|Rxdesc|Rxok);	/* Phy|Pme|Mib */
 	csr32r(ctlr, Risr);	/* clear status */
 	csr32w(ctlr, Rier, Ie);
-	debug("83815: ctlrinit: set Ie, done\n");
+	debug("ns83815: ctlrinit: set Ie, done\n");
 }
 
 static void
@@ -703,7 +728,7 @@ eegetw(Ctlr *ctlr, int a)
 	eeidle(ctlr);
 	eeclk(ctlr, 0);
 	eeclk(ctlr, Eeclk);
-	d = 0x180 | a;
+	d = 0x180 | a;			// read EEPROM at address `a'
 	for(i=0x400; i; i>>=1){
 		if(d & i)
 			csr32w(ctlr, Rmear, Eesel|Eedi);
@@ -726,13 +751,10 @@ eegetw(Ctlr *ctlr, int a)
 }
 
 static void
-softreset(Ctlr* ctlr, int resetphys)
+resetctlr(Ctlr *ctlr)
 {
-	int i, w;
+	int i;
 
-	/*
-	 * Soft-reset the controller
-	 */
 	csr32w(ctlr, Rcr, Rst);
 	for(i=0;; i++){
 		if(i > 100)
@@ -742,7 +764,27 @@ softreset(Ctlr* ctlr, int resetphys)
 			break;
 		delay(1);
 	}
+}
 
+static void
+shutdown(Ether* ether)
+{
+	Ctlr *ctlr = ether->ctlr;
+
+print("ether83815 shutting down\n");
+	csr32w(ctlr, Rcr, Rxd|Txd);	/* disable transceiver */
+	resetctlr(ctlr);
+}
+
+static void
+softreset(Ctlr* ctlr, int resetphys)
+{
+	int i, w;
+
+	/*
+	 * Soft-reset the controller
+	 */
+	resetctlr(ctlr);
 	csr32w(ctlr, Rccsr, Pmests);
 	csr32w(ctlr, Rccsr, 0);
 	csr32w(ctlr, Rcfg, csr32r(ctlr, Rcfg) | Pint_acen);
@@ -762,7 +804,8 @@ softreset(Ctlr* ctlr, int resetphys)
 	}
 
 	/*
-	 * Initialisation values, in sequence (see 4.4 Recommended Registers Configuration)
+	 * Initialisation values, in sequence
+	 * (see 4.4 Recommended Registers Configuration)
 	 */
 	csr16w(ctlr, 0xCC, 0x0001);	/* PGSEL */
 	csr16w(ctlr, 0xE4, 0x189C);	/* PMCCSR */
@@ -780,7 +823,7 @@ softreset(Ctlr* ctlr, int resetphys)
 		csr16w(ctlr, Rbmcr, Anena|Anrestart);
 		for(i=0;; i++){
 			if(i > 6000){
-				print("ns83815: auto neg timed out\n");
+				print("ns83815: speed auto neg. timed out\n");
 				break;
 			}
 			if((w = csr16r(ctlr, Rbmsr)) & Ancomp)
@@ -830,38 +873,56 @@ static char* mediatable[9] = {
 enum {
 	MagicReg = 0x48,
 	MagicRegSz = 1,
+	Magicrden = 0x40,	/* read enable, apparently */
+	Paddr=		0x70,	/* address port */
+	Pdata=		0x71,	/* data port */
 };
 
-static void
+/* rcmos() originally from LANL's SiS 900 driver's rcmos() */
+static int
 sisrdcmos(Ctlr *ctlr)
 {
-	union {
-		uchar	eaddr[Eaddrlen];
-		ushort	alignment;
-	} ee;
-	ushort *shp = (ushort *)ee.eaddr;
-	int off = 9, cnt = sizeof ee.eaddr;
+	int i;
+	unsigned reg;
+	ulong port;
+	Pcidev *p;
+
+	print("ns83815: SiS 630 rev. %ux reading mac address from cmos\n",
+		ctlr->pcidev->rid);
+	p = pcimatch(nil, SiS, SiS630bridge);
+	if (p == nil) {
+		print("ns83815: no SiS 630 rev. %ux bridge for mac addr\n",
+			ctlr->pcidev->rid);
+		return 0;
+	}
+	port = p->mem[0].bar & ~0x01;
+	print(
+"ns83815: SiS 630 rev. %ux reading mac addr from cmos via bridge at port 0x%lux\n",
+		ctlr->pcidev->rid, port);
 
-	print("ns83815: sis: reading mac address from cmos (unimplemented)!\n");
-	memset(ctlr->sromea, 0, sizeof ctlr->sromea);
-#ifdef notdef
-	// TODO: make this code from freebsd work to read mac addr from cmos
-	int i, reg, btag = 0;
-	void *bridge = (void *)sis_find_bridge(0 /* dev */);
+	reg = pcicfgr8(p, MagicReg);
+	pcicfgw8(p, MagicReg, reg|Magicrden);
 
-	if (bridge == nil)
-		return;
-	reg = pci_read_config(bridge, MagicReg, MagicRegSz);
-	pci_write_config(bridge, MagicReg, reg|0x40, MagicRegSz);
-	// btag = I386_BUS_SPACE_IO;
-	for (i = 0; i < cnt; i++) {
-		bus_space_write_1(btag, 0x0, 0x70, i + off);
-		dest[i] = bus_space_read_1(btag, 0x0, 0x71);
+	for (i = 0; i < Eaddrlen; i++) {
+		outb(port+Paddr, SiS630eenodeaddr + i);
+		ctlr->sromea[i] = inb(port+Pdata);
 	}
-	pci_write_config(bridge, MagicReg, reg & ~0x40, MagicRegSz);
-#endif
-	USED(off, cnt, shp);
-	memmove(ctlr->sromea, ee.eaddr, sizeof ctlr->sromea);
+
+	pcicfgw8(p, MagicReg, reg & ~Magicrden);
+	return 1;
+}
+
+static int
+is630(ulong id, Pcidev *p)
+{
+	if (id == SiS900)
+		switch (p->rid) {
+		case SiSrev630s:
+		case SiSrev630e:
+	  	case SiSrev630ea1:
+			return 1;
+		}
+	return 0;
 }
 
 /*
@@ -879,21 +940,12 @@ sissrom(Ctlr *ctlr)
 	int i, off = SiSeenodeaddr, cnt = sizeof ee.eaddr / sizeof(short);
 	ushort *shp = (ushort *)ee.eaddr;
 
-	// TODO: try ignoring this; maybe eeprom has the mac address anyway
-#ifndef FS
-	if (0 && ctlr->id == SiS900)
-		switch (ctlr->pcidev->rid) {
-		case SiSrev630s:
-		case SiSrev630e:
-	  	case SiSrev630ea1:
-			sisrdcmos(ctlr);
-			return;
-		}
-#endif
-	print("ns83815: sis: reading mac address from eeprom\n");
-	for (i = 0; i < cnt; i++)
-		*shp++ = eegetw(ctlr, off + i);
-	memmove(ctlr->sromea, ee.eaddr, sizeof ctlr->sromea);
+	if (!is630(ctlr->id, ctlr->pcidev) || !sisrdcmos(ctlr)) {
+		print("ns83815: reading SiS mac address from eeprom\n");
+		for (i = 0; i < cnt; i++)
+			*shp++ = eegetw(ctlr, off++);
+		memmove(ctlr->sromea, ee.eaddr, sizeof ctlr->sromea);
+	}
 }
 
 static void
@@ -908,7 +960,6 @@ nssrom(Ctlr *ctlr)
 	/*
 	 * the MAC address is reversed, straddling word boundaries
 	 */
-	memset(ctlr->sromea, 0, sizeof(ctlr->sromea));
 	j = Nseenodeaddr*16 + 15;		// bit offset to read
 	for (i=0; i<48; i++, j++)
 		ctlr->sromea[i>>3] |=
@@ -918,6 +969,7 @@ nssrom(Ctlr *ctlr)
 static void
 srom(Ctlr* ctlr)
 {
+	memset(ctlr->sromea, 0, sizeof(ctlr->sromea));
 	switch (ctlr->id) {
 	case SiS900:
 	case SiS7016:
@@ -954,11 +1006,8 @@ scanpci83815(void)
 	Pcidev *p = nil;
 
 	while(p = pcimatch(p, 0, 0)){
-#ifdef FS
-		if (p->ccru != ((Pcinetctlr<<8)|0x00))
-#else
-		if (p->ccrb != Pcinetctlr || p->ccru != 0)
-#endif
+		/* ccru is a short in the FS kernel, thus the cast to uchar */
+		if (p->ccrb != Pcinetctlr || (uchar)p->ccru != 0)
 			continue;		// not a nic
 		id = (p->did<<16)|p->vid;
 		// print("ns83815: id 0x%lux on pci bus\n", id);
@@ -967,7 +1016,10 @@ scanpci83815(void)
 			print("ns83815: FA31[12] found\n");
 			break;
 		case SiS900:
-			print("ns83815: SiS900 found\n");
+			print("ns83815: SiS900");
+			if (is630(id, p))
+				print(" (within SiS630)");
+			print(" found\n");
 			break;
 		case SiS7016:
 			print("ns83815: SiS7016 found\n");
@@ -1058,7 +1110,7 @@ dp83815reset(Ether* ether)
 
 	/*
 	 * Look for a medium override in case there's no autonegotiation
-	 * the autonegotiation fails.
+	 * or the autonegotiation fails.
 	 */
 
 	for(i = 0; i < ether->nopt; i++){
@@ -1107,9 +1159,11 @@ dp83815reset(Ether* ether)
 #ifndef FS
 	ether->ifstat = ifstat;
 	ether->arg = ether;
+	ether->multicast = multicast;
+	ether->shutdown = shutdown;
 	ether->promiscuous = promiscuous;
 #endif
-	debug("83815: dp83815reset: done\n");
+	debug("ns83815: dp83815reset: done\n");
 	return 0;
 }
 

+ 1363 - 0
sys/src/fs/pc/ether83815.mii.c

@@ -0,0 +1,1363 @@
+/*
+ * National Semiconductor DP83815
+ *
+ * Supports only internal PHY and has been tested on:
+ *	Netgear FA311TX (using Netgear DS108 10/100 hub, and using switches)
+ *	SiS 900 within SiS 630 (works under light load only)
+ * To do:
+ *	check Ethernet address;
+ *	test autonegotiation on 10 Mbit, and 100 Mbit full duplex;
+ *	thresholds;
+ *	ring sizing;
+ *	push initialisation back to attach.
+ *
+ * C H Forsyth, forsyth@vitanuova.com, 18th June 2001.
+ * made to drive the SiS 900, including using common MII/PHY code,
+ *	by Geoff Collyer, March 2003.
+ */
+
+#ifdef FS
+#include "all.h"
+#include "io.h"
+#include "mem.h"
+#include "../ip/ip.h"
+#else				/* FS */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+#endif				/* FS */
+
+#include "etherif.h"
+#include "ethermii.h"
+
+#include "compat.h"
+
+enum {
+	Debug		= 0,
+	Nrde		= 64,
+	Ntde		= 64,
+	Rbsz		= ROUNDUP(sizeof(Etherpkt)+4, 4),
+};
+
+#define debug		if(Debug)print
+
+typedef struct Des {
+	ulong	next;
+	int	cmdsts;
+	ulong	addr;
+	Block*	bp;
+} Des;
+
+enum {	/* cmdsts */
+	Own		= 1<<31,	/* set by data producer to hand to consumer */
+	More	= 1<<30,	/* more of packet in next descriptor */
+	Intr		= 1<<29,	/* interrupt when device is done with it */
+	Supcrc	= 1<<28,	/* suppress crc on transmit */
+	Inccrc	= 1<<28,	/* crc included on receive (always) */
+	Ok		= 1<<27,	/* packet ok */
+	Size		= 0xFFF,	/* packet size in bytes */
+
+	/* transmit */
+	Txa	= 1<<26,	/* transmission aborted */
+	Tfu	= 1<<25,	/* transmit fifo underrun */
+	Crs	= 1<<24,	/* carrier sense lost */
+	Td	= 1<<23,	/* transmission deferred */
+	Ed	= 1<<22,	/* excessive deferral */
+	Owc	= 1<<21,	/* out of window collision */
+	Ec	= 1<<20,	/* excessive collisions */
+	/* 19-16 collision count */
+
+	/* receive */
+	Rxa	= 1<<26,	/* receive aborted (same as Rxo) */
+	Rxo	= 1<<25,	/* receive overrun */
+	Dest	= 3<<23,	/* destination class */
+	  Drej=	0<<23,		/* packet was rejected */
+	  Duni=	1<<23,		/* unicast */
+	  Dmulti=	2<<23,		/* multicast */
+	  Dbroad=	3<<23,		/* broadcast */
+	Long = 1<<22,	/* too long packet received */
+	Runt =  1<<21,	/* packet less than 64 bytes */
+	Ise =	1<<20,	/* invalid symbol */
+	Crce =	1<<19,	/* invalid crc */
+	Fae =	1<<18,	/* frame alignment error */
+	Lbp =	1<<17,	/* loopback packet */
+	Col =	1<<16,	/* collision during receive */
+};
+
+enum {
+	// PCI vendor & device IDs
+	NatSemi		= 0x100B,
+	Nat83815	=   (0x20<<16)|NatSemi,
+	SiS		= 0x1039,
+	SiS900		=  (0x900<<16)|SiS,
+	SiS7016		= (0x7016<<16)|SiS,
+	SiS630bridge	= 0x0008,
+
+	// SiS 900 PCI revision codes
+	SiSrev630s =	0x81,
+	SiSrev630e =	0x82,
+	SiSrev630ea1 =	0x83,
+
+	SiSeenodeaddr = 8,		// short addr of SiS eeprom mac addr
+	SiS630eenodeaddr = 9,		// likewise for the 630
+	Nseenodeaddr = 6,		// " for NS eeprom
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	int	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+	int	id;			/* (pcidev->did<<16)|pcidev->vid */
+
+	ushort	srom[0xB+1];
+	uchar	sromea[Eaddrlen];			/* MAC address */
+
+	uchar	fd;			/* option or auto negotiation */
+
+	int	mbps;
+
+	Lock	lock;
+	Mii*	mii;
+
+	Des*	rdr;			/* receive descriptor ring */
+	int	nrdr;			/* size of rdr */
+	int	rdrx;			/* index into rdr */
+
+	Lock	tlock;
+	Des*	tdr;			/* transmit descriptor ring */
+	int	ntdr;			/* size of tdr */
+	int	tdrh;			/* host index into tdr */
+	int	tdri;			/* interface index into tdr */
+	int	ntq;			/* descriptors active */
+	int	ntqmax;
+
+	ulong	rxa;			/* receive statistics */
+	ulong	rxo;
+	ulong	rlong;
+	ulong	runt;
+	ulong	ise;
+	ulong	crce;
+	ulong	fae;
+	ulong	lbp;
+	ulong	col;
+	ulong	rxsovr;
+	ulong	rxorn;
+
+	ulong	txa;			/* transmit statistics */
+	ulong	tfu;
+	ulong	crs;
+	ulong	td;
+	ulong	ed;
+	ulong	owc;
+	ulong	ec;
+	ulong	txurn;
+
+	ulong	dperr;		/* system errors */
+	ulong	rmabt;
+	ulong	rtabt;
+	ulong	sserr;
+	ulong	rxsover;
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+enum {
+	/* registers (could memory map) */
+	Rcr=		0x00,	/* command register */
+	  Rst=		1<<8,
+	  Rxr=		1<<5,	/* receiver reset */
+	  Txr=		1<<4,	/* transmitter reset */
+	  Rxd=		1<<3,	/* receiver disable */
+	  Rxe=		1<<2,	/* receiver enable */
+	  Txd=		1<<1,	/* transmitter disable */
+	  Txe=		1<<0,	/* transmitter enable */
+	Rcfg=	0x04,	/* configuration */
+	  Lnksts=		1<<31,	/* link good */
+	  Speed100=	1<<30,	/* 100 Mb/s link */
+	  Fdup=		1<<29,	/* full duplex */
+	  Pol=		1<<28,	/* polarity reversal (10baseT) */
+	  Aneg_dn=	1<<27,	/* autonegotiation done */
+	  Pint_acen=	1<<17,	/* PHY interrupt auto clear enable */
+	  Pause_adv=	1<<16,	/* advertise pause during auto neg */
+	  Paneg_ena=	1<<13,	/* auto negotiation enable */
+	  Paneg_all=	7<<13,	/* auto negotiation enable 10/100 half & full */
+	  Ext_phy=	1<<12,	/* enable MII for external PHY */
+	  Phy_rst=		1<<10,	/* reset internal PHY */
+	  Phy_dis=		1<<9,	/* disable internal PHY (eg, low power) */
+	  Req_alg=	1<<7,	/* PCI bus request: set means less aggressive */
+	  Sb=			1<<6,	/* single slot back-off not random */
+	  Pow=		1<<5,	/* out of window timer selection */
+	  Exd=		1<<4,	/* disable excessive deferral timer */
+	  Pesel=		1<<3,	/* parity error algorithm selection */
+	  Brom_dis=	1<<2,	/* disable boot rom interface */
+	  Bem=		1<<0,	/* big-endian mode */
+	Rmear=	0x08,	/* eeprom access */
+	  Mdc=		1<<6,	/* MII mangement check */
+	  Mddir=		1<<5,	/* MII management direction */
+	  Mdio=		1<<4,	/* MII mangement data */
+	  Eesel=		1<<3,	/* EEPROM chip select */
+	  Eeclk=		1<<2,	/* EEPROM clock */
+	  Eedo=		1<<1,	/* EEPROM data out (from chip) */
+	  Eedi=		1<<0,	/* EEPROM data in (to chip) */
+	Rptscr=	0x0C,	/* pci test control */
+	Risr=		0x10,	/* interrupt status */
+	  Txrcmp=	1<<25,	/* transmit reset complete */
+	  Rxrcmp=	1<<24,	/* receiver reset complete */
+	  Dperr=		1<<23,	/* detected parity error */
+	  Sserr=		1<<22,	/* signalled system error */
+	  Rmabt=		1<<21,	/* received master abort */
+	  Rtabt=		1<<20,	/* received target abort */
+	  Rxsovr=		1<<16,	/* RX status FIFO overrun */
+	  Hiberr=		1<<15,	/* high bits error set (OR of 25-16) */
+	  Phy=		1<<14,	/* PHY interrupt */
+	  Pme=		1<<13,	/* power management event (wake online) */
+	  Swi=		1<<12,	/* software interrupt */
+	  Mib=		1<<11,	/* MIB service */
+	  Txurn=		1<<10,	/* TX underrun */
+	  Txidle=		1<<9,	/* TX idle */
+	  Txerr=		1<<8,	/* TX packet error */
+	  Txdesc=		1<<7,	/* TX descriptor (with Intr bit done) */
+	  Txok=		1<<6,	/* TX ok */
+	  Rxorn=		1<<5,	/* RX overrun */
+	  Rxidle=		1<<4,	/* RX idle */
+	  Rxearly=		1<<3,	/* RX early threshold */
+	  Rxerr=		1<<2,	/* RX packet error */
+	  Rxdesc=		1<<1,	/* RX descriptor (with Intr bit done) */
+	  Rxok=		1<<0,	/* RX ok */
+	Rimr=	0x14,	/* interrupt mask */
+	Rier=	0x18,	/* interrupt enable */
+	  Ie=			1<<0,	/* interrupt enable */
+	Rtxdp=	0x20,	/* transmit descriptor pointer */
+	Rtxcfg=	0x24,	/* transmit configuration */
+	  Csi=		1<<31,	/* carrier sense ignore (needed for full duplex) */
+	  Hbi=		1<<30,	/* heartbeat ignore (needed for full duplex) */
+	  Atp=		1<<28,	/* automatic padding of runt packets */
+	  Mxdma=		7<<20,	/* maximum dma transfer field */
+	  Mxdma32=	4<<20,	/* 4x32-bit words (32 bytes) */
+	  Mxdma64=	5<<20,	/* 8x32-bit words (64 bytes) */
+	  Flth=		0x3F<<8,	/* Tx fill threshold, units of 32 bytes (must be > Mxdma) */
+	  Drth=		0x3F<<0,	/* Tx drain threshold (units of 32 bytes) */
+	  Flth128=		4<<8,	/* fill at 128 bytes */
+	// seems to be the same on SiS 900; maybe use larger value @ 100Mb/s
+	  Drth512=	16<<0,	/* drain at 512 bytes */
+	Rrxdp=	0x30,	/* receive descriptor pointer */
+	Rrxcfg=	0x34,	/* receive configuration */
+	  Atx=		1<<28,	/* accept transmit packets (needed for full duplex) */
+	  Rdrth=		0x1F<<1,	/* Rx drain threshold (units of 32 bytes) */
+	  Rdrth64=	2<<1,	/* drain at 64 bytes */
+	Rccsr=	0x3C,	/* CLKRUN control/status */
+	  Pmests=		1<<15,	/* PME status */
+	Rwcsr=	0x40,	/* wake on lan control/status */
+	Rpcr=	0x44,	/* pause control/status */
+	// TODO: different on SiS, but does it matter?
+	Rrfcr=	0x48,	/* receive filter/match control */
+	  Rfen=		1<<31,	/* receive filter enable */
+	  Aab=		1<<30,	/* accept all broadcast */
+	  Aam=		1<<29,	/* accept all multicast */
+	  Aau=		1<<28,	/* accept all unicast */
+	  Apm=		1<<27,	/* accept on perfect match */
+	  Apat=		0xF<<23,	/* accept on pattern match */
+	  Aarp=		1<<22,	/* accept ARP */
+	  Mhen=		1<<21,	/* multicast hash enable */
+	  Uhen=		1<<20,	/* unicast hash enable */
+	  Ulm=		1<<19,	/* U/L bit mask */
+						/* bits 0-9 are rfaddr */
+	Rrfdr=	0x4C,	/* receive filter/match data */
+	Rbrar=	0x50,	/* boot rom address */
+	Rbrdr=	0x54,	/* boot rom data */
+	Rsrr=	0x58,	/* silicon revision */
+	Rmibc=	0x5C,	/* MIB control */
+					/* 60-78 MIB data */
+
+	/* PHY registers */
+	Rbmcr=	0x80,	/* basic mode configuration */
+	  Reset=		1<<15,
+	  Sel100=		1<<13,	/* select 100Mb/sec if no auto neg */
+	  Anena=		1<<12,	/* auto negotiation enable */
+	  Anrestart=	1<<9,	/* restart auto negotiation */
+	  Selfdx=		1<<8,	/* select full duplex if no auto neg */
+	Rbmsr=	0x84,	/* basic mode status */
+	  Ancomp=	1<<5,	/* autonegotiation complete */
+	Rphyidr1= 0x88,
+	Rphyidr2= 0x8C,
+	Ranar=	0x90,	/* autonegotiation advertisement */
+	Ranlpar=	0x94,	/* autonegotiation link partner ability */
+	Raner=	0x98,	/* autonegotiation expansion */
+	Rannptr=	0x9C,	/* autonegotiation next page TX */
+	Rphysts=	0xC0,	/* PHY status */
+	Rmicr=	0xC4,	/* MII control */
+	  Inten=		1<<1,	/* PHY interrupt enable */
+	Rmisr=	0xC8,	/* MII status */
+	Rfcscr=	0xD0,	/* false carrier sense counter */
+	Rrecr=	0xD4,	/* receive error counter */
+	Rpcsr=	0xD8,	/* 100Mb config/status */
+	Rphycr=	0xE4,	/* PHY control */
+	Rtbscr=	0xE8,	/* 10BaseT status/control */
+};
+
+/*
+ * eeprom addresses
+ * 	7 to 9 (16 bit words): mac address, shifted and reversed
+ */
+
+#define csr32r(c, r)	(inl((c)->port+(r)))
+#define csr32w(c, r, l)	(outl((c)->port+(r), (ulong)(l)))
+#define csr16r(c, r)	(ins((c)->port+(r)))
+#define csr16w(c, r, l)	(outs((c)->port+(r), (ulong)(l)))
+
+static void
+dumpcregs(Ctlr *ctlr)
+{
+	int i;
+
+	for(i=0; i<=0x5C; i+=4)
+		print("%2.2ux %8.8lux\n", i, csr32r(ctlr, i));
+}
+
+// TODO: finish this
+static void
+promiscuous(void* arg, int on)
+{
+	Ctlr *ctlr;
+	ulong w;
+
+	ctlr = ((Ether*)arg)->ctlr;
+	ilock(&ctlr->lock);
+	w = csr32r(ctlr, Rrfcr);
+	if(on != ((w&Aau)!=0)){
+		csr32w(ctlr, Rrfcr, w & ~Rfen);
+		csr32w(ctlr, Rrfcr, Rfen | (w ^ Aau));
+	}
+	iunlock(&ctlr->lock);
+}
+
+static void
+attach(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(&ctlr->lock);
+	if(0)
+		dumpcregs(ctlr);
+	csr32w(ctlr, Rcr, Rxe);
+	iunlock(&ctlr->lock);
+}
+
+#ifndef FS
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+	Ctlr *ctlr;
+	char *buf, *p;
+	int i, l, len;
+
+	ctlr = ether->ctlr;
+
+	ether->crcs = ctlr->crce;
+	ether->frames = ctlr->runt+ctlr->ise+ctlr->rlong+ctlr->fae;
+	ether->buffs = ctlr->rxorn+ctlr->tfu;
+	ether->overflows = ctlr->rxsovr;
+
+	if(n == 0)
+		return 0;
+
+	p = malloc(READSTR);
+	l = snprint(p, READSTR, "Rxa: %lud\n", ctlr->rxa);
+	l += snprint(p+l, READSTR-l, "Rxo: %lud\n", ctlr->rxo);
+	l += snprint(p+l, READSTR-l, "Rlong: %lud\n", ctlr->rlong);
+	l += snprint(p+l, READSTR-l, "Runt: %lud\n", ctlr->runt);
+	l += snprint(p+l, READSTR-l, "Ise: %lud\n", ctlr->ise);
+	l += snprint(p+l, READSTR-l, "Fae: %lud\n", ctlr->fae);
+	l += snprint(p+l, READSTR-l, "Lbp: %lud\n", ctlr->lbp);
+	l += snprint(p+l, READSTR-l, "Tfu: %lud\n", ctlr->tfu);
+	l += snprint(p+l, READSTR-l, "Txa: %lud\n", ctlr->txa);
+	l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->crce);
+	l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->col);
+	l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->rlong);
+	l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->runt);
+	l += snprint(p+l, READSTR-l, "Rx Underflow Error: %lud\n", ctlr->rxorn);
+	l += snprint(p+l, READSTR-l, "Tx Underrun: %lud\n", ctlr->txurn);
+	l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec);
+	l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->owc);
+	l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->crs);
+	l += snprint(p+l, READSTR-l, "Parity: %lud\n", ctlr->dperr);
+	l += snprint(p+l, READSTR-l, "Aborts: %lud\n", ctlr->rmabt+ctlr->rtabt);
+	l += snprint(p+l, READSTR-l, "RX Status overrun: %lud\n", ctlr->rxsover);
+	snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax);
+	ctlr->ntqmax = 0;
+	buf = a;
+	len = readstr(offset, buf, n, p);
+	if(offset > l)
+		offset -= l;
+	else
+		offset = 0;
+	buf += len;
+	n -= len;
+
+	l = snprint(p, READSTR, "srom:");
+	for(i = 0; i < nelem(ctlr->srom); i++){
+		if(i && ((i & 0x0F) == 0))
+			l += snprint(p+l, READSTR-l, "\n     ");
+		l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->srom[i]);
+	}
+
+	snprint(p+l, READSTR-l, "\n");
+	len += readstr(offset, buf, n, p);
+	free(p);
+
+	return len;
+}
+#endif
+
+/* always called under ilock(&ether->ctlr->tlock) */
+static void
+txstart(Ether* ether)
+{
+	Ctlr *ctlr;
+	Block *bp;
+	Des *des;
+	int started;
+
+	ctlr = ether->ctlr;
+	started = 0;
+	while(ctlr->ntq < ctlr->ntdr-1){
+		bp = etheroq(ether);
+		if(bp == nil)
+			break;
+		des = &ctlr->tdr[ctlr->tdrh];
+		des->bp = bp;
+		des->addr = PADDR(bp->rp);
+		// debug("ns83815: txstart: des->addr %lux\n", des->addr);
+		ctlr->ntq++;
+		coherence();
+		des->cmdsts = Own | BLEN(bp);
+		// debug("ns83815: txstart: des->cmdsts %ux\n", des->cmdsts);
+		ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr);
+		started = 1;
+	}
+	if(started){
+		coherence();
+		csr32w(ctlr, Rcr, Txe);	/* prompt */
+	}
+
+	if(ctlr->ntq > ctlr->ntqmax)
+		ctlr->ntqmax = ctlr->ntq;
+}
+
+static void
+transmit(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(&ctlr->tlock);
+	txstart(ether);
+	iunlock(&ctlr->tlock);
+}
+
+// TODO: finish this; set speed too
+static void
+txrxcfg(Ctlr *ctlr, int txdrth)
+{
+	ulong rx, tx;
+
+	rx = csr32r(ctlr, Rrxcfg);
+	tx = csr32r(ctlr, Rtxcfg);
+	if(ctlr->fd){
+		rx |= Atx;
+		tx |= Csi | Hbi;
+	}else{
+		rx &= ~Atx;
+		tx &= ~(Csi | Hbi);
+	}
+	tx &= ~(Mxdma|Drth|Flth);
+	tx |= Mxdma64 | Flth128 | txdrth;
+	csr32w(ctlr, Rtxcfg, tx);
+	rx &= ~(Mxdma|Rdrth);
+	rx |= Mxdma64 | Rdrth64;
+	csr32w(ctlr, Rrxcfg, rx);
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+	Ctlr *ctlr;
+	Ether *ether;
+	int len, status, cmdsts, iter = 0;
+	Des *des;
+	Block *bp;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+	while((status = csr32r(ctlr, Risr)) != 0){
+		if (iter++ >= 100)
+			print("ns83815: interrupt: >= 100 iterations\n");
+		status &= ~(Pme|Mib);
+		if(status & Hiberr){
+			if(status & Rxsovr)
+				ctlr->rxsover++;
+			if(status & Sserr)
+				ctlr->sserr++;
+			if(status & Dperr)
+				ctlr->dperr++;
+			if(status & Rmabt)
+				ctlr->rmabt++;
+			if(status & Rtabt)
+				ctlr->rtabt++;
+			status &= ~(Hiberr|Txrcmp|Rxrcmp|Rxsovr|Dperr|Sserr|Rmabt|Rtabt);
+		}
+
+		/*
+		 * Received packets.
+		 */
+		if(status & (Rxdesc|Rxok|Rxerr|Rxearly|Rxorn)){
+			des = &ctlr->rdr[ctlr->rdrx];
+			while((cmdsts = des->cmdsts) & Own){
+				if((cmdsts&Ok) == 0){
+					if(cmdsts & Rxa)
+						ctlr->rxa++;
+					if(cmdsts & Rxo)
+						ctlr->rxo++;
+					if(cmdsts & Long)
+						ctlr->rlong++;
+					if(cmdsts & Runt)
+						ctlr->runt++;
+					if(cmdsts & Ise)
+						ctlr->ise++;
+					if(cmdsts & Crce)
+						ctlr->crce++;
+					if(cmdsts & Fae)
+						ctlr->fae++;
+					if(cmdsts & Lbp)
+						ctlr->lbp++;
+					if(cmdsts & Col)
+						ctlr->col++;
+				}
+				else if(bp = iallocb(Rbsz)){
+					len = (cmdsts&Size)-4;
+					if (len <= 0) {
+						print(
+				"ns83815: interrupt: packet len %d <= 0\n",
+							len);
+						freeb(des->bp);	/* toss it */
+					} else {
+						SETWPCNT(des->bp, len);
+						ETHERIQ(ether, des->bp, 1);
+					}
+					/* replace just-queued/freed packet */
+					des->bp = bp;
+					des->addr = PADDR(bp->rp);
+//					debug(
+//		"ns83815: interrupt: packet into input q, new des->addr %lux\n",
+//						des->addr);
+					coherence();
+				} else {
+					print(
+		"ns83815: interrupt: iallocb for input buffer failed\n");
+					/*
+					 * prevent accidents & ignore this
+					 * packet.  it will be overwritten.
+					 */
+					des->bp->next = 0;
+				}
+
+				des->cmdsts = Rbsz;
+				coherence();
+
+				ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr);
+				des = &ctlr->rdr[ctlr->rdrx];
+			}
+			status &= ~(Rxdesc|Rxok|Rxerr|Rxearly|Rxorn);
+		}
+
+		/*
+		 * Check the transmit side:
+		 *	check for Transmit Underflow and Adjust
+		 *	the threshold upwards;
+		 *	free any transmitted buffers and try to
+		 *	top-up the ring.
+		 */
+		if(status & Txurn){
+			ctlr->txurn++;
+			ilock(&ctlr->lock);
+			/* change threshold */
+			iunlock(&ctlr->lock);
+			status &= ~(Txurn);
+		}
+
+		ilock(&ctlr->tlock);
+		while(ctlr->ntq > 0){
+			des = &ctlr->tdr[ctlr->tdri];
+			cmdsts = des->cmdsts;
+			if(cmdsts & Own)
+				break;
+
+			if((cmdsts & Ok) == 0){
+				if(cmdsts & Txa)
+					ctlr->txa++;
+				if(cmdsts & Tfu)
+					ctlr->tfu++;
+				if(cmdsts & Td)
+					ctlr->td++;
+				if(cmdsts & Ed)
+					ctlr->ed++;
+				if(cmdsts & Owc)
+					ctlr->owc++;
+				if(cmdsts & Ec)
+					ctlr->ec++;
+#ifndef FS
+				ether->oerrs++;
+#endif
+			}
+
+//			debug("ns83815: interrupt: done output for des->addr %lux\n",
+//				des->addr);
+			if (des->bp == nil)
+				panic("83815 interrupt: nil des->bp");
+			freeb(des->bp);
+			des->bp = nil;
+			des->cmdsts = 0;
+
+			ctlr->ntq--;
+			ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr);
+		}
+		txstart(ether);
+		iunlock(&ctlr->tlock);
+
+		if((status & Phy) && ctlr->mii != nil){
+			ctlr->mii->mir(ctlr->mii, 1, Bmsr);
+			print("phy: cfg %8.8lux bmsr %4.4ux\n",
+				csr32r(ctlr, Rcfg),
+				ctlr->mii->mir(ctlr->mii, 1, Bmsr));
+			/* TODO: set speed too; can't do it yet */
+			txrxcfg(ctlr, Drth512);	/* set duplicity */
+			status &= ~Phy;
+		}
+
+		status &= ~(Txurn|Txidle|Txerr|Txdesc|Txok);
+
+		/*
+		 * Anything left not catered for?
+		 */
+		if(status)
+			print("#l%d: status %8.8ux\n", ether->ctlrno, status);
+	}
+}
+
+static void
+ctlrinit(Ether* ether)
+{
+	Ctlr *ctlr;
+	Des *des, *last;
+
+	ctlr = ether->ctlr;
+
+	/*
+	 * Allocate and initialise the receive ring;
+	 * allocate and initialise the transmit ring;
+	 * unmask interrupts and start the transmit side
+	 */
+	ctlr->rdr = malloc(ctlr->nrdr*sizeof(Des));
+	if(ctlr->rdr == nil) {
+		print("ns83815: ctlrinit: iallocb of rcv. descs. failed\n");
+		return;
+	}
+	last = nil;
+	for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){
+		des->bp = iallocb(Rbsz);
+		if (des->bp == nil) {
+			print("ns83815: ctlrinit: iallocb(%d) failed\n", Rbsz);
+			return;
+		}
+		des->cmdsts = Rbsz;
+		des->addr = PADDR(des->bp->rp);
+		if(last != nil)
+			last->next = PADDR(des);
+		last = des;
+	}
+	ctlr->rdr[ctlr->nrdr-1].next = PADDR(ctlr->rdr);
+	ctlr->rdrx = 0;
+	csr32w(ctlr, Rrxdp, PADDR(ctlr->rdr));
+
+	ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0);
+	last = nil;
+	for(des = ctlr->tdr; des < &ctlr->tdr[ctlr->ntdr]; des++){
+		des->cmdsts = 0;
+		des->bp = nil;
+		des->addr = ~0;
+		if(last != nil)
+			last->next = PADDR(des);
+		last = des;
+	}
+	ctlr->tdr[ctlr->ntdr-1].next = PADDR(ctlr->tdr);
+	ctlr->tdrh = 0;
+	ctlr->tdri = 0;
+	csr32w(ctlr, Rtxdp, PADDR(ctlr->tdr));
+
+	txrxcfg(ctlr, Drth512);
+
+// TODO: finish this
+// MII
+	csr32w(ctlr, Rimr, Dperr|Sserr|Rmabt|Rtabt|Rxsovr|Hiberr|Txurn|Txerr|
+		Txdesc|Txok|Rxorn|Rxerr|Rxdesc|Rxok);	/* Phy|Pme|Mib */
+	csr32r(ctlr, Risr);	/* clear status */
+	csr32w(ctlr, Rier, Ie);
+	debug("ns83815: ctlrinit: set Ie, done\n");
+}
+
+/*
+ * EEPROM
+ */
+
+static void
+eeclk(Ctlr *ctlr, int clk)
+{
+	csr32w(ctlr, Rmear, Eesel | clk);
+	microdelay(2);
+}
+
+static void
+eeidle(Ctlr *ctlr)
+{
+	int i;
+
+	eeclk(ctlr, 0);
+	eeclk(ctlr, Eeclk);
+	for(i=0; i<25; i++){
+		eeclk(ctlr, 0);
+		eeclk(ctlr, Eeclk);
+	}
+	eeclk(ctlr, 0);
+	csr32w(ctlr, Rmear, 0);
+	microdelay(2);
+}
+
+static int
+eegetw(Ctlr *ctlr, int a)
+{
+	int d, i, w;
+
+	eeidle(ctlr);
+	eeclk(ctlr, 0);
+	eeclk(ctlr, Eeclk);
+	d = 0x180 | a;			// read EEPROM at address `a'
+	for(i=0x400; i; i>>=1){
+		if(d & i)
+			csr32w(ctlr, Rmear, Eesel|Eedi);
+		else
+			csr32w(ctlr, Rmear, Eesel);
+		eeclk(ctlr, Eeclk);
+		eeclk(ctlr, 0);
+		microdelay(2);
+	}
+	w = 0;
+	for(i=0x8000; i; i >>= 1){
+		eeclk(ctlr, Eeclk);
+		if(csr32r(ctlr, Rmear) & Eedo)
+			w |= i;
+		microdelay(2);
+		eeclk(ctlr, 0);
+	}
+	eeidle(ctlr);
+	return w;
+}
+
+
+static void
+softreset(Ctlr* ctlr, int resetphys)
+{
+	int i, w;
+
+	/*
+	 * Soft-reset the controller
+	 */
+	csr32w(ctlr, Rcr, Rst);
+	for(i=0;; i++){
+		if(i > 100)
+			panic("ns83815: soft reset did not complete");
+		microdelay(250);
+		if((csr32r(ctlr, Rcr) & Rst) == 0)
+			break;
+		delay(1);
+	}
+
+	csr32w(ctlr, Rccsr, Pmests);
+	csr32w(ctlr, Rccsr, 0);
+	csr32w(ctlr, Rcfg, csr32r(ctlr, Rcfg) | Pint_acen);
+// MII
+// TODO: finish this
+	if(resetphys){
+		/*
+		 * Soft-reset the PHY
+		 */
+		csr32w(ctlr, Rbmcr, Reset);
+		for(i=0;; i++){
+			if(i > 100)
+				panic("ns83815: PHY soft reset time out");
+			if((csr32r(ctlr, Rbmcr) & Reset) == 0)
+				break;
+			delay(1);
+		}
+	}
+
+	/*
+	 * Initialisation values, in sequence (see 4.4 Recommended Registers Configuration)
+	 */
+	csr16w(ctlr, 0xCC, 0x0001);	/* PGSEL */
+	csr16w(ctlr, 0xE4, 0x189C);	/* PMCCSR */
+	csr16w(ctlr, 0xFC, 0x0000);	/* TSTDAT */
+	csr16w(ctlr, 0xF4, 0x5040);	/* DSPCFG */
+	csr16w(ctlr, 0xF8, 0x008C);	/* SDCFG */
+
+	/*
+	 * Auto negotiate
+	 */
+	w = csr16r(ctlr, Rbmsr);	/* clear latched bits */
+	debug("anar: %4.4ux\n", csr16r(ctlr, Ranar));
+	csr16w(ctlr, Rbmcr, Anena);
+	if(csr16r(ctlr, Ranar) == 0 || (csr32r(ctlr, Rcfg) & Aneg_dn) == 0){
+		csr16w(ctlr, Rbmcr, Anena|Anrestart);
+		/* TODO: this times out on the SiS900. why? */
+// TODO: finish this
+		for(i=0;; i++){
+			if(i > 6000){
+				print("ns83815: speed auto neg. timed out\n");
+				break;
+			}
+			if((w = csr16r(ctlr, Rbmsr)) & Ancomp)
+				break;
+			delay(1);
+		}
+		debug("%d ms\n", i);
+		w &= 0xFFFF;
+		debug("bmsr: %4.4ux\n", w);
+		USED(w);
+	}
+	debug("anar: %4.4ux\n", csr16r(ctlr, Ranar));
+	debug("anlpar: %4.4ux\n", csr16r(ctlr, Ranlpar));
+	debug("aner: %4.4ux\n", csr16r(ctlr, Raner));
+	debug("physts: %4.4ux\n", csr16r(ctlr, Rphysts));
+	debug("tbscr: %4.4ux\n", csr16r(ctlr, Rtbscr));
+}
+
+/*
+ * MII/PHY
+ */
+
+// TODO: finish this
+static int
+mdior(Ctlr* ctlr, int n)
+{
+	int data, i, mear, r;
+
+//	mear = csr32r(ctlr, Mear);
+//	r = ~(Mdc|Mddir) & mear;
+	data = 0;
+	for(i = n-1; i >= 0; i--){
+//		if(csr32r(ctlr, Mear) & Mdio)
+			data |= (1<<i);
+//		csr32w(ctlr, Mear, Mdc|r);
+//		csr32w(ctlr, Mear, r);
+	}
+//	csr32w(ctlr, Mear, mear);
+	return data;
+}
+
+// TODO: finish this
+static void
+mdiow(Ctlr* ctlr, int bits, int n)
+{
+	int i, mear, r;
+
+//	mear = csr32r(ctlr, Mear);
+//	r = Mddir|(~Mdc & mear);
+	for(i = n-1; i >= 0; i--){
+//		if(bits & (1<<i))
+//			r |= Mdio;
+//		else
+//			r &= ~Mdio;
+//		csr32w(ctlr, Mear, r);
+//		csr32w(ctlr, Mear, Mdc|r);
+	}
+//	csr32w(ctlr, Mear, mear);
+}
+
+static int
+ns83815miimir(Mii* mii, int pa, int ra)
+{
+	int data;
+	Ctlr *ctlr;
+
+	ctlr = mii->ctlr;
+
+	/*
+	 * MII Management Interface Read.
+	 *
+	 * Preamble;
+	 * ST+OP+PA+RA;
+	 * LT + 16 data bits.
+	 */
+	mdiow(ctlr, 0xFFFFFFFF, 32);
+	mdiow(ctlr, 0x1800|(pa<<5)|ra, 14);
+	data = mdior(ctlr, 18);
+
+	if(data & 0x10000)
+		return -1;
+
+	return data & 0xFFFF;
+}
+
+static int
+ns83815miimiw(Mii* mii, int pa, int ra, int data)
+{
+	Ctlr *ctlr;
+
+	ctlr = mii->ctlr;
+
+	/*
+	 * MII Management Interface Write.
+	 *
+	 * Preamble;
+	 * ST+OP+PA+RA+LT + 16 data bits;
+	 * Z.
+	 */
+	mdiow(ctlr, 0xFFFFFFFF, 32);
+	data &= 0xFFFF;
+	data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16);
+	mdiow(ctlr, data, 32);
+	return data & 0xFFFF;	/* TODO: what's the caller expect here? */
+}
+
+// TODO: finish this
+static int
+sismiimir(Mii* mii, int pa, int ra)
+{
+	int data;
+	Ctlr *ctlr;
+
+	ctlr = mii->ctlr;
+
+	/*
+	 * MII Management Interface Read.
+	 *
+	 * Preamble;
+	 * ST+OP+PA+RA;
+	 * LT + 16 data bits.
+	 */
+	mdiow(ctlr, 0xFFFFFFFF, 32);
+	mdiow(ctlr, 0x1800|(pa<<5)|ra, 14);
+	data = mdior(ctlr, 18);
+
+	if(data & 0x10000)
+		return -1;
+
+	return data & 0xFFFF;
+}
+
+// TODO: finish this
+static int
+sismiimiw(Mii* mii, int pa, int ra, int data)
+{
+	Ctlr *ctlr;
+
+	ctlr = mii->ctlr;
+
+	/*
+	 * MII Management Interface Write.
+	 *
+	 * Preamble;
+	 * ST+OP+PA+RA+LT + 16 data bits;
+	 * Z.
+	 */
+	mdiow(ctlr, 0xFFFFFFFF, 32);
+	data &= 0xFFFF;
+	data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16);
+	mdiow(ctlr, data, 32);
+	return data & 0xFFFF;	/* TODO: what's the caller expect here? */
+}
+
+// MII
+// TODO: finish this
+static int
+media(Ether* ether)
+{
+	Ctlr* ctlr;
+	ulong cfg;
+
+	ctlr = ether->ctlr;
+	cfg = csr32r(ctlr, Rcfg);
+
+	if(waserror()){
+err:
+		if(ctlr->mii != nil){
+			free(ctlr->mii);
+			ctlr->mii = nil;
+		}
+#ifdef FS
+		return 10;
+#else
+		nexterror();
+#endif
+	}
+
+	switch (ctlr->id) {
+	case SiS900:
+	case SiS7016:
+		if (1){
+			if((ctlr->mii = malloc(sizeof(Mii))) == nil)
+				error(Enomem);
+			ctlr->mii->ctlr = ctlr;
+			ctlr->mii->mir = sismiimir;
+			ctlr->mii->miw = sismiimiw;
+			if(mii(ctlr->mii, ~0) == 0)
+				error("no PHY");
+			// ctlr->cfg |= Dupstsien|Lnkstsien|Spdstsien;
+			// ctlr->imr |= Phy;
+		}
+		break;
+	case Nat83815:
+		if (1){
+			if((ctlr->mii = malloc(sizeof(Mii))) == nil)
+				error(Enomem);
+			ctlr->mii->ctlr = ctlr;
+			ctlr->mii->mir = ns83815miimir;
+			ctlr->mii->miw = ns83815miimiw;
+			if(mii(ctlr->mii, ~0) == 0)
+				error("no PHY");
+			// ctlr->cfg |= Dupstsien|Lnkstsien|Spdstsien;
+			// ctlr->imr |= Phy;
+		}
+		break;
+	}
+
+	ctlr->fd = (cfg & Fdup) != 0;
+	if(cfg & Speed100)
+		return 100;
+	if((cfg & Lnksts) == 0)
+		return 100;	/* no link: use 100 to ensure larger queues */
+	return 10;
+}
+
+
+static char* mediatable[9] = {
+	"10BASE-T",				/* TP */
+	"10BASE-2",				/* BNC */
+	"10BASE-5",				/* AUI */
+	"100BASE-TX",
+	"10BASE-TFD",
+	"100BASE-TXFD",
+	"100BASE-T4",
+	"100BASE-FX",
+	"100BASE-FXFD",
+};
+
+enum {
+	MagicReg = 0x48,
+	MagicRegSz = 1,
+	Magicrden = 0x40,	/* read enable, apparently */
+	Paddr=		0x70,	/* address port */
+	Pdata=		0x71,	/* data port */
+};
+
+/* rcmos() originally from LANL's SiS 900 driver's rcmos() */
+static int
+sisrdcmos(Ctlr *ctlr)
+{
+	int i;
+	unsigned reg;
+	ulong port;
+	Pcidev *p;
+
+	p = pcimatch(nil, SiS, SiS630bridge);
+	if (p == nil) {
+		print("ns83815: no SiS 630 rev. %ux bridge for mac addr\n",
+			ctlr->pcidev->rid);
+		return 0;
+	}
+	port = p->mem[0].bar & ~0x01;
+	print(
+"ns83815: SiS 630 rev. %ux reading mac addr from cmos via bridge at port 0x%lux\n",
+		ctlr->pcidev->rid, port);
+
+	reg = pcicfgr8(p, MagicReg);
+	pcicfgw8(p, MagicReg, reg|Magicrden);
+
+	for (i = 0; i < Eaddrlen; i++) {
+		outb(port+Paddr, SiS630eenodeaddr + i);
+		ctlr->sromea[i] = inb(port+Pdata);
+	}
+
+	pcicfgw8(p, MagicReg, reg & ~Magicrden);
+	return 1;
+}
+
+static int
+is630(ulong id, Pcidev *p)
+{
+	if (id == SiS900)
+		switch (p->rid) {
+		case SiSrev630s:
+		case SiSrev630e:
+	  	case SiSrev630ea1:
+			return 1;
+		}
+	return 0;
+}
+
+/*
+ * If this is a SiS 630E chipset with an embedded SiS 900 controller,
+ * we have to read the MAC address from the APC CMOS RAM. - sez freebsd.
+ * However, CMOS *is* NVRAM normally.  See devrtc.c:440, memory.c:88.
+ */
+static void
+sissrom(Ctlr *ctlr)
+{
+	union {
+		uchar	eaddr[Eaddrlen];
+		ushort	alignment;
+	} ee;
+	int i, off = SiSeenodeaddr, cnt = sizeof ee.eaddr / sizeof(short);
+	ushort *shp = (ushort *)ee.eaddr;
+
+	if (!is630(ctlr->id, ctlr->pcidev) || !sisrdcmos(ctlr)) {
+		print("ns83815: reading SiS mac address from eeprom\n");
+		for (i = 0; i < cnt; i++)
+			*shp++ = eegetw(ctlr, off++);
+		memmove(ctlr->sromea, ee.eaddr, sizeof ctlr->sromea);
+	}
+}
+
+static void
+nssrom(Ctlr *ctlr)
+{
+	int i, j;
+
+	debug("ns83815: fa311: reading mac address from eeprom & swizzling\n");
+	for(i = 0; i < nelem(ctlr->srom); i++)
+		ctlr->srom[i] = eegetw(ctlr, i);
+
+	/*
+	 * the MAC address is reversed, straddling word boundaries
+	 */
+	j = Nseenodeaddr*16 + 15;		// bit offset to read
+	for (i=0; i<48; i++, j++)
+		ctlr->sromea[i>>3] |=
+			((ctlr->srom[j>>4] >> (15-(j&0xF))) & 1) << (i&7);
+}
+
+static void
+srom(Ctlr* ctlr)
+{
+	memset(ctlr->sromea, 0, sizeof(ctlr->sromea));
+	switch (ctlr->id) {
+	case SiS900:
+	case SiS7016:
+		sissrom(ctlr);
+		break;
+	case Nat83815:
+		nssrom(ctlr);
+		break;
+	default:
+		print("ns83815: srom: unknown id 0x%ux\n", ctlr->id);
+		break;
+	}
+}
+
+// from pci.c
+enum {
+	Pcinetctlr = 0x02,		/* network controller */
+//	PciCCRp	= 0x09,			/* programming interface class code */
+//	PciCCRu	= 0x0A,			/* sub-class code */
+//	PciCCRb	= 0x0B,			/* base class code */
+};
+
+static void
+scanpci83815(void)
+{
+	typedef struct {
+		ulong	bar;		/* base address */
+		int	size;
+	} Bar;
+	int port;
+	ulong id;
+	Bar *portbarp;
+	Ctlr *ctlr;
+	Pcidev *p = nil;
+
+	while(p = pcimatch(p, 0, 0)){
+		/* ccru is a short in the FS kernel, thus the cast to uchar */
+		if (p->ccrb != Pcinetctlr || (uchar)p->ccru != 0)
+			continue;		// not a nic
+		id = (p->did<<16)|p->vid;
+		// print("ns83815: id 0x%lux on pci bus\n", id);
+		switch(id){
+		case Nat83815:
+			print("ns83815: FA31[12] found\n");
+			break;
+		case SiS900:
+			print("ns83815: SiS900");
+			if (is630(id, p))
+				print(" (within SiS630)");
+			print(" found\n");
+			break;
+		case SiS7016:
+			print("ns83815: SiS7016 found\n");
+			break;
+		default:
+			continue;		// unrecognised
+		}
+		portbarp = &p->mem[0];
+		port = portbarp->bar & ~1;
+
+		/*
+		 * bar[0] is the I/O port register address and
+		 * bar[1] is the memory-mapped register address.
+		 */
+		ctlr = mallocz(sizeof(Ctlr), 1);
+		ctlr->port = port;
+		ctlr->pcidev = p;
+		ctlr->id = id;
+
+		if(ioalloc(ctlr->port, portbarp->size, 0, "ns83815") < 0){
+			print("ns83815: port 0x%ux in use\n", ctlr->port);
+			free(ctlr);
+			continue;
+		}
+
+		softreset(ctlr, 0);
+		srom(ctlr);
+
+		if(ctlrhead != nil)
+			ctlrtail->next = ctlr;
+		else
+			ctlrhead = ctlr;
+		ctlrtail = ctlr;
+	}
+}
+
+int
+dp83815reset(Ether* ether)
+{
+	Ctlr *ctlr;
+	int i, x;
+	ulong ctladdr;
+	uchar ea[Eaddrlen];
+	static int scandone;
+
+	if(scandone == 0){
+		scanpci83815();
+		scandone = 1;
+	}
+
+	/*
+	 * Any adapter matches if no ether->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(ether->port == 0 || ether->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	ether->ctlr = ctlr;
+	ether->port = ctlr->port;
+	ether->irq = ctlr->pcidev->intl;
+	ether->tbdf = ctlr->pcidev->tbdf;
+
+	/*
+	 * Check if the adapter's station address is to be overridden.
+	 * If not, read it from the EEPROM and set in ether->ea prior to
+	 * loading the station address in the hardware.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0)
+		memmove(ether->ea, ctlr->sromea, Eaddrlen);
+	print("ns83815: setting mac addr\n");		/* DEBUG */
+	for(i=0; i<Eaddrlen; i+=2){
+		x = ether->ea[i] | (ether->ea[i+1]<<8);
+		ctladdr = (ctlr->id == Nat83815? i: i<<15);
+		csr32w(ctlr, Rrfcr, ctladdr);
+		csr32w(ctlr, Rrfdr, x);
+	}
+	csr32w(ctlr, Rrfcr, Rfen|Apm|Aab|Aam);
+
+	ether->mbps = media(ether);
+
+	/*
+	 * Look for a medium override in case there's no autonegotiation
+	 * or the autonegotiation fails.
+	 */
+
+	for(i = 0; i < ether->nopt; i++){
+		if(cistrcmp(ether->opt[i], "FD") == 0){
+			ctlr->fd = 1;
+			continue;
+		}
+		for(x = 0; x < nelem(mediatable); x++){
+			debug("compare <%s> <%s>\n", mediatable[x],
+				ether->opt[i]);
+			if(cistrcmp(mediatable[x], ether->opt[i]) == 0){
+				if(x != 4 && x >= 3)
+					ether->mbps = 100;
+				else
+					ether->mbps = 10;
+				switch(x){
+				default:
+					ctlr->fd = 0;
+					break;
+
+				case 0x04:		/* 10BASE-TFD */
+				case 0x05:		/* 100BASE-TXFD */
+				case 0x08:		/* 100BASE-FXFD */
+					ctlr->fd = 1;
+					break;
+				}
+				break;
+			}
+		}
+	}
+
+	/*
+	 * Initialise descriptor rings, ethernet address.
+	 */
+	ctlr->nrdr = Nrde;
+	ctlr->ntdr = Ntde;
+	pcisetbme(ctlr->pcidev);
+	ctlrinit(ether);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+#ifndef FS
+	ether->ifstat = ifstat;
+	ether->arg = ether;
+	ether->promiscuous = promiscuous;
+#endif
+	debug("ns83815: dp83815reset: done\n");
+	return 0;
+}
+
+#ifndef FS
+void
+ether83815link(void)
+{
+	addethercard("83815",  dp83815reset);
+}
+#endif

+ 20 - 20
sys/src/fs/pc/etherdp83820.c

@@ -468,7 +468,7 @@ mdiow(Ctlr* ctlr, int bits, int n)
 }
 
 static int
-miimir(Mii* mii, int pa, int ra)
+dp83820miimir(Mii* mii, int pa, int ra)
 {
 	int data;
 	Ctlr *ctlr;
@@ -492,8 +492,8 @@ miimir(Mii* mii, int pa, int ra)
 	return data & 0xFFFF;
 }
 
-static void
-miimiw(Mii* mii, int pa, int ra, int data)
+static int
+dp83820miimiw(Mii* mii, int pa, int ra, int data)
 {
 	Ctlr *ctlr;
 
@@ -510,6 +510,7 @@ miimiw(Mii* mii, int pa, int ra, int data)
 	data &= 0xFFFF;
 	data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16);
 	mdiow(ctlr, data, 32);
+	return data & 0xFFFF;	/* TODO: what's the caller expect here? */
 }
 
 /*
@@ -746,8 +747,8 @@ err:
 		if((ctlr->mii = malloc(sizeof(Mii))) == nil)
 			error(Enomem);
 		ctlr->mii->ctlr = ctlr;
-		ctlr->mii->mir = miimir;
-		ctlr->mii->miw = miimiw;
+		ctlr->mii->mir = dp83820miimir;
+		ctlr->mii->miw = dp83820miimiw;
 		if(mii(ctlr->mii, ~0) == 0)
 			error("no PHY");
 		ctlr->cfg |= Dupstsien|Lnkstsien|Spdstsien;
@@ -766,12 +767,7 @@ err:
 	 * allocate receive Blocks+buffers, add all to receive Block+buffer pool
 	 */
 	for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){
-		if((bp = iallocb(Rbsz)) == nil) {
-			print(
-		"dp83820attach: iallocb failed with %d rcv bufs allocated\n",
-				ctlr->nrb);
-			error(Enomem);
-		}
+		bp = iallocb(Rbsz);
 #ifdef FS
 		bp->flags |= Mbrcvbuf;
 #endif
@@ -976,12 +972,13 @@ dp83820ifstat(Ether* edev, void* a, long n, ulong offset)
 	}
 	l += snprint(p+l, READSTR-l, "\n");
 
-	if(ctlr->mii != nil && (phy = ctlr->mii->curphy) != nil){
+	if(0 && ctlr->mii != nil && (phy = ctlr->mii->curphy) != nil){
 		l += snprint(p+l, READSTR, "phy:");
 		for(i = 0; i < NMiiPhyr; i++){
 			if(i && ((i & 0x07) == 0))
 				l += snprint(p+l, READSTR-l, "\n    ");
-			l += snprint(p+l, READSTR-l, " %4.4uX", phy->r[i]);
+			/* phy->r no longer exists */
+			// l += snprint(p+l, READSTR-l, " %4.4uX", phy->r[i]);
 		}
 		snprint(p+l, READSTR-l, "\n");
 	}
@@ -1021,7 +1018,6 @@ reread:
 		else
 			r &= ~Eedi;
 		csr32w(ctlr, Mear, r);
-		microdelay(1);
 		csr32w(ctlr, Mear, Eeclk|r);
 		microdelay(1);
 		csr32w(ctlr, Mear, r);
@@ -1144,6 +1140,14 @@ print("cfg %8.8ux pcicfg %8.8ux\n", ctlr->cfg, pcicfgr32(ctlr->pcidev, PciPCR));
 	return 0;
 }
 
+// from pci.c
+enum {
+	Pcinetctlr = 0x02,		/* network controller */
+//	PciCCRp	= 0x09,			/* programming interface class code */
+//	PciCCRu	= 0x0A,			/* sub-class code */
+//	PciCCRb	= 0x0B,			/* base class code */
+};
+
 static void
 dp83820pci(void)
 {
@@ -1153,13 +1157,9 @@ dp83820pci(void)
 
 	p = nil;
 	while(p = pcimatch(p, 0, 0)){
-#ifdef FS
-		if(p->ccru != ((0x02<<8)|0x00))
-#else
-		if(p->ccrb != 0x02 || p->ccru != 0)
-#endif
+		/* ccru is a short in the FS kernel, thus the cast to uchar */
+		if(p->ccrb != Pcinetctlr || (uchar)p->ccru != 0)
 			continue;
-
 		switch((p->did<<16)|p->vid){
 		default:
 			continue;

+ 8 - 7
sys/src/fs/pc/etherga620.c

@@ -473,7 +473,7 @@ ga620interrupt(Ureg*, void* arg)
 			return;
 	}
 	if(DoCountTicks)
-		rdtsc(&tsc0);
+		cycles(&tsc0);
 
 	ctlr->interrupts++;
 	csr32w(ctlr, Hi, 1);
@@ -493,7 +493,7 @@ ga620interrupt(Ureg*, void* arg)
 	csr32w(ctlr, Hi, 0);
 
 	if(DoCountTicks){
-		rdtsc(&tsc1);
+		cycles(&tsc1);
 		ctlr->ticks += tsc1-tsc0;
 	}
 }
@@ -603,9 +603,9 @@ ga620init(Ether* edev)
 	sethost64(&ctlr->gib->srcb.addr, ctlr->sr);
 	if(DoHardwareCksum)
 		flags = TcpUdpCksum|NoPseudoHdrCksum|HostRing;
-	else 
+	else
 		flags = HostRing;
-	if(DoCoalUpdateOnly) 
+	if(DoCoalUpdateOnly)
 		flags |= CoalUpdateOnly;
 	ctlr->gib->srcb.control = (Nsr<<16)|flags;
 	sethost64(&ctlr->gib->scp, ctlr->sci);
@@ -693,7 +693,7 @@ ga620init(Ether* edev)
 	 * A unique index for this controller and the maximum packet
 	 * length expected.
 	 * For now only standard packets are expected.
-	 */ 
+	 */
 	csr32w(ctlr, Ifx, 1);
 	csr32w(ctlr, IfMTU, ETHERMAXTU+4);
 
@@ -879,7 +879,7 @@ ga620detach(Ctlr* ctlr)
 static int
 ga620reset(Ctlr* ctlr)
 {
-	int cls, csr, i;
+	int cls, csr, i, b;
 
 	if(ga620detach(ctlr) < 0)
 		return -1;
@@ -918,8 +918,9 @@ ga620reset(Ctlr* ctlr)
 	 * Snarf the MAC address from the serial EEPROM.
 	 */
 	for(i = 0; i < Easize; i++){
-		if((ctlr->ea[i] = at24c32r(ctlr, 0x8E+i)) == -1)
+		if((b = at24c32r(ctlr, 0x8E+i)) == -1)
 			return -1;
+		ctlr->ea[i] = b;
 	}
 
 	/*

+ 11 - 9
sys/src/fs/pc/etherif.c

@@ -9,6 +9,7 @@ extern int etherga620reset(Ether*);
 extern int ether21140reset(Ether*);
 extern int etherelnk3reset(Ether*);
 extern int etheri82557reset(Ether*);
+extern int igbepnp(Ether *);
 extern int dp83815reset(Ether*);
 extern int dp83820pnp(Ether*);
 extern int rtl8139pnp(Ether*);
@@ -19,15 +20,16 @@ static struct
 	int	(*reset)(Ether*);
 } etherctlr[] =
 {
-	{ "ga620", etherga620reset, },
-	{ "21140", ether21140reset, },
-	{ "2114x", ether21140reset, },
-	{ "3C509", etherelnk3reset, },
-	{ "83815",  dp83815reset, },
-	{ "dp83820", dp83820pnp, },
-	{ "elnk3", etherelnk3reset, },
-	{ "i82557", etheri82557reset, },
-	{ "rtl8139", rtl8139pnp, },
+	{ "21140",	ether21140reset, },
+	{ "2114x",	ether21140reset, },
+	{ "3C509",	etherelnk3reset, },
+	{ "83815",	dp83815reset, },
+	{ "dp83820",	dp83820pnp, },
+	{ "elnk3",	etherelnk3reset, },
+	{ "ga620",	etherga620reset, },
+	{ "i82557",	etheri82557reset, },
+	{ "igbe",	igbepnp, },
+	{ "rtl8139",	rtl8139pnp, },
 	{ 0, },
 };
 

+ 2060 - 0
sys/src/fs/pc/etherigbe.c

@@ -0,0 +1,2060 @@
+/*
+ * Intel 8254[340]NN Gigabit Ethernet Controller
+ * as found on the Intel PRO/1000 series of adapters:
+ *	82543GC	Intel PRO/1000 T
+ *	82544EI Intel PRO/1000 XT
+ *	82540EM Intel PRO/1000 MT
+ *	82541[GP]I
+ *	82547GI
+ *	82546GB
+ *	82546EB
+ * To Do:
+ *	finish autonegotiation code;
+ *	integrate fiber stuff back in (this ONLY handles
+ *		the CAT5 cards at the moment);
+ *	add checksum-offload;
+ *	add tuning control via ctl file;
+ *	this driver is little-endian specific.
+ */
+
+#ifdef FS
+#include "all.h"
+#include "io.h"
+#include "mem.h"
+#include "../ip/ip.h"
+
+#else
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+#endif			/* FS */
+
+#include "etherif.h"
+#include "ethermii.h"
+#include "compat.h"
+
+enum {
+	i82542     	= (0x1000<<16)|0x8086,
+	i82543gc   	= (0x1004<<16)|0x8086,
+	i82544ei   	= (0x1008<<16)|0x8086,
+	i82547ei   	= (0x1019<<16)|0x8086,
+	i82540em   	= (0x100E<<16)|0x8086,
+	i82540eplp 	= (0x101E<<16)|0x8086,
+	i82545gmc	= (0x1026<<16)|0x8086,
+	i82547gi   	= (0x1075<<16)|0x8086,
+	i82541gi   	= (0x1076<<16)|0x8086,
+	i82541gi2		= (0x1077<<16)|0x8086,
+	i82546gb   	= (0x1079<<16)|0x8086,
+	i82541pi	= (0x107c<<16)|0x8086,
+	i82546eb	= (0x1010<<16)|0x8086,
+};
+
+/* from pci.c */
+enum
+{					/* command register (pcidev->pcr) */
+	IOen		= (1<<0),
+	MEMen		= (1<<1),
+	MASen		= (1<<2),
+	MemWrInv	= (1<<4),
+	PErrEn		= (1<<6),
+	SErrEn		= (1<<8),
+};
+enum {
+	Ctrl		= 0x00000000,	/* Device Control */
+	Ctrldup		= 0x00000004,	/* Device Control Duplicate */
+	Status		= 0x00000008,	/* Device Status */
+	Eecd		= 0x00000010,	/* EEPROM/Flash Control/Data */
+	Ctrlext		= 0x00000018,	/* Extended Device Control */
+	Mdic		= 0x00000020,	/* MDI Control */
+	Fcal		= 0x00000028,	/* Flow Control Address Low */
+	Fcah		= 0x0000002C,	/* Flow Control Address High */
+	Fct		= 0x00000030,	/* Flow Control Type */
+	Icr		= 0x000000C0,	/* Interrupt Cause Read */
+	Ics		= 0x000000C8,	/* Interrupt Cause Set */
+	Ims		= 0x000000D0,	/* Interrupt Mask Set/Read */
+	Imc		= 0x000000D8,	/* Interrupt mask Clear */
+	Rctl		= 0x00000100,	/* Receive Control */
+	Fcttv		= 0x00000170,	/* Flow Control Transmit Timer Value */
+	Txcw		= 0x00000178,	/* Transmit Configuration Word */
+	Rxcw		= 0x00000180,	/* Receive Configuration Word */
+	Tctl		= 0x00000400,	/* Transmit Control */
+	Tipg		= 0x00000410,	/* Transmit IPG */
+	Tbt		= 0x00000448,	/* Transmit Burst Timer */
+	Ait		= 0x00000458,	/* Adaptive IFS Throttle */
+	Fcrtl		= 0x00002160,	/* Flow Control RX Threshold Low */
+	Fcrth		= 0x00002168,	/* Flow Control Rx Threshold High */
+	Rdfh		= 0x00002410,	/* Receive data fifo head */
+	Rdft		= 0x00002418,	/* Receive data fifo tail */
+	Rdfhs		= 0x00002420,	/* Receive data fifo head saved */
+	Rdfts		= 0x00002428,	/* Receive data fifo tail saved */
+	Rdfpc		= 0x00002430,	/* Receive data fifo packet count */
+	Rdbal		= 0x00002800,	/* Rd Base Address Low */
+	Rdbah		= 0x00002804,	/* Rd Base Address High */
+	Rdlen		= 0x00002808,	/* Receive Descriptor Length */
+	Rdh		= 0x00002810,	/* Receive Descriptor Head */
+	Rdt		= 0x00002818,	/* Receive Descriptor Tail */
+	Rdtr		= 0x00002820,	/* Receive Descriptor Timer Ring */
+	Rxdctl		= 0x00002828,	/* Receive Descriptor Control */
+	Radv		= 0x0000282C,	/* Receive Interrupt Absolute Delay Timer */
+	Txdmac		= 0x00003000,	/* Transfer DMA Control */
+	Ett		= 0x00003008,	/* Early Transmit Control */
+	Tdfh		= 0x00003410,	/* Transmit data fifo head */
+	Tdft		= 0x00003418,	/* Transmit data fifo tail */
+	Tdfhs		= 0x00003420,	/* Transmit data Fifo Head saved */
+	Tdfts		= 0x00003428,	/* Transmit data fifo tail saved */
+	Tdfpc		= 0x00003430,	/* Trasnmit data Fifo packet count */
+	Tdbal		= 0x00003800,	/* Td Base Address Low */
+	Tdbah		= 0x00003804,	/* Td Base Address High */
+	Tdlen		= 0x00003808,	/* Transmit Descriptor Length */
+	Tdh		= 0x00003810,	/* Transmit Descriptor Head */
+	Tdt		= 0x00003818,	/* Transmit Descriptor Tail */
+	Tidv		= 0x00003820,	/* Transmit Interrupt Delay Value */
+	Txdctl		= 0x00003828,	/* Transmit Descriptor Control */
+	Tadv		= 0x0000382C,	/* Transmit Interrupt Absolute Delay Timer */
+
+	Statistics	= 0x00004000,	/* Start of Statistics Area */
+	Gorcl		= 0x88/4,	/* Good Octets Received Count */
+	Gotcl		= 0x90/4,	/* Good Octets Transmitted Count */
+	Torl		= 0xC0/4,	/* Total Octets Received */
+	Totl		= 0xC8/4,	/* Total Octets Transmitted */
+	Nstatistics	= 64,
+
+	Rxcsum		= 0x00005000,	/* Receive Checksum Control */
+	Mta		= 0x00005200,	/* Multicast Table Array */
+	Ral		= 0x00005400,	/* Receive Address Low */
+	Rah		= 0x00005404,	/* Receive Address High */
+	Manc		= 0x00005820,	/* Management Control */
+};
+
+enum {					/* Ctrl */
+	Bem		= 0x00000002,	/* Big Endian Mode */
+	Prior		= 0x00000004,	/* Priority on the PCI bus */
+	Lrst		= 0x00000008,	/* Link Reset */
+	Asde		= 0x00000020,	/* Auto-Speed Detection Enable */
+	Slu		= 0x00000040,	/* Set Link Up */
+	Ilos		= 0x00000080,	/* Invert Loss of Signal (LOS) */
+	SspeedMASK	= 0x00000300,	/* Speed Selection */
+	SspeedSHIFT	= 8,
+	Sspeed10	= 0x00000000,	/* 10Mb/s */
+	Sspeed100	= 0x00000100,	/* 100Mb/s */
+	Sspeed1000	= 0x00000200,	/* 1000Mb/s */
+	Frcspd		= 0x00000800,	/* Force Speed */
+	Frcdplx		= 0x00001000,	/* Force Duplex */
+	SwdpinsloMASK	= 0x003C0000,	/* Software Defined Pins - lo nibble */
+	SwdpinsloSHIFT	= 18,
+	SwdpioloMASK	= 0x03C00000,	/* Software Defined Pins - I or O */
+	SwdpioloSHIFT	= 22,
+	Devrst		= 0x04000000,	/* Device Reset */
+	Rfce		= 0x08000000,	/* Receive Flow Control Enable */
+	Tfce		= 0x10000000,	/* Transmit Flow Control Enable */
+	Vme		= 0x40000000,	/* VLAN Mode Enable */
+};
+
+enum {					/* Status */
+	Lu		= 0x00000002,	/* Link Up */
+	Tckok		= 0x00000004,	/* Transmit clock is running */
+	Rbcok		= 0x00000008,	/* Receive clock is running */
+	Txoff		= 0x00000010,	/* Transmission Paused */
+	Tbimode		= 0x00000020,	/* TBI Mode Indication */
+	LspeedMASK	= 0x000000C0,	/* Link Speed Setting */
+	LspeedSHIFT	= 6,
+	Lspeed10	= 0x00000000,	/* 10Mb/s */
+	Lspeed100	= 0x00000040,	/* 100Mb/s */
+	Lspeed1000	= 0x00000080,	/* 1000Mb/s */
+	Mtxckok		= 0x00000400,	/* MTX clock is running */
+	Pci66		= 0x00000800,	/* PCI Bus speed indication */
+	Bus64		= 0x00001000,	/* PCI Bus width indication */
+	Pcixmode	= 0x00002000,	/* PCI-X mode */
+	PcixspeedMASK	= 0x0000C000,	/* PCI-X bus speed */
+	PcixspeedSHIFT	= 14,
+	Pcix66		= 0x00000000,	/* 50-66MHz */
+	Pcix100		= 0x00004000,	/* 66-100MHz */
+	Pcix133		= 0x00008000,	/* 100-133MHz */
+};
+
+enum {					/* Ctrl and Status */
+	Fd		= 0x00000001,	/* Full-Duplex */
+	AsdvMASK	= 0x00000300,
+	AsdvSHIFT	= 8,
+	Asdv10		= 0x00000000,	/* 10Mb/s */
+	Asdv100		= 0x00000100,	/* 100Mb/s */
+	Asdv1000	= 0x00000200,	/* 1000Mb/s */
+};
+
+enum {					/* Eecd */
+	Sk		= 0x00000001,	/* Clock input to the EEPROM */
+	Cs		= 0x00000002,	/* Chip Select */
+	Di		= 0x00000004,	/* Data Input to the EEPROM */
+	Do		= 0x00000008,	/* Data Output from the EEPROM */
+	Areq		= 0x00000040,	/* EEPROM Access Request */
+	Agnt		= 0x00000080,	/* EEPROM Access Grant */
+	Eesz256		= 0x00000200,	/* EEPROM is 256 words not 64 */
+	Spi		= 0x00002000,	/* EEPROM is SPI not Microwire */
+};
+
+enum {					/* Ctrlext */
+	Gpien		= 0x0000000F,	/* General Purpose Interrupt Enables */
+	SwdpinshiMASK	= 0x000000F0,	/* Software Defined Pins - hi nibble */
+	SwdpinshiSHIFT	= 4,
+	SwdpiohiMASK	= 0x00000F00,	/* Software Defined Pins - I or O */
+	SwdpiohiSHIFT	= 8,
+	Asdchk		= 0x00001000,	/* ASD Check */
+	Eerst		= 0x00002000,	/* EEPROM Reset */
+	Ips		= 0x00004000,	/* Invert Power State */
+	Spdbyps		= 0x00008000,	/* Speed Select Bypass */
+};
+
+enum {					/* EEPROM content offsets */
+	Ea		= 0x00,		/* Ethernet Address */
+	Cf		= 0x03,		/* Compatibility Field */
+	Pba		= 0x08,		/* Printed Board Assembly number */
+	/* in fs kernel, Icw1 is defined in io.h; changed it here */
+#define Icw1 Igbe_icw1
+	Icw1		= 0x0A,		/* Initialization Control Word 1 */
+	Sid		= 0x0B,		/* Subsystem ID */
+	Svid		= 0x0C,		/* Subsystem Vendor ID */
+	Did		= 0x0D,		/* Device ID */
+	Vid		= 0x0E,		/* Vendor ID */
+	Icw2		= 0x0F,		/* Initialization Control Word 2 */
+};
+
+enum {					/* Mdic */
+	MDIdMASK	= 0x0000FFFF,	/* Data */
+	MDIdSHIFT	= 0,
+	MDIrMASK	= 0x001F0000,	/* PHY Register Address */
+	MDIrSHIFT	= 16,
+	MDIpMASK	= 0x03E00000,	/* PHY Address */
+	MDIpSHIFT	= 21,
+	MDIwop		= 0x04000000,	/* Write Operation */
+	MDIrop		= 0x08000000,	/* Read Operation */
+	MDIready	= 0x10000000,	/* End of Transaction */
+	MDIie		= 0x20000000,	/* Interrupt Enable */
+	MDIe		= 0x40000000,	/* Error */
+};
+
+enum {					/* Icr, Ics, Ims, Imc */
+	Txdw		= 0x00000001,	/* Transmit Descriptor Written Back */
+	Txqe		= 0x00000002,	/* Transmit Queue Empty */
+	Lsc		= 0x00000004,	/* Link Status Change */
+	Rxseq		= 0x00000008,	/* Receive Sequence Error */
+	Rxdmt0		= 0x00000010,	/* Rd Minimum Threshold Reached */
+	Rxo		= 0x00000040,	/* Receiver Overrun */
+	Rxt0		= 0x00000080,	/* Receiver Timer Interrupt */
+	Mdac		= 0x00000200,	/* MDIO Access Completed */
+	Rxcfg		= 0x00000400,	/* Receiving /C/ ordered sets */
+	Gpi0		= 0x00000800,	/* General Purpose Interrupts */
+	Gpi1		= 0x00001000,
+	Gpi2		= 0x00002000,
+	Gpi3		= 0x00004000,
+};
+
+/*
+ * The Mdic register isn't implemented on the 82543GC,
+ * the software defined pins are used instead.
+ * These definitions work for the Intel PRO/1000 T Server Adapter.
+ * The direction pin bits are read from the EEPROM.
+ */
+enum {
+	Mdd		= ((1<<2)<<SwdpinsloSHIFT),	/* data */
+	Mddo		= ((1<<2)<<SwdpioloSHIFT),	/* pin direction */
+	Mdc		= ((1<<3)<<SwdpinsloSHIFT),	/* clock */
+	Mdco		= ((1<<3)<<SwdpioloSHIFT),	/* pin direction */
+	Mdr		= ((1<<0)<<SwdpinshiSHIFT),	/* reset */
+	Mdro		= ((1<<0)<<SwdpiohiSHIFT),	/* pin direction */
+};
+
+enum {					/* Txcw */
+	TxcwFd		= 0x00000020,	/* Full Duplex */
+	TxcwHd		= 0x00000040,	/* Half Duplex */
+	TxcwPauseMASK	= 0x00000180,	/* Pause */
+	TxcwPauseSHIFT	= 7,
+	TxcwPs		= (1<<TxcwPauseSHIFT),	/* Pause Supported */
+	TxcwAs		= (2<<TxcwPauseSHIFT),	/* Asymmetric FC desired */
+	TxcwRfiMASK	= 0x00003000,	/* Remote Fault Indication */
+	TxcwRfiSHIFT	= 12,
+	TxcwNpr		= 0x00008000,	/* Next Page Request */
+	TxcwConfig	= 0x40000000,	/* Transmit COnfig Control */
+	TxcwAne		= 0x80000000,	/* Auto-Negotiation Enable */
+};
+
+enum {					/* Rxcw */
+	Rxword		= 0x0000FFFF,	/* Data from auto-negotiation process */
+	Rxnocarrier	= 0x04000000,	/* Carrier Sense indication */
+	Rxinvalid	= 0x08000000,	/* Invalid Symbol during configuration */
+	Rxchange	= 0x10000000,	/* Change to the Rxword indication */
+	Rxconfig	= 0x20000000,	/* /C/ order set reception indication */
+	Rxsync		= 0x40000000,	/* Lost bit synchronization indication */
+	Anc		= 0x80000000,	/* Auto Negotiation Complete */
+};
+
+enum {					/* Rctl */
+	Rrst		= 0x00000001,	/* Receiver Software Reset */
+	Ren		= 0x00000002,	/* Receiver Enable */
+	Sbp		= 0x00000004,	/* Store Bad Packets */
+	Upe		= 0x00000008,	/* Unicast Promiscuous Enable */
+	Mpe		= 0x00000010,	/* Multicast Promiscuous Enable */
+	Lpe		= 0x00000020,	/* Long Packet Reception Enable */
+	LbmMASK		= 0x000000C0,	/* Loopback Mode */
+	LbmOFF		= 0x00000000,	/* No Loopback */
+	LbmTBI		= 0x00000040,	/* TBI Loopback */
+	LbmMII		= 0x00000080,	/* GMII/MII Loopback */
+	LbmXCVR		= 0x000000C0,	/* Transceiver Loopback */
+	RdtmsMASK	= 0x00000300,	/* Rd Minimum Threshold Size */
+	RdtmsHALF	= 0x00000000,	/* Threshold is 1/2 Rdlen */
+	RdtmsQUARTER	= 0x00000100,	/* Threshold is 1/4 Rdlen */
+	RdtmsEIGHTH	= 0x00000200,	/* Threshold is 1/8 Rdlen */
+	MoMASK		= 0x00003000,	/* Multicast Offset */
+	Mo47b36		= 0x00000000,	/* bits [47:36] of received address */
+	Mo46b35		= 0x00001000,	/* bits [46:35] of received address */
+	Mo45b34		= 0x00002000,	/* bits [45:34] of received address */
+	Mo43b32		= 0x00003000,	/* bits [43:32] of received address */
+	Bam		= 0x00008000,	/* Broadcast Accept Mode */
+	BsizeMASK	= 0x00030000,	/* Receive Buffer Size */
+	Bsize2048	= 0x00000000,	/* Bsex = 0 */
+	Bsize1024	= 0x00010000,	/* Bsex = 0 */
+	Bsize512	= 0x00020000,	/* Bsex = 0 */
+	Bsize256	= 0x00030000,	/* Bsex = 0 */
+	Bsize16384	= 0x00010000,	/* Bsex = 1 */
+	Vfe		= 0x00040000,	/* VLAN Filter Enable */
+	Cfien		= 0x00080000,	/* Canonical Form Indicator Enable */
+	Cfi		= 0x00100000,	/* Canonical Form Indicator value */
+	Dpf		= 0x00400000,	/* Discard Pause Frames */
+	Pmcf		= 0x00800000,	/* Pass MAC Control Frames */
+	Bsex		= 0x02000000,	/* Buffer Size Extension */
+	Secrc		= 0x04000000,	/* Strip CRC from incoming packet */
+};
+
+enum {					/* Tctl */
+	Trst		= 0x00000001,	/* Transmitter Software Reset */
+	Ten		= 0x00000002,	/* Transmit Enable */
+	Psp		= 0x00000008,	/* Pad Short Packets */
+	CtMASK		= 0x00000FF0,	/* Collision Threshold */
+	CtSHIFT		= 4,
+	ColdMASK	= 0x003FF000,	/* Collision Distance */
+	ColdSHIFT	= 12,
+	Swxoff		= 0x00400000,	/* Sofware XOFF Transmission */
+	Pbe		= 0x00800000,	/* Packet Burst Enable */
+	Rtlc		= 0x01000000,	/* Re-transmit on Late Collision */
+	Nrtu		= 0x02000000,	/* No Re-transmit on Underrrun */
+};
+
+enum {					/* [RT]xdctl */
+	PthreshMASK	= 0x0000003F,	/* Prefetch Threshold */
+	PthreshSHIFT	= 0,
+	HthreshMASK	= 0x00003F00,	/* Host Threshold */
+	HthreshSHIFT	= 8,
+	WthreshMASK	= 0x003F0000,	/* Writeback Threshold */
+	WthreshSHIFT	= 16,
+	Gran		= 0x01000000,	/* Granularity */
+	LthreshMASK	= 0xFE000000,	/* Low Threshold */
+	LthreshSHIFT	= 25,
+};
+
+enum {					/* Rxcsum */
+	PcssMASK	= 0x000000FF,	/* Packet Checksum Start */
+	PcssSHIFT	= 0,
+	Ipofl		= 0x00000100,	/* IP Checksum Off-load Enable */
+	Tuofl		= 0x00000200,	/* TCP/UDP Checksum Off-load Enable */
+};
+
+enum {					/* Manc */
+	Arpen		= 0x00002000,	/* Enable ARP Request Filtering */
+};
+
+enum {					/* Receive Delay Timer Ring */
+	DelayMASK	= 0x0000FFFF,	/* delay timer in 1.024nS increments */
+	DelaySHIFT	= 0,
+	Fpd		= 0x80000000,	/* Flush partial Descriptor Block */
+};
+
+typedef struct Rd {			/* Receive Descriptor */
+	uint	addr[2];
+	ushort	length;
+	ushort	checksum;
+	uchar	status;
+	uchar	errors;
+	ushort	special;
+} Rd;
+
+enum {					/* Rd status */
+	Rdd		= 0x01,		/* Descriptor Done */
+	Reop		= 0x02,		/* End of Packet */
+	Ixsm		= 0x04,		/* Ignore Checksum Indication */
+	Vp		= 0x08,		/* Packet is 802.1Q (matched VET) */
+	Tcpcs		= 0x20,		/* TCP Checksum Calculated on Packet */
+	Ipcs		= 0x40,		/* IP Checksum Calculated on Packet */
+	Pif		= 0x80,		/* Passed in-exact filter */
+};
+
+enum {					/* Rd errors */
+	Ce		= 0x01,		/* CRC Error or Alignment Error */
+	Se		= 0x02,		/* Symbol Error */
+	Seq		= 0x04,		/* Sequence Error */
+	Cxe		= 0x10,		/* Carrier Extension Error */
+	Tcpe		= 0x20,		/* TCP/UDP Checksum Error */
+	Ipe		= 0x40,		/* IP Checksum Error */
+	Rxe		= 0x80,		/* RX Data Error */
+};
+
+typedef struct {			/* Transmit Descriptor */
+	union {
+		uint	addr[2];	/* Data */
+		struct {		/* Context */
+			uchar	ipcss;
+			uchar	ipcso;
+			ushort	ipcse;
+			uchar	tucss;
+			uchar	tucso;
+			ushort	tucse;
+		};
+	};
+	uint	control;
+	uint	status;
+} Td;
+
+enum {					/* Td control */
+	LenMASK		= 0x000FFFFF,	/* Data/Packet Length Field */
+	LenSHIFT	= 0,
+	DtypeCD		= 0x00000000,	/* Data Type 'Context Descriptor' */
+	DtypeDD		= 0x00100000,	/* Data Type 'Data Descriptor' */
+	PtypeTCP	= 0x01000000,	/* TCP/UDP Packet Type (CD) */
+	Teop		= 0x01000000,	/* End of Packet (DD) */
+	PtypeIP		= 0x02000000,	/* IP Packet Type (CD) */
+	Ifcs		= 0x02000000,	/* Insert FCS (DD) */
+	Tse		= 0x04000000,	/* TCP Segmentation Enable */
+	Rs		= 0x08000000,	/* Report Status */
+	Rps		= 0x10000000,	/* Report Status Sent */
+	Dext		= 0x20000000,	/* Descriptor Extension */
+	Vle		= 0x40000000,	/* VLAN Packet Enable */
+	Ide		= 0x80000000,	/* Interrupt Delay Enable */
+};
+
+enum {					/* Td status */
+	Tdd		= 0x00000001,	/* Descriptor Done */
+	Ec		= 0x00000002,	/* Excess Collisions */
+	Lc		= 0x00000004,	/* Late Collision */
+	Tu		= 0x00000008,	/* Transmit Underrun */
+	Iixsm		= 0x00000100,	/* Insert IP Checksum */
+	Itxsm		= 0x00000200,	/* Insert TCP/UDP Checksum */
+	HdrlenMASK	= 0x0000FF00,	/* Header Length (Tse) */
+	HdrlenSHIFT	= 8,
+	VlanMASK	= 0x0FFF0000,	/* VLAN Identifier */
+	VlanSHIFT	= 16,
+	Tcfi		= 0x10000000,	/* Canonical Form Indicator */
+	PriMASK		= 0xE0000000,	/* User Priority */
+	PriSHIFT	= 29,
+	MssMASK		= 0xFFFF0000,	/* Maximum Segment Size (Tse) */
+	MssSHIFT	= 16,
+};
+
+enum {
+	Nrd		= 256,		/* multiple of 8 */
+	/*
+	 * we require a power of 2. also must be a multiple of TDD_INTVL (64).
+	 * chip errata says max 256.
+	 */
+#ifdef FS
+	Ntd		= 256,		/* chip requires multiple of 8 */
+#else
+	Ntd		= 64,		/* chip requires multiple of 8 */
+#endif
+	Nrb		= 1024,		/* private receive buffers per Ctlr */
+	Rbsz		= 2048,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+	ulong	port;
+	Pcidev*	pcidev;
+	Ctlr*	next;
+	int	active;
+	int	started;
+	int	id;
+	int	cls;
+	ushort	eeprom[0x40];
+
+	QLock	alock;			/* attach */
+	void*	alloc;			/* receive/transmit descriptors */
+	int	nrd;
+	int	ntd;
+	int	nrb;			/* how many this Ctlr has in the pool */
+
+	int*	nic;
+	Lock	imlock;
+	int	im;			/* interrupt mask */
+
+	Mii*	mii;
+	Rendez	lrendez;
+	int	lim;
+
+	int	link;
+
+	QLock	slock;
+	uint	statistics[Nstatistics];
+	uint	lsleep;
+	uint	lintr;
+	uint	rsleep;
+	uint	rintr;
+	uint	txdw;
+	uint	tintr;
+	uint	ixsm;
+	uint	ipcs;
+	uint	tcpcs;
+
+	uchar	ra[Eaddrlen];		/* receive address */
+	ulong	mta[128];		/* multicast table array */
+
+	Rendez	rrendez;
+	int	rim;
+	int	rdfree;
+	Rd*	rdba;			/* receive descriptor base address */
+	Block**	rb;			/* receive buffers */
+	int	rdh;			/* receive descriptor head */
+	int	rdt;			/* receive descriptor tail */
+	int	rdtr;			/* receive delay timer ring value */
+
+	Lock	tlock;
+	int	tbusy;
+	int	tdfree;
+	Td*	tdba;			/* transmit descriptor base address */
+	Block**	tb;			/* transmit buffers */
+	int	tdh;			/* transmit descriptor head */
+	int	tdt;			/* transmit descriptor tail */
+
+	int	txcw;
+	int	fcrtl;
+	int	fcrth;
+} Ctlr;
+
+#define csr32r(c, r)	(*((c)->nic+((r)/4)))
+#define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
+
+static Ctlr* igbectlrhead;
+static Ctlr* igbectlrtail;
+
+static Lock igberblock;		/* free receive Blocks */
+static Block* igberbpool;
+
+static char* statistics[Nstatistics] = {
+	"CRC Error",
+	"Alignment Error",
+	"Symbol Error",
+	"RX Error",
+	"Missed Packets",
+	"Single Collision",
+	"Excessive Collisions",
+	"Multiple Collision",
+	"Late Collisions",
+	nil,
+	"Collision",
+	"Transmit Underrun",
+	"Defer",
+	"Transmit - No CRS",
+	"Sequence Error",
+	"Carrier Extension Error",
+	"Receive Error Length",
+	nil,
+	"XON Received",
+	"XON Transmitted",
+	"XOFF Received",
+	"XOFF Transmitted",
+	"FC Received Unsupported",
+	"Packets Received (64 Bytes)",
+	"Packets Received (65-127 Bytes)",
+	"Packets Received (128-255 Bytes)",
+	"Packets Received (256-511 Bytes)",
+	"Packets Received (512-1023 Bytes)",
+	"Packets Received (1024-1522 Bytes)",
+	"Good Packets Received",
+	"Broadcast Packets Received",
+	"Multicast Packets Received",
+	"Good Packets Transmitted",
+	nil,
+	"Good Octets Received",
+	nil,
+	"Good Octets Transmitted",
+	nil,
+	nil,
+	nil,
+	"Receive No Buffers",
+	"Receive Undersize",
+	"Receive Fragment",
+	"Receive Oversize",
+	"Receive Jabber",
+	nil,
+	nil,
+	nil,
+	"Total Octets Received",
+	nil,
+	"Total Octets Transmitted",
+	nil,
+	"Total Packets Received",
+	"Total Packets Transmitted",
+	"Packets Transmitted (64 Bytes)",
+	"Packets Transmitted (65-127 Bytes)",
+	"Packets Transmitted (128-255 Bytes)",
+	"Packets Transmitted (256-511 Bytes)",
+	"Packets Transmitted (512-1023 Bytes)",
+	"Packets Transmitted (1024-1522 Bytes)",
+	"Multicast Packets Transmitted",
+	"Broadcast Packets Transmitted",
+	"TCP Segmentation Context Transmitted",
+	"TCP Segmentation Context Fail",
+};
+
+#ifndef FS
+static long
+igbeifstat(Ether* edev, void* a, long n, ulong offset)
+{
+	Ctlr *ctlr;
+	char *p, *s;
+	int i, l, r;
+	uvlong tuvl, ruvl;
+
+	ctlr = edev->ctlr;
+	qlock(&ctlr->slock);
+	p = malloc(2*READSTR);
+	if (p == nil)
+		panic("igbeifstat: no mem");
+	l = 0;
+	for(i = 0; i < Nstatistics; i++){
+		r = csr32r(ctlr, Statistics+i*4);
+		if((s = statistics[i]) == nil)
+			continue;
+		switch(i){
+		case Gorcl:
+		case Gotcl:
+		case Torl:
+		case Totl:
+			ruvl = r;
+			ruvl += ((uvlong)csr32r(ctlr, Statistics+(i+1)*4))<<32;
+			tuvl = ruvl;
+			tuvl += ctlr->statistics[i];
+			tuvl += ((uvlong)ctlr->statistics[i+1])<<32;
+			if(tuvl == 0)
+				continue;
+			ctlr->statistics[i] = tuvl;
+			ctlr->statistics[i+1] = tuvl>>32;
+			l += snprint(p+l, 2*READSTR-l, "%s: %llud %llud\n",
+				s, tuvl, ruvl);
+			i++;
+			break;
+
+		default:
+			ctlr->statistics[i] += r;
+			if(ctlr->statistics[i] == 0)
+				continue;
+			l += snprint(p+l, 2*READSTR-l, "%s: %ud %ud\n",
+				s, ctlr->statistics[i], r);
+			break;
+		}
+	}
+
+	l += snprint(p+l, 2*READSTR-l, "lintr: %ud %ud\n",
+		ctlr->lintr, ctlr->lsleep);
+	l += snprint(p+l, 2*READSTR-l, "rintr: %ud %ud\n",
+		ctlr->rintr, ctlr->rsleep);
+	l += snprint(p+l, 2*READSTR-l, "tintr: %ud %ud\n",
+		ctlr->tintr, ctlr->txdw);
+	l += snprint(p+l, 2*READSTR-l, "ixcs: %ud %ud %ud\n",
+		ctlr->ixsm, ctlr->ipcs, ctlr->tcpcs);
+	l += snprint(p+l, 2*READSTR-l, "rdtr: %ud\n", ctlr->rdtr);
+	l += snprint(p+l, 2*READSTR-l, "Ctrlext: %08x\n", csr32r(ctlr, Ctrlext));
+
+	l += snprint(p+l, 2*READSTR-l, "eeprom:");
+	for(i = 0; i < 0x40; i++){
+		if(i && ((i & 0x07) == 0))
+			l += snprint(p+l, 2*READSTR-l, "\n       ");
+		l += snprint(p+l, 2*READSTR-l, " %4.4uX", ctlr->eeprom[i]);
+	}
+	l += snprint(p+l, 2*READSTR-l, "\n");
+
+	if(ctlr->mii != nil && ctlr->mii->curphy != nil){
+		l += snprint(p+l, 2*READSTR, "phy:   ");
+		for(i = 0; i < NMiiPhyr; i++){
+			if(i && ((i & 0x07) == 0))
+				l += snprint(p+l, 2*READSTR-l, "\n       ");
+			r = miimir(ctlr->mii, i);
+			l += snprint(p+l, 2*READSTR-l, " %4.4uX", r);
+		}
+		snprint(p+l, 2*READSTR-l, "\n");
+	}
+	n = readstr(offset, a, n, p);
+	free(p);
+	qunlock(&ctlr->slock);
+
+	return n;
+}
+
+enum {
+	CMrdtr,
+};
+
+static Cmdtab igbectlmsg[] = {
+	CMrdtr,	"rdtr",	2,
+};
+
+static long
+igbectl(Ether* edev, void* buf, long n)
+{
+	int v;
+	char *p;
+	Ctlr *ctlr;
+	Cmdbuf *cb;
+	Cmdtab *ct;
+
+	if((ctlr = edev->ctlr) == nil)
+		error(Enonexist);
+
+	cb = parsecmd(buf, n);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+
+	ct = lookupcmd(cb, igbectlmsg, nelem(igbectlmsg));
+	switch(ct->index){
+	case CMrdtr:
+		v = strtol(cb->f[1], &p, 0);
+		if(v < 0 || p == cb->f[1] || v > 0xFFFF)
+			error(Ebadarg);
+		ctlr->rdtr = v;;
+		csr32w(ctlr, Rdtr, Fpd|v);
+		break;
+	}
+	free(cb);
+	poperror();
+
+	return n;
+}
+#endif		/* FS */
+
+static void
+igbepromiscuous(void* arg, int on)
+{
+	int rctl;
+	Ctlr *ctlr;
+	Ether *edev;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	rctl = csr32r(ctlr, Rctl);
+	rctl &= ~MoMASK;
+	rctl |= Mo47b36;
+	if(on)
+		rctl |= Upe|Mpe;
+	else
+		rctl &= ~(Upe|Mpe);
+	csr32w(ctlr, Rctl, rctl);
+}
+
+static void
+igbemulticast(void* arg, uchar* addr, int on)
+{
+	int bit, x;
+	Ctlr *ctlr;
+	Ether *edev;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	x = addr[5]>>1;
+	bit = ((addr[5] & 1)<<4)|(addr[4]>>4);
+	if(on)
+		ctlr->mta[x] |= 1<<bit;
+	else
+		ctlr->mta[x] &= ~(1<<bit);
+
+	csr32w(ctlr, Mta+x*4, ctlr->mta[x]);
+}
+
+static Block*
+igberballoc(void)
+{
+	Block *bp;
+
+	ilock(&igberblock);
+	if((bp = igberbpool) != nil){
+		igberbpool = bp->next;
+		bp->next = nil;
+	}
+	iunlock(&igberblock);
+
+	return bp;
+}
+
+static void
+igberbfree(Block* bp)
+{
+	BLKRESET(bp);
+	ilock(&igberblock);
+	bp->next = igberbpool;
+	igberbpool = bp;
+	iunlock(&igberblock);
+}
+
+static void
+igbeim(Ctlr* ctlr, int im)
+{
+	ilock(&ctlr->imlock);
+	ctlr->im |= im;
+	csr32w(ctlr, Ims, ctlr->im);
+	iunlock(&ctlr->imlock);
+}
+
+static int
+igbelim(void* ctlr)
+{
+	return ((Ctlr*)ctlr)->lim != 0;
+}
+
+static void
+igbelproc(PROCARG(void *arg))
+{
+	Ctlr *ctlr;
+	Ether *edev;
+	MiiPhy *phy;
+	int ctrl, r;
+
+	edev = GETARG(arg);
+	ctlr = edev->ctlr;
+	for(;;){
+		if(ctlr->mii == nil || ctlr->mii->curphy == nil)
+			continue;
+
+		/*
+		 * To do:
+		 *	logic to manage status change,
+		 *	this is incomplete but should work
+		 *	one time to set up the hardware.
+		 *
+		 *	MiiPhy.speed, etc. should be in Mii.
+		 */
+		if(miistatus(ctlr->mii) < 0)
+			//continue;
+			goto enable;
+
+		phy = ctlr->mii->curphy;
+		ctrl = csr32r(ctlr, Ctrl);
+
+		switch(ctlr->id){
+		case i82543gc:
+		case i82544ei:
+		default:
+			if(!(ctrl & Asde)){
+				ctrl &= ~(SspeedMASK|Ilos|Fd);
+				ctrl |= Frcdplx|Frcspd;
+				if(phy->speed == 1000)
+					ctrl |= Sspeed1000;
+				else if(phy->speed == 100)
+					ctrl |= Sspeed100;
+				if(phy->fd)
+					ctrl |= Fd;
+			}
+			break;
+
+		case i82540em:
+		case i82540eplp:
+		case i82547gi:
+		case i82541gi2:
+		case i82541gi:
+		case i82541pi:
+			break;
+		}
+
+		/*
+		 * Collision Distance.
+		 */
+		r = csr32r(ctlr, Tctl);
+		r &= ~ColdMASK;
+		if(phy->fd)
+			r |= 64<<ColdSHIFT;
+		else
+			r |= 512<<ColdSHIFT;
+		csr32w(ctlr, Tctl, r);
+
+		/*
+		 * Flow control.
+		 */
+		if(phy->rfc)
+			ctrl |= Rfce;
+		if(phy->tfc)
+			ctrl |= Tfce;
+		csr32w(ctlr, Ctrl, ctrl);
+
+enable:
+		ctlr->lim = 0;
+		igbeim(ctlr, Lsc);
+
+		ctlr->lsleep++;
+		sleep(&ctlr->lrendez, igbelim, ctlr);
+	}
+}
+
+static void
+igbetxinit(Ctlr* ctlr)
+{
+	int i, r;
+	Block *bp;
+
+	csr32w(ctlr, Tctl, (0x0F<<CtSHIFT)|Psp|(66<<ColdSHIFT));
+	switch(ctlr->id){
+	default:
+		r = 6;
+		break;
+	case i82543gc:
+	case i82544ei:
+	case i82547ei:
+	case i82540em:
+	case i82540eplp:
+	case i82541gi:
+	case i82541pi:
+	case i82545gmc:
+	case i82546gb:
+	case i82546eb:
+	case i82547gi:
+	case i82541gi2:
+		r = 8;
+		break;
+	}
+	csr32w(ctlr, Tipg, (6<<20)|(8<<10)|r);
+	csr32w(ctlr, Ait, 0);
+	csr32w(ctlr, Txdmac, 0);
+
+	csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
+	csr32w(ctlr, Tdbah, 0);
+	csr32w(ctlr, Tdlen, ctlr->ntd*sizeof(Td));
+	ctlr->tdh = PREV(0, ctlr->ntd);
+	csr32w(ctlr, Tdh, 0);
+	ctlr->tdt = 0;
+	csr32w(ctlr, Tdt, 0);
+
+	for(i = 0; i < ctlr->ntd; i++){
+		if((bp = ctlr->tb[i]) != nil){
+			ctlr->tb[i] = nil;
+			freeb(bp);
+		}
+		memset(&ctlr->tdba[i], 0, sizeof(Td));
+	}
+	ctlr->tdfree = ctlr->ntd;
+
+	csr32w(ctlr, Tidv, 128);
+	r = (4<<WthreshSHIFT)|(4<<HthreshSHIFT)|(8<<PthreshSHIFT);
+
+	switch(ctlr->id){
+	default:
+		break;
+	case i82540em:
+	case i82540eplp:
+	case i82547gi:
+	case i82541gi2:
+	case i82545gmc:
+	case i82546gb:
+	case i82546eb:
+	case i82541gi:
+	case i82541pi:
+		r = csr32r(ctlr, Txdctl);
+		r &= ~WthreshMASK;
+		r |= Gran|(4<<WthreshSHIFT);
+
+		csr32w(ctlr, Tadv, 64);
+		break;
+	}
+
+	csr32w(ctlr, Txdctl, r);
+
+	r = csr32r(ctlr, Tctl);
+	r |= Ten;
+	csr32w(ctlr, Tctl, r);
+}
+
+static void
+igbetransmit(Ether* edev)
+{
+	Td *td;
+	Block *bp;
+	Ctlr *ctlr;
+	int tdh, tdt;
+
+	ctlr = edev->ctlr;
+
+	ilock(&ctlr->tlock);
+
+	/*
+	 * Free any completed packets
+	 */
+	tdh = ctlr->tdh;
+	while(NEXT(tdh, ctlr->ntd) != csr32r(ctlr, Tdh)){
+		if((bp = ctlr->tb[tdh]) != nil){
+			ctlr->tb[tdh] = nil;
+			freeb(bp);
+		}
+		memset(&ctlr->tdba[tdh], 0, sizeof(Td));
+		tdh = NEXT(tdh, ctlr->ntd);
+	}
+	ctlr->tdh = tdh;
+
+	/*
+	 * Try to fill the ring back up.
+	 */
+	tdt = ctlr->tdt;
+	while(NEXT(tdt, ctlr->ntd) != tdh){
+		if((bp = etheroq(edev)) == nil)
+			break;
+		td = &ctlr->tdba[tdt];
+		td->addr[0] = PCIWADDR(bp->rp);
+		td->control = ((BLEN(bp) & LenMASK)<<LenSHIFT);
+		td->control |= Dext|Ifcs|Teop|DtypeDD;
+		ctlr->tb[tdt] = bp;
+		tdt = NEXT(tdt, ctlr->ntd);
+		if(NEXT(tdt, ctlr->ntd) == tdh){
+			td->control |= Rs;
+			ctlr->txdw++;
+			ctlr->tdt = tdt;
+			csr32w(ctlr, Tdt, tdt);
+			igbeim(ctlr, Txdw);
+			break;
+		}
+		ctlr->tdt = tdt;
+		csr32w(ctlr, Tdt, tdt);
+	}
+
+	iunlock(&ctlr->tlock);
+}
+
+static void
+igbereplenish(Ctlr* ctlr)
+{
+	Rd *rd;
+	int rdt;
+	Block *bp;
+
+	rdt = ctlr->rdt;
+	while(NEXT(rdt, ctlr->nrd) != ctlr->rdh){
+		rd = &ctlr->rdba[rdt];
+		if(ctlr->rb[rdt] == nil){
+			bp = igberballoc();
+			if(bp == nil) {
+				iprint("igbereplenish: no available buffers\n");
+				break;
+			}
+			ctlr->rb[rdt] = bp;
+			rd->addr[0] = PCIWADDR(bp->rp);
+			rd->addr[1] = 0;
+		}
+		coherence();
+		rd->status = 0;
+		rdt = NEXT(rdt, ctlr->nrd);
+		ctlr->rdfree++;
+	}
+	ctlr->rdt = rdt;
+	csr32w(ctlr, Rdt, rdt);
+}
+
+static void
+igberxinit(Ctlr* ctlr)
+{
+	int i;
+	Block *bp;
+
+	csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF);
+
+	csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
+	csr32w(ctlr, Rdbah, 0);
+	csr32w(ctlr, Rdlen, ctlr->nrd*sizeof(Rd));
+	ctlr->rdh = 0;
+	csr32w(ctlr, Rdh, 0);
+	ctlr->rdt = 0;
+	csr32w(ctlr, Rdt, 0);
+	ctlr->rdtr = 0;
+	csr32w(ctlr, Rdtr, Fpd|0);
+
+	for(i = 0; i < ctlr->nrd; i++){
+		if((bp = ctlr->rb[i]) != nil){
+			ctlr->rb[i] = nil;
+			freeb(bp);
+		}
+	}
+	igbereplenish(ctlr);
+
+	switch(ctlr->id){
+	case i82540em:
+	case i82540eplp:
+	case i82541gi:
+	case i82541pi:
+	case i82545gmc:
+	case i82546gb:
+	case i82546eb:
+	case i82547gi:
+	case i82541gi2:
+		csr32w(ctlr, Radv, 64);
+		break;
+	}
+	csr32w(ctlr, Rxdctl, (8<<WthreshSHIFT)|(8<<HthreshSHIFT)|4);
+
+	/*
+	 * Enable checksum offload.
+	 */
+	csr32w(ctlr, Rxcsum, Tuofl|Ipofl|(ETHERHDRSIZE<<PcssSHIFT));
+}
+
+static int
+igberim(void* ctlr)
+{
+	return ((Ctlr*)ctlr)->rim != 0;
+}
+
+static void
+igberproc(PROCARG(void *arg))
+{
+	Rd *rd;
+	Block *bp;
+	Ctlr *ctlr;
+	int r, rdh;
+	Ether *edev;
+
+	edev = GETARG(arg);
+	ctlr = edev->ctlr;
+
+	igberxinit(ctlr);
+	r = csr32r(ctlr, Rctl);
+	r |= Ren;
+	csr32w(ctlr, Rctl, r);
+
+	for(;;){
+		ctlr->rim = 0;
+		igbeim(ctlr, Rxt0|Rxo|Rxdmt0|Rxseq);
+		ctlr->rsleep++;
+		sleep(&ctlr->rrendez, igberim, ctlr);
+
+		rdh = ctlr->rdh;
+		for(;;){
+			rd = &ctlr->rdba[rdh];
+
+			if(!(rd->status & Rdd))
+				break;
+
+			/*
+			 * Accept eop packets with no errors.
+			 * With no errors and the Ixsm bit set,
+			 * the descriptor status Tpcs and Ipcs bits give
+			 * an indication of whether the checksums were
+			 * calculated and valid.
+			 */
+			if((rd->status & Reop) && rd->errors == 0){
+				bp = ctlr->rb[rdh];
+				ctlr->rb[rdh] = nil;
+				INCRPTR(bp, rd->length);
+				bp->next = nil;
+				if(!(rd->status & Ixsm)){
+					ctlr->ixsm++;
+					if(rd->status & Ipcs){
+						/*
+						 * IP checksum calculated
+						 * (and valid as errors == 0).
+						 */
+						ctlr->ipcs++;
+#ifndef FS
+						bp->flag |= Bipck;
+#endif
+					}
+					if(rd->status & Tcpcs){
+						/*
+						 * TCP/UDP checksum calculated
+						 * (and valid as errors == 0).
+						 */
+						ctlr->tcpcs++;
+#ifndef FS
+						bp->flag |= Btcpck|Budpck;
+#endif
+					}
+#ifndef FS
+					bp->checksum = rd->checksum;
+					bp->flag |= Bpktck;
+#endif
+				}
+				ETHERIQ(edev, bp, 1);
+			}
+			else if(ctlr->rb[rdh] != nil){
+				freeb(ctlr->rb[rdh]);
+				ctlr->rb[rdh] = nil;
+			}
+
+			memset(rd, 0, sizeof(Rd));
+			coherence();
+			ctlr->rdfree--;
+			rdh = NEXT(rdh, ctlr->nrd);
+		}
+		ctlr->rdh = rdh;
+
+		if(ctlr->rdfree < ctlr->nrd/2 || (ctlr->rim & Rxdmt0))
+			igbereplenish(ctlr);
+	}
+}
+
+static void
+igbeattach(Ether* edev)
+{
+	Block *bp;
+	Ctlr *ctlr;
+	char name[KNAMELEN];
+
+	ctlr = edev->ctlr;
+	qlock(&ctlr->alock);
+	if(ctlr->alloc != nil){
+		qunlock(&ctlr->alock);
+		return;
+	}
+
+	ctlr->nrd = ROUND(Nrd, 8);
+	ctlr->ntd = ROUND(Ntd, 8);
+	ctlr->alloc = malloc(ctlr->nrd*sizeof(Rd)+ctlr->ntd*sizeof(Td) + 127);
+	if(ctlr->alloc == nil){
+		qunlock(&ctlr->alock);
+		return;
+	}
+	ctlr->rdba = (Rd*)ROUNDUP((ulong)ctlr->alloc, 128);
+	ctlr->tdba = (Td*)(ctlr->rdba+ctlr->nrd);
+
+	ctlr->rb = malloc(ctlr->nrd*sizeof(Block*));
+	ctlr->tb = malloc(ctlr->ntd*sizeof(Block*));
+	if (ctlr->tb == nil)
+		panic("igbeattach: no mem");
+
+	if(waserror()){
+		while(ctlr->nrb > 0){
+			bp = igberballoc();
+			if (bp == nil)
+				panic("igbeattach: no mem for rcv bufs");
+			bp->free = nil;
+			freeb(bp);
+			ctlr->nrb--;
+		}
+		free(ctlr->tb);
+		ctlr->tb = nil;
+		free(ctlr->rb);
+		ctlr->rb = nil;
+		free(ctlr->alloc);
+		ctlr->alloc = nil;
+		qunlock(&ctlr->alock);
+		nexterror();
+	}
+
+	for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){
+		if((bp = allocb(Rbsz)) == nil)
+			break;
+#ifdef FS
+		bp->flags |= Mbrcvbuf;
+#endif
+		bp->free = igberbfree;
+		freeb(bp);
+	}
+
+	snprint(name, KNAMELEN, "#l%dlproc", edev->ctlrno);
+	kproc(name, igbelproc, edev);
+
+	snprint(name, KNAMELEN, "#l%drproc", edev->ctlrno);
+	kproc(name, igberproc, edev);
+
+	igbetxinit(ctlr);
+
+	qunlock(&ctlr->alock);
+	poperror();
+}
+
+static void
+igbeinterrupt(Ureg*, void* arg)
+{
+	Ctlr *ctlr;
+	Ether *edev;
+	int icr, im, txdw;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	ilock(&ctlr->imlock);
+	csr32w(ctlr, Imc, ~0);
+	im = ctlr->im;
+	txdw = 0;
+
+	while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){
+		if(icr & Lsc){
+			im &= ~Lsc;
+			ctlr->lim = icr & Lsc;
+			wakeup(&ctlr->lrendez);
+			ctlr->lintr++;
+		}
+		if(icr & (Rxt0|Rxo|Rxdmt0|Rxseq)){
+			im &= ~(Rxt0|Rxo|Rxdmt0|Rxseq);
+			ctlr->rim = icr & (Rxt0|Rxo|Rxdmt0|Rxseq);
+			wakeup(&ctlr->rrendez);
+			ctlr->rintr++;
+		}
+		if(icr & Txdw){
+			im &= ~Txdw;
+			txdw++;
+			ctlr->tintr++;
+		}
+	}
+
+	ctlr->im = im;
+	csr32w(ctlr, Ims, im);
+	iunlock(&ctlr->imlock);
+
+	if(txdw)
+		igbetransmit(edev);
+}
+
+static int
+i82543mdior(Ctlr* ctlr, int n)
+{
+	int ctrl, data, i, r;
+
+	/*
+	 * Read n bits from the Management Data I/O Interface.
+	 */
+	ctrl = csr32r(ctlr, Ctrl);
+	r = (ctrl & ~Mddo)|Mdco;
+	data = 0;
+	for(i = n-1; i >= 0; i--){
+		if(csr32r(ctlr, Ctrl) & Mdd)
+			data |= (1<<i);
+		csr32w(ctlr, Ctrl, Mdc|r);
+		csr32w(ctlr, Ctrl, r);
+	}
+	csr32w(ctlr, Ctrl, ctrl);
+
+	return data;
+}
+
+static int
+i82543mdiow(Ctlr* ctlr, int bits, int n)
+{
+	int ctrl, i, r;
+
+	/*
+	 * Write n bits to the Management Data I/O Interface.
+	 */
+	ctrl = csr32r(ctlr, Ctrl);
+	r = Mdco|Mddo|ctrl;
+	for(i = n-1; i >= 0; i--){
+		if(bits & (1<<i))
+			r |= Mdd;
+		else
+			r &= ~Mdd;
+		csr32w(ctlr, Ctrl, Mdc|r);
+		csr32w(ctlr, Ctrl, r);
+	}
+	csr32w(ctlr, Ctrl, ctrl);
+
+	return 0;
+}
+
+static int
+i82543miimir(Mii* mii, int pa, int ra)
+{
+	int data;
+	Ctlr *ctlr;
+
+	ctlr = mii->ctlr;
+
+	/*
+	 * MII Management Interface Read.
+	 *
+	 * Preamble;
+	 * ST+OP+PHYAD+REGAD;
+	 * TA + 16 data bits.
+	 */
+	i82543mdiow(ctlr, 0xFFFFFFFF, 32);
+	i82543mdiow(ctlr, 0x1800|(pa<<5)|ra, 14);
+	data = i82543mdior(ctlr, 18);
+
+	if(data & 0x10000)
+		return -1;
+
+	return data & 0xFFFF;
+}
+
+static int
+i82543miimiw(Mii* mii, int pa, int ra, int data)
+{
+	Ctlr *ctlr;
+
+	ctlr = mii->ctlr;
+
+	/*
+	 * MII Management Interface Write.
+	 *
+	 * Preamble;
+	 * ST+OP+PHYAD+REGAD+TA + 16 data bits;
+	 * Z.
+	 */
+	i82543mdiow(ctlr, 0xFFFFFFFF, 32);
+	data &= 0xFFFF;
+	data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16);
+	i82543mdiow(ctlr, data, 32);
+
+	return 0;
+}
+
+static int
+igbemiimir(Mii* mii, int pa, int ra)
+{
+	Ctlr *ctlr;
+	int mdic, timo;
+
+	ctlr = mii->ctlr;
+
+	csr32w(ctlr, Mdic, MDIrop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT));
+	mdic = 0;
+	for(timo = 64; timo; timo--){
+		mdic = csr32r(ctlr, Mdic);
+		if(mdic & (MDIe|MDIready))
+			break;
+		microdelay(1);
+	}
+
+	if((mdic & (MDIe|MDIready)) == MDIready)
+		return mdic & 0xFFFF;
+	return -1;
+}
+
+static int
+igbemiimiw(Mii* mii, int pa, int ra, int data)
+{
+	Ctlr *ctlr;
+	int mdic, timo;
+
+	ctlr = mii->ctlr;
+
+	data &= MDIdMASK;
+	csr32w(ctlr, Mdic, MDIwop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT)|data);
+	mdic = 0;
+	for(timo = 64; timo; timo--){
+		mdic = csr32r(ctlr, Mdic);
+		if(mdic & (MDIe|MDIready))
+			break;
+		microdelay(1);
+	}
+	if((mdic & (MDIe|MDIready)) == MDIready)
+		return 0;
+	return -1;
+}
+
+static int
+igbemii(Ctlr* ctlr)
+{
+	MiiPhy *phy;
+	int ctrl, p, r;
+
+	r = csr32r(ctlr, Status);
+	if(r & Tbimode)
+		return -1;
+	if((ctlr->mii = malloc(sizeof(Mii))) == nil)
+		return -1;
+	ctlr->mii->ctlr = ctlr;
+
+	ctrl = csr32r(ctlr, Ctrl);
+	ctrl |= Slu;
+
+	switch(ctlr->id){
+	case i82543gc:
+		ctrl |= Frcdplx|Frcspd;
+		csr32w(ctlr, Ctrl, ctrl);
+
+		/*
+		 * The reset pin direction (Mdro) should already
+		 * be set from the EEPROM load.
+		 * If it's not set this configuration is unexpected
+		 * so bail.
+		 */
+		r = csr32r(ctlr, Ctrlext);
+		if(!(r & Mdro))
+			return -1;
+		csr32w(ctlr, Ctrlext, r);
+		delay(20);
+		r = csr32r(ctlr, Ctrlext);
+		r &= ~Mdr;
+		csr32w(ctlr, Ctrlext, r);
+		delay(20);
+		r = csr32r(ctlr, Ctrlext);
+		r |= Mdr;
+		csr32w(ctlr, Ctrlext, r);
+		delay(20);
+
+		ctlr->mii->mir = i82543miimir;
+		ctlr->mii->miw = i82543miimiw;
+		break;
+	case i82544ei:
+	case i82547ei:
+	case i82540em:
+	case i82540eplp:
+	case i82547gi:
+	case i82541gi2:
+	case i82541gi:
+	case i82541pi:
+	case i82545gmc:
+	case i82546gb:
+	case i82546eb:
+		ctrl &= ~(Frcdplx|Frcspd);
+		csr32w(ctlr, Ctrl, ctrl);
+		ctlr->mii->mir = igbemiimir;
+		ctlr->mii->miw = igbemiimiw;
+		break;
+	default:
+		free(ctlr->mii);
+		ctlr->mii = nil;
+		return -1;
+	}
+
+	if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
+		free(ctlr->mii);
+		ctlr->mii = nil;
+		return -1;
+	}
+	USED(phy);
+//	print("oui %X phyno %d\n", phy->oui, phy->phyno);
+
+	/*
+	 * 8254X-specific PHY registers not in 802.3:
+	 *	0x10	PHY specific control
+	 *	0x14	extended PHY specific control
+	 * Set appropriate values then reset the PHY to have
+	 * changes noted.
+	 */
+	switch(ctlr->id){
+	case i82547gi:
+	case i82541gi2:
+	case i82541gi:
+	case i82541pi:
+	case i82545gmc:
+	case i82546gb:
+	case i82546eb:
+		break;
+	default:
+		r = miimir(ctlr->mii, 16);
+		r |= 0x0800;			/* assert CRS on Tx */
+		r |= 0x0060;			/* auto-crossover all speeds */
+		r |= 0x0002;			/* polarity reversal enabled */
+		miimiw(ctlr->mii, 16, r);
+
+		r = miimir(ctlr->mii, 20);
+		r |= 0x0070;			/* +25MHz clock */
+		r &= ~0x0F00;
+		r |= 0x0100;			/* 1x downshift */
+		miimiw(ctlr->mii, 20, r);
+
+		miireset(ctlr->mii);
+
+		p = 0;
+		if(ctlr->txcw & TxcwPs)
+			p |= AnaP;
+		if(ctlr->txcw & TxcwAs)
+			p |= AnaAP;
+		miiane(ctlr->mii, ~0, p, ~0);
+		break;
+	}
+	return 0;
+}
+
+static int
+at93c46io(Ctlr* ctlr, char* op, int data)
+{
+	char *lp, *p;
+	int i, loop, eecd, r;
+
+	eecd = csr32r(ctlr, Eecd);
+
+	r = 0;
+	loop = -1;
+	lp = nil;
+	for(p = op; *p != '\0'; p++){
+		switch(*p){
+		default:
+			return -1;
+		case ' ':
+			continue;
+		case ':':			/* start of loop */
+			loop = strtol(p+1, &lp, 0)-1;
+			lp--;
+			if(p == lp)
+				loop = 7;
+			p = lp;
+			continue;
+		case ';':			/* end of loop */
+			if(lp == nil)
+				return -1;
+			loop--;
+			if(loop >= 0)
+				p = lp;
+			else
+				lp = nil;
+			continue;
+		case 'C':			/* assert clock */
+			eecd |= Sk;
+			break;
+		case 'c':			/* deassert clock */
+			eecd &= ~Sk;
+			break;
+		case 'D':			/* next bit in 'data' byte */
+			if(loop < 0)
+				return -1;
+			if(data & (1<<loop))
+				eecd |= Di;
+			else
+				eecd &= ~Di;
+			break;
+		case 'O':			/* collect data output */
+			i = (csr32r(ctlr, Eecd) & Do) != 0;
+			if(loop >= 0)
+				r |= (i<<loop);
+			else
+				r = i;
+			continue;
+		case 'I':			/* assert data input */
+			eecd |= Di;
+			break;
+		case 'i':			/* deassert data input */
+			eecd &= ~Di;
+			break;
+		case 'S':			/* enable chip select */
+			eecd |= Cs;
+			break;
+		case 's':			/* disable chip select */
+			eecd &= ~Cs;
+			break;
+		}
+		csr32w(ctlr, Eecd, eecd);
+		microdelay(1);
+	}
+	if(loop >= 0)
+		return -1;
+	return r;
+}
+
+static int
+at93c46r(Ctlr* ctlr)
+{
+	ushort sum;
+	char rop[20];
+	int addr, areq, bits, data, eecd, i;
+
+	eecd = csr32r(ctlr, Eecd);
+	if(eecd & Spi){
+		print("igbe: SPI EEPROM access not implemented\n");
+		return 0;
+	}
+	if(eecd & Eesz256)
+		bits = 8;
+	else
+		bits = 6;
+	snprint(rop, sizeof(rop), "S :%dDCc;", bits+3);
+
+	sum = 0;
+
+	switch(ctlr->id){
+	default:
+		areq = 0;
+		break;
+	case i82540em:
+	case i82540eplp:
+	case i82541gi:
+	case i82541pi:
+	case i82547gi:
+	case i82541gi2:
+	case i82545gmc:
+	case i82546gb:
+	case i82546eb:
+		areq = 1;
+		csr32w(ctlr, Eecd, eecd|Areq);
+		for(i = 0; i < 1000; i++){
+			if((eecd = csr32r(ctlr, Eecd)) & Agnt)
+				break;
+			microdelay(5);
+		}
+		if(!(eecd & Agnt)){
+			print("igbe: not granted EEPROM access\n");
+			goto release;
+		}
+		break;
+	}
+
+	for(addr = 0; addr < 0x40; addr++){
+		/*
+		 * Read a word at address 'addr' from the Atmel AT93C46
+		 * 3-Wire Serial EEPROM or compatible. The EEPROM access is
+		 * controlled by 4 bits in Eecd. See the AT93C46 datasheet
+		 * for protocol details.
+		 */
+		if(at93c46io(ctlr, rop, (0x06<<bits)|addr) != 0){
+			print("igbe: can't set EEPROM address 0x%2.2X\n", addr);
+			goto release;
+		}
+		data = at93c46io(ctlr, ":16COc;", 0);
+		at93c46io(ctlr, "sic", 0);
+		ctlr->eeprom[addr] = data;
+		sum += data;
+	}
+
+release:
+	if(areq)
+		csr32w(ctlr, Eecd, eecd & ~Areq);
+	return sum;
+}
+
+static int
+igbedetach(Ctlr* ctlr)
+{
+	int r, timeo;
+
+	if (ctlr == nil)
+		return -1;
+
+	/*
+	 * Perform a device reset to get the chip back to the
+	 * power-on state, followed by an EEPROM reset to read
+	 * the defaults for some internal registers.
+	 *
+	 * The alpha needs the rest of this routine to run at splhi
+	 * or else the card interrupts and resets the processor.  Don't know
+	 * why.  Since the alpha isn't important any more, let's ignore that.
+	 */
+	csr32w(ctlr, Imc, ~0);
+	csr32w(ctlr, Rctl, 0);
+	csr32w(ctlr, Tctl, 0);
+
+	delay(10);		/* was 10 then 100 */
+
+	csr32w(ctlr, Ctrl, Devrst);
+	delay(1);		/* was 100 */
+	for(timeo = 0; timeo < 1000; timeo++){
+		if(!(csr32r(ctlr, Ctrl) & Devrst))
+			break;
+		delay(1);
+	}
+	if(csr32r(ctlr, Ctrl) & Devrst)
+		return -1;
+	r = csr32r(ctlr, Ctrlext);
+	csr32w(ctlr, Ctrlext, r|Eerst);
+	delay(1);
+	for(timeo = 0; timeo < 1000; timeo++){
+		if(!(csr32r(ctlr, Ctrlext) & Eerst))
+			break;
+		delay(1);
+	}
+	if(csr32r(ctlr, Ctrlext) & Eerst)
+		return -1;
+
+	switch(ctlr->id){
+	default:
+		break;
+	case i82540em:
+	case i82540eplp:
+	case i82541gi:
+	case i82541pi:
+	case i82547gi:
+	case i82541gi2:
+	case i82545gmc:
+	case i82546gb:
+	case i82546eb:
+		r = csr32r(ctlr, Manc);
+		r &= ~Arpen;
+		csr32w(ctlr, Manc, r);
+		break;
+	}
+
+	csr32w(ctlr, Imc, ~0);
+	delay(1);		/* was 100 */
+	for(timeo = 0; timeo < 1000; timeo++){
+		if(!csr32r(ctlr, Icr))
+			break;
+		delay(1);
+	}
+	if(csr32r(ctlr, Icr))
+		return -1;
+	return 0;
+}
+
+static void
+igbeshutdown(Ether* ether)
+{
+iprint("etherigbe shutting down\n");
+	igbedetach(ether->ctlr);
+}
+
+int
+etherigbereset(Ctlr* ctlr)
+{
+	int ctrl, i, pause, r, swdpio, txcw;
+
+	if(igbedetach(ctlr))
+		return -1;
+	/*
+	 * Read the EEPROM, validate the checksum
+	 * then get the device back to a power-on state.
+	 */
+	if((r = at93c46r(ctlr)) != 0xBABA){
+		print("igbe: bad EEPROM checksum - 0x%4.4uX\n", r);
+		return -1;
+	}
+
+	/*
+	 * Snarf and set up the receive addresses.
+	 * There are 16 addresses. The first should be the MAC address.
+	 * The others are cleared and not marked valid (MS bit of Rah).
+	 */
+	if ((ctlr->id == i82546gb || ctlr->id == i82546eb) && BUSFNO(ctlr->pcidev->tbdf) == 1)
+		ctlr->eeprom[Ea+2] += 0x100;	// second interface
+	for(i = Ea; i < Eaddrlen/2; i++){
+		ctlr->ra[2*i] = ctlr->eeprom[i];
+		ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8;
+	}
+	r = (ctlr->ra[3]<<24)|(ctlr->ra[2]<<16)|(ctlr->ra[1]<<8)|ctlr->ra[0];
+	csr32w(ctlr, Ral, r);
+	r = 0x80000000|(ctlr->ra[5]<<8)|ctlr->ra[4];
+	csr32w(ctlr, Rah, r);
+	for(i = 1; i < 16; i++){
+		csr32w(ctlr, Ral+i*8, 0);
+		csr32w(ctlr, Rah+i*8, 0);
+	}
+
+	/*
+	 * Clear the Multicast Table Array.
+	 * It's a 4096 bit vector accessed as 128 32-bit registers.
+	 */
+	memset(ctlr->mta, 0, sizeof(ctlr->mta));
+	for(i = 0; i < 128; i++)
+		csr32w(ctlr, Mta+i*4, 0);
+
+	/*
+	 * Just in case the Eerst didn't load the defaults
+	 * (doesn't appear to fully on the 8243GC), do it manually.
+	 */
+	if (ctlr->id == i82543gc) {	// 82543
+		txcw = csr32r(ctlr, Txcw);
+		txcw &= ~(TxcwAne|TxcwPauseMASK|TxcwFd);
+		ctrl = csr32r(ctlr, Ctrl);
+		ctrl &= ~(SwdpioloMASK|Frcspd|Ilos|Lrst|Fd);
+
+		if(ctlr->eeprom[Icw1] & 0x0400){
+			ctrl |= Fd;
+			txcw |= TxcwFd;
+//print("igbe: full duplex\n");
+		}
+//else print("igbe: half duplex\n");
+		if(ctlr->eeprom[Icw1] & 0x0200)
+			ctrl |= Lrst;
+		if(ctlr->eeprom[Icw1] & 0x0010)
+			ctrl |= Ilos;
+		if(ctlr->eeprom[Icw1] & 0x0800)
+			ctrl |= Frcspd;
+		swdpio = (ctlr->eeprom[Icw1] & 0x01E0)>>5;
+		ctrl |= swdpio<<SwdpioloSHIFT;
+		csr32w(ctlr, Ctrl, ctrl);
+
+		ctrl = csr32r(ctlr, Ctrlext);
+		ctrl &= ~(Ips|SwdpiohiMASK);
+		swdpio = (ctlr->eeprom[Icw2] & 0x00F0)>>4;
+		if(ctlr->eeprom[Icw1] & 0x1000)
+			ctrl |= Ips;
+		ctrl |= swdpio<<SwdpiohiSHIFT;
+		csr32w(ctlr, Ctrlext, ctrl);
+
+		if(ctlr->eeprom[Icw2] & 0x0800)
+			txcw |= TxcwAne;
+		pause = (ctlr->eeprom[Icw2] & 0x3000)>>12;
+		txcw |= pause<<TxcwPauseSHIFT;
+		switch(pause){
+		default:
+			ctlr->fcrtl = 0x00002000;
+			ctlr->fcrth = 0x00004000;
+			txcw |= TxcwAs|TxcwPs;
+			break;
+		case 0:
+			ctlr->fcrtl = 0x00002000;
+			ctlr->fcrth = 0x00004000;
+			break;
+		case 2:
+			ctlr->fcrtl = 0;
+			ctlr->fcrth = 0;
+			txcw |= TxcwAs;
+			break;
+		}
+		ctlr->txcw = txcw;
+		csr32w(ctlr, Txcw, txcw);
+	}
+
+	/*
+	 * Flow control - values from the datasheet.
+	 */
+	csr32w(ctlr, Fcal, 0x00C28001);
+	csr32w(ctlr, Fcah, 0x00000100);
+	csr32w(ctlr, Fct, 0x00008808);
+	csr32w(ctlr, Fcttv, 0x00000100);
+
+	csr32w(ctlr, Fcrtl, ctlr->fcrtl);
+	csr32w(ctlr, Fcrth, ctlr->fcrth);
+
+	if(!(csr32r(ctlr, Status) & Tbimode) && igbemii(ctlr) < 0)
+		return -1;
+	return 0;
+}
+
+static void
+igbepci(void)
+{
+	int cls;
+	Pcidev *p;
+	Ctlr *ctlr;
+	void *mem;
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		/* ccru is a short in the FS kernel, thus the cast to uchar */
+		if(p->ccrb != 0x02 ||
+#ifdef FS
+		    (uchar)
+#endif
+		    p->ccru != 0)
+			continue;
+
+		switch((p->did<<16)|p->vid){
+		default:
+			continue;
+		case i82543gc:
+		case i82544ei:
+		case i82547ei:
+		case i82540em:
+		case i82540eplp:
+		case i82541gi:
+		case i82547gi:
+		case i82541gi2:
+		case i82541pi:
+		case i82545gmc:
+		case i82546gb:
+		case i82546eb:
+			break;
+		}
+
+		/* cast for FS */
+		mem = (void *)vmap(p->mem[0].bar & ~0x0F, p->mem[0].size);
+		if(mem == nil){
+			print("igbe: can't map %8.8luX\n", p->mem[0].bar);
+			continue;
+		}
+
+		/*
+		 * from etherga620.c:
+		 * If PCI Write-and-Invalidate is enabled set the max write DMA
+		 * value to the host cache-line size (32 on Pentium or later).
+		 */
+		if(p->pcr & MemWrInv){
+			cls = pcicfgr8(p, PciCLS) * 4;
+			if(cls != CACHELINESZ)
+				pcicfgw8(p, PciCLS, CACHELINESZ/4);
+		}
+
+		cls = pcicfgr8(p, PciCLS);
+		switch(cls){
+			default:
+				print("igbe: unexpected CLS - %d bytes\n",
+					cls*sizeof(long));
+				break;
+			case 0x00:
+			case 0xFF:
+				/* alphapc 164lx returns 0 */
+				print("igbe: unusable PciCLS: %d, using %d longs\n",
+					cls, CACHELINESZ/sizeof(long));
+				cls = CACHELINESZ/sizeof(long);
+				pcicfgw8(p, PciCLS, cls);
+				break;
+			case 0x08:
+			case 0x10:
+				break;
+		}
+		ctlr = malloc(sizeof(Ctlr));
+		if (ctlr == nil)
+			panic("ibgepci: no mem");
+		ctlr->port = p->mem[0].bar & ~0x0F;
+		ctlr->pcidev = p;
+		ctlr->id = (p->did<<16)|p->vid;
+		ctlr->cls = cls*4;
+		ctlr->nic = mem;
+
+		if(etherigbereset(ctlr)){
+			free(ctlr);
+			continue;
+		}
+		pcisetbme(p);
+
+		if(igbectlrhead != nil)
+			igbectlrtail->next = ctlr;
+		else
+			igbectlrhead = ctlr;
+		igbectlrtail = ctlr;
+	}
+}
+
+int
+igbepnp(Ether* edev)
+{
+	Ctlr *ctlr;
+
+	if(igbectlrhead == nil)
+		igbepci();
+
+	/*
+	 * Any adapter matches if no edev->port is supplied,
+	 * otherwise the ports must match.
+	 */
+	for(ctlr = igbectlrhead; ctlr != nil; ctlr = ctlr->next){
+		if(ctlr->active)
+			continue;
+		if(edev->port == 0 || edev->port == ctlr->port){
+			ctlr->active = 1;
+			break;
+		}
+	}
+	if(ctlr == nil)
+		return -1;
+
+	edev->ctlr = ctlr;
+	edev->port = ctlr->port;
+	edev->irq = ctlr->pcidev->intl;
+	edev->tbdf = ctlr->pcidev->tbdf;
+	edev->mbps = 1000;
+	memmove(edev->ea, ctlr->ra, Eaddrlen);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	edev->attach = igbeattach;
+	edev->transmit = igbetransmit;
+	edev->interrupt = igbeinterrupt;
+#ifndef FS
+	edev->ifstat = igbeifstat;
+	edev->ctl = igbectl;
+
+	edev->arg = edev;
+	edev->promiscuous = igbepromiscuous;
+	edev->shutdown = igbeshutdown;
+	edev->multicast = igbemulticast;
+#endif
+	return 0;
+}
+
+#ifndef FS
+void
+etherigbebothlink(void)
+{
+	addethercard("i82543", igbepnp);
+	addethercard("igbe", igbepnp);
+}
+#endif

+ 185 - 10
sys/src/fs/pc/ethermii.c

@@ -24,7 +24,7 @@ int
 mii(Mii* mii, int mask)
 {
 	MiiPhy *miiphy;
-	int bit, phy, r, rmask;
+	int bit, phyno, r, rmask;
 
 	/*
 	 * Probe through mii for PHYs in mask;
@@ -33,30 +33,31 @@ mii(Mii* mii, int mask)
 	 * the Mii information.
 	 */
 	rmask = 0;
-	for(phy = 0; phy < NMiiPhy; phy++){
-		bit = 1<<phy;
+	for(phyno = 0; phyno < NMiiPhy; phyno++){
+		bit = 1<<phyno;
 		if(!(mask & bit))
 			continue;
 		if(mii->mask & bit){
 			rmask |= bit;
 			continue;
 		}
-		if(mii->mir(mii, phy, Bmsr) == -1)
+		if(mii->mir(mii, phyno, Bmsr) == -1)
 			continue;
 		if((miiphy = malloc(sizeof(MiiPhy))) == nil)
 			continue;
 
 		miiphy->mii = mii;
-		r = mii->mir(mii, phy, Phyidr1);
+		r = mii->mir(mii, phyno, Phyidr1);
 		miiphy->oui = (r & 0x3FFF)<<6;
-		r = mii->mir(mii, phy, Phyidr2);
+		r = mii->mir(mii, phyno, Phyidr2);
 		miiphy->oui |= r>>10;
-		miiphy->phy = phy;
+		miiphy->phyno = phyno;
 
-		for(r = 0; r < NMiiPhyr; r++)
-			miiphy->r[r] = mii->mir(mii, phy, r);
+		miiphy->anar = ~0;
+		miiphy->fc = ~0;
+		miiphy->mscr = ~0;
 
-		mii->phy[phy] = miiphy;
+		mii->phy[phyno] = miiphy;
 		if(mii->curphy == nil)
 			mii->curphy = miiphy;
 		mii->mask |= bit;
@@ -66,3 +67,177 @@ mii(Mii* mii, int mask)
 	}
 	return rmask;
 }
+
+int
+miimir(Mii* mii, int r)
+{
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	return mii->mir(mii, mii->curphy->phyno, r);
+}
+
+int
+miimiw(Mii* mii, int r, int data)
+{
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	return mii->miw(mii, mii->curphy->phyno, r, data);
+}
+
+int
+miireset(Mii* mii)
+{
+	int bmcr;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	bmcr = mii->mir(mii, mii->curphy->phyno, Bmcr);
+	bmcr |= BmcrR;
+	mii->miw(mii, mii->curphy->phyno, Bmcr, bmcr);
+	microdelay(1);
+
+	return 0;
+}
+
+int
+miiane(Mii* mii, int a, int p, int e)
+{
+	int anar, bmsr, mscr, r, phyno;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	phyno = mii->curphy->phyno;
+
+	bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & BmsrAna))
+		return -1;
+
+	if(a != ~0)
+		anar = (AnaTXFD|AnaTXHD|Ana10FD|Ana10HD) & a;
+	else if(mii->curphy->anar != ~0)
+		anar = mii->curphy->anar;
+	else{
+		anar = mii->mir(mii, phyno, Anar);
+		anar &= ~(AnaAP|AnaP|AnaT4|AnaTXFD|AnaTXHD|Ana10FD|Ana10HD);
+		if(bmsr & Bmsr10THD)
+			anar |= Ana10HD;
+		if(bmsr & Bmsr10TFD)
+			anar |= Ana10FD;
+		if(bmsr & Bmsr100TXHD)
+			anar |= AnaTXHD;
+		if(bmsr & Bmsr100TXFD)
+			anar |= AnaTXFD;
+	}
+	mii->curphy->anar = anar;
+
+	if(p != ~0)
+		anar |= (AnaAP|AnaP) & p;
+	else if(mii->curphy->fc != ~0)
+		anar |= mii->curphy->fc;
+	mii->curphy->fc = (AnaAP|AnaP) & anar;
+
+	if(bmsr & BmsrEs){
+		mscr = mii->mir(mii, phyno, Mscr);
+		mscr &= ~(Mscr1000TFD|Mscr1000THD);
+		if(e != ~0)
+			mscr |= (Mscr1000TFD|Mscr1000THD) & e;
+		else if(mii->curphy->mscr != ~0)
+			mscr = mii->curphy->mscr;
+		else{
+			r = mii->mir(mii, phyno, Esr);
+			if(r & Esr1000THD)
+				mscr |= Mscr1000THD;
+			if(r & Esr1000TFD)
+				mscr |= Mscr1000TFD;
+		}
+		mii->curphy->mscr = mscr;
+		mii->miw(mii, phyno, Mscr, mscr);
+	}
+	mii->miw(mii, phyno, Anar, anar);
+
+	r = mii->mir(mii, phyno, Bmcr);
+	if(!(r & BmcrR)){
+		r |= BmcrAne|BmcrRan;
+		mii->miw(mii, phyno, Bmcr, r);
+	}
+
+	return 0;
+}
+
+int
+miistatus(Mii* mii)
+{
+	MiiPhy *phy;
+	int anlpar, bmsr, p, r, phyno;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	phy = mii->curphy;
+	phyno = phy->phyno;
+
+	/*
+	 * Check Auto-Negotiation is complete and link is up.
+	 * (Read status twice as the Ls bit is sticky).
+	 */
+	bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & (BmsrAnc|BmsrAna)))
+{
+//print("miistatus 1\n");
+		return -1;
+}
+
+	bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & BmsrLs)){
+//print("miistatus 2\n");
+		phy->link = 0;
+		return -1;
+	}
+
+	phy->speed = phy->fd = phy->rfc = phy->tfc = 0;
+	if(phy->mscr){
+		r = mii->mir(mii, phyno, Mssr);
+		if((phy->mscr & Mscr1000TFD) && (r & Mssr1000TFD)){
+			phy->speed = 1000;
+			phy->fd = 1;
+		}
+		else if((phy->mscr & Mscr1000THD) && (r & Mssr1000THD))
+			phy->speed = 1000;
+	}
+
+	anlpar = mii->mir(mii, phyno, Anlpar);
+	if(phy->speed == 0){
+		r = phy->anar & anlpar;
+		if(r & AnaTXFD){
+			phy->speed = 100;
+			phy->fd = 1;
+		}
+		else if(r & AnaTXHD)
+			phy->speed = 100;
+		else if(r & Ana10FD){
+			phy->speed = 10;
+			phy->fd = 1;
+		}
+		else if(r & Ana10HD)
+			phy->speed = 10;
+	}
+	if(phy->speed == 0)
+{
+//print("miistatus 3\n");
+		return -1;
+}
+
+	if(phy->fd){
+		p = phy->fc;
+		r = anlpar & (AnaAP|AnaP);
+		if(p == AnaAP && r == (AnaAP|AnaP))
+			phy->tfc = 1;
+		else if(p == (AnaAP|AnaP) && r == AnaAP)
+			phy->rfc = 1;
+		else if((p & AnaP) && (r & AnaP))
+			phy->rfc = phy->tfc = 1;
+	}
+
+	phy->link = 1;
+
+	return 0;
+}

+ 77 - 9
sys/src/fs/pc/ethermii.h

@@ -11,14 +11,75 @@ enum {					/* registers */
 	Aner		= 0x06,		/* AN Expansion */
 	Annptr		= 0x07,		/* AN Next Page TX */
 	Annprr		= 0x08,		/* AN Next Page RX */
-	Gbtcr		= 0x09,		/* 1000BASE-T Control */
-	Gbtsr		= 0x0A,		/* 1000BASE-T Status */
-	Gbscr		= 0x0F,		/* 1000BASE-T Extended Status */
+	Mscr		= 0x09,		/* MASTER-SLAVE Control */
+	Mssr		= 0x0A,		/* MASTER-SLAVE Status */
+	Esr		= 0x0F,		/* Extended Status */
 
 	NMiiPhyr	= 32,
 	NMiiPhy		= 32,
 };
 
+enum {					/* Bmcr */
+	BmcrSs1		= 0x0040,	/* Speed Select[1] */
+	BmcrCte		= 0x0080,	/* Collision Test Enable */
+	BmcrDm		= 0x0100,	/* Duplex Mode */
+	BmcrRan		= 0x0200,	/* Restart Auto-Negotiation */
+	BmcrI		= 0x0400,	/* Isolate */
+	BmcrPd		= 0x0800,	/* Power Down */
+	BmcrAne		= 0x1000,	/* Auto-Negotiation Enable */
+	BmcrSs0		= 0x2000,	/* Speed Select[0] */
+	BmcrLe		= 0x4000,	/* Loopback Enable */
+	BmcrR		= 0x8000,	/* Reset */
+};
+
+enum {					/* Bmsr */
+	BmsrEc		= 0x0001,	/* Extended Capability */
+	BmsrJd		= 0x0002,	/* Jabber Detect */
+	BmsrLs		= 0x0004,	/* Link Status */
+	BmsrAna		= 0x0008,	/* Auto-Negotiation Ability */
+	BmsrRf		= 0x0010,	/* Remote Fault */
+	BmsrAnc		= 0x0020,	/* Auto-Negotiation Complete */
+	BmsrPs		= 0x0040,	/* Preamble Suppression Capable */
+	BmsrEs		= 0x0100,	/* Extended Status */
+	Bmsr100T2HD	= 0x0200,	/* 100BASE-T2 HD Capable */
+	Bmsr100T2FD	= 0x0400,	/* 100BASE-T2 FD Capable */
+	Bmsr10THD	= 0x0800,	/* 100BASE-T HD Capable */
+	Bmsr10TFD	= 0x1000,	/* 10BASE-T FD Capable */
+	Bmsr100TXHD	= 0x2000,	/* 100BASE-TX HD Capable */
+	Bmsr100TXFD	= 0x4000,	/* 100BASE-TX FD Capable */
+	Bmsr100T4	= 0x8000,	/* 100BASE-T4 Capable */
+};
+
+enum {					/* Anar/Anlpar */
+	Ana10HD		= 0x0020,	/* Advertise 10BASE-T */
+	Ana10FD		= 0x0040,	/* Advertise 10BASE-T FD */
+	AnaTXHD		= 0x0080,	/* Advertise 100BASE-TX */
+	AnaTXFD		= 0x0100,	/* Advertise 100BASE-TX FD */
+	AnaT4		= 0x0200,	/* Advertise 100BASE-T4 */
+	AnaP		= 0x0400,	/* Pause */
+	AnaAP		= 0x0800,	/* Asymmetrical Pause */
+	AnaRf		= 0x2000,	/* Remote Fault */
+	AnaAck		= 0x4000,	/* Acknowledge */
+	AnaNp		= 0x8000,	/* Next Page Indication */
+};
+
+enum {					/* Mscr */
+	Mscr1000THD	= 0x0100,	/* Advertise 1000BASE-T HD */
+	Mscr1000TFD	= 0x0200,	/* Advertise 1000BASE-T FD */
+};
+
+enum {					/* Mssr */
+	Mssr1000THD	= 0x0400,	/* Link Partner 1000BASE-T HD able */
+	Mssr1000TFD	= 0x0800,	/* Link Partner 1000BASE-T FD able */
+};
+
+enum {					/* Esr */
+	Esr1000THD	= 0x1000,	/* 1000BASE-T HD Capable */
+	Esr1000TFD	= 0x2000,	/* 1000BASE-T FD Capable */
+	Esr1000XHD	= 0x4000,	/* 1000BASE-X HD Capable */
+	Esr1000XFD	= 0x8000,	/* 1000BASE-X FD Capable */
+};
+
 typedef struct Mii {
 	Lock;
 	int	nphy;
@@ -28,21 +89,28 @@ typedef struct Mii {
 
 	void*	ctlr;
 	int	(*mir)(Mii*, int, int);
-	void	(*miw)(Mii*, int, int, int);
+	int	(*miw)(Mii*, int, int, int);
 } Mii;
 
 typedef struct MiiPhy {
 	Mii*	mii;
 	int	oui;
-	int	phy;
+	int	phyno;
 
-	ushort	r[NMiiPhyr];
+	int	anar;
+	int	fc;
+	int	mscr;
 
-	int	valid;
 	int	link;
 	int	speed;
-	int	duplex;
-	int	pause;
+	int	fd;
+	int	rfc;
+	int	tfc;
 };
 
 extern int mii(Mii*, int);
+extern int miiane(Mii*, int, int, int);
+extern int miimir(Mii*, int);
+extern int miimiw(Mii*, int, int);
+extern int miireset(Mii*);
+extern int miistatus(Mii*);

+ 14 - 14
sys/src/fs/pc/floppy.c

@@ -85,7 +85,7 @@ struct Type
 	int	heads;		/* number of heads */
 	int	steps;		/* steps per cylinder */
 	int	tracks;		/* tracks/disk */
-	int	gpl;		/* intersector gap length for read/write */	
+	int	gpl;		/* intersector gap length for read/write */
 	int	fgpl;		/* intersector gap length for format */
 	int	rate;		/* rate code */
 
@@ -137,7 +137,7 @@ struct Floppy
 	int	dt;
 	int	dev;
 
-	ulong	lasttouched;	/* time last touched */
+	Timet	lasttouched;	/* time last touched */
 	int	cyl;		/* current cylinder */
 	int	confused;	/* needs to be recalibrated (or worse) */
 	long	offset;		/* current offset */
@@ -455,7 +455,7 @@ floppypos(Floppy *dp, long off)
 				*dp->t->sectors;
 	}
 
-	dp->lasttouched = MACHP(0)->ticks;	
+	dp->lasttouched = MACHP(0)->ticks;
 	fl.intr = 0;
 }
 
@@ -532,8 +532,8 @@ floppyrecal(Floppy *dp)
 	return 0;
 }
 
-vlong
-floppyseek(int dev, vlong off)
+Devsize
+floppyseek(int dev, Devsize off)
 {
 	Floppy *dp;
 
@@ -679,13 +679,13 @@ floppyxfer(Floppy *dp, int cmd, void *a, long n)
 	return dp->len;
 }
 
-long
+Off
 floppyread(int dev, void *a, long n)
 {
 	Floppy *dp;
-	long rv, i, nn, offset, sec;
-	uchar *aa;
 	int tries;
+	Off rv, i, nn, offset, sec;
+	uchar *aa;
 
 	dp = &fl.d[dev];
 
@@ -693,7 +693,7 @@ floppyread(int dev, void *a, long n)
 	qlock(&fl);
 	floppypos(dp, dp->offset);
 	offset = dp->offset;
-	sec = dp->tsec + dp->t->sectors*dp->thead;
+	sec = dp->tsec + (Off)dp->t->sectors*(Off)dp->thead;
 	n = dp->len;
 	if(fl.ccyl==dp->tcyl && fl.cdev==dev)
 		goto out;
@@ -701,8 +701,8 @@ floppyread(int dev, void *a, long n)
 	fl.ccyl = -1;
 	fl.cdev = dev;
 	aa = fl.ccache;
-	nn = dp->t->bytes*dp->t->sectors*dp->t->heads;
-	dp->offset = dp->tcyl*nn;
+	nn = (Off)dp->t->bytes * (Off)dp->t->sectors * (Off)dp->t->heads;
+	dp->offset = dp->tcyl * nn;
 	for(rv = 0; rv < nn; rv += i){
 		i = 0;
 		for(tries = 0; tries < dp->maxtries; tries++){
@@ -727,13 +727,13 @@ out:
 	return n;
 }
 
-long
+Off
 floppywrite(int dev, void *a, long n)
 {
 	Floppy *dp;
-	long rv, i, offset;
-	uchar *aa;
 	int tries;
+	Off rv, i, offset;
+	uchar *aa;
 
 	dp = &fl.d[dev];
 

+ 11 - 2
sys/src/fs/pc/l.s

@@ -3,6 +3,7 @@
 #define OP16	BYTE	$0x66
 #define NOP	XCHGL	AX,AX
 #define CPUID	BYTE $0x0F; BYTE $0xA2	/* CPUID, argument in AX */
+#define WRMSR	BYTE $0x0F; BYTE $0x30	/* WRMSR, argument in AX/DX (lo/hi) */
 #define RDMSR	BYTE $0x0F; BYTE $0x32	/* RDMSR, result in AX/DX (lo/hi) */
 #define RDTSC 	BYTE $0x0F; BYTE $0x31
 
@@ -251,6 +252,7 @@ TEXT	tas(SB),$0
 	RET
 
 TEXT wbflush(SB), $0
+	XORL	AX, AX
 	CPUID
 	RET
 
@@ -312,7 +314,7 @@ TEXT	getcr2(SB),$0		/* fault address */
 	MOVL	CR0,AX;\
 	ANDL	$~0x4,AX	/* EM=0 */;\
 	MOVL	AX,CR0
-	
+
 TEXT	fpoff(SB),$0		/* turn off floating point */
 	FPOFF
 	RET
@@ -560,7 +562,7 @@ TEXT	getstatus(SB),$0
 	POPL	AX
 	RET
 
-TEXT rdtsc(SB), $0				/* time stamp counter; cycles since power up */
+TEXT _cycles(SB), $0				/* time stamp counter; cycles since power up */
 	RDTSC
 	MOVL	vlong+0(FP), CX			/* &vlong */
 	MOVL	AX, 0(CX)			/* lo */
@@ -575,6 +577,13 @@ TEXT rdmsr(SB), $0				/* model-specific register */
 	MOVL	DX, 4(CX)			/* hi */
 	RET
 
+TEXT wrmsr(SB), $0
+	MOVL	index+0(FP), CX
+	MOVL	lo+4(FP), AX
+	MOVL	hi+8(FP), DX
+	WRMSR
+	RET
+
 /*
  * Try to determine the CPU type which requires fiddling with EFLAGS.
  * If the Id bit can be toggled then the CPUID instruciton can be used

+ 1 - 2
sys/src/fs/pc/malloc.c

@@ -136,8 +136,7 @@ iobufinit(void)
 	i = 0;
 	for(mbp = mconf.bank; mbp < &mconf.bank[mconf.nbank]; mbp++)
 		i += mbp->limit - mbp->base;
-	print("	mem left = %d\n", i);
-	print("		out of = %ld\n", conf.mem);
+	print("	mem left = %,d, out of %,ld\n", i, conf.mem);
 	/* paranoia: add this command as late as is easy */
 	cmd_install("memory", "-- print ranges of memory banks", cmd_memory);
 }

+ 2 - 1
sys/src/fs/pc/mkfile

@@ -9,4 +9,5 @@ PCSFILES=`{builtin cd ../pc;echo *.s | sed 's/ /|/g; s/\.s//g'}
 $ETHER:	../pc/etherif.h
 
 dosfs.$O nvr.$O:	../pc/dosfs.h
-compat.$O ether83815.$O etherdp83820.$O ether8139.$O: ../pc/compat.h
+compat.$O ether8139.$O ether83815.$O etherdp83820.$O etherigbe.$O etherif.$O\
+	ethermii.$O sdata.$O sdscsi.$O: ../pc/compat.h

+ 2 - 2
sys/src/fs/pc/pc.c

@@ -118,7 +118,7 @@ cistrcmp(char* a, char* b)
 	for(;;){
 		ac = *a++;
 		bc = *b++;
-	
+
 		if(ac >= 'A' && ac <= 'Z')
 			ac = 'a' + (ac - 'A');
 		if(bc >= 'A' && bc <= 'Z')
@@ -170,7 +170,7 @@ getconf(char *name)
 
 /* memory map */
 /* the file server kernel will only see MAXMEG megabytes of RAM at most.  */
-#define MAXMEG 1024
+#define MAXMEG 1791		/* 1.75GB-1MB, to avoid overshooting 1.75GB */
 
 char mmap[MAXMEG+2];
 Mconf mconf;

+ 27 - 1
sys/src/fs/pc/pci.c

@@ -18,6 +18,16 @@ enum {					/* configuration mechanism #1 */
 	MaxUBN		= 255,
 };
 
+enum
+{					/* command register */
+	IOen		= (1<<0),
+	MEMen		= (1<<1),
+	MASen		= (1<<2),
+	MemWrInv	= (1<<4),
+	PErrEn		= (1<<6),
+	SErrEn		= (1<<8),
+};
+
 static Lock pcicfglock;
 static Lock pcicfginitlock;
 static int pcicfgmode = -1;
@@ -94,9 +104,16 @@ pciscan(int bno, Pcidev** list)
 				pcilist = p;
 			pcitail = p;
 
-			p->intl = pcicfgr8(p, PciINTL);
+			p->rid = pcicfgr8(p, PciRID);
+			p->ccrp = pcicfgr8(p, PciCCRp);
+			p->ccrb = pcicfgr8(p, PciCCRb);
+			p->pcr = pcicfgr32(p, PciPCR);
+			/* ccru is uchar in cpu kernel */
+			/* p->ccru = pcicfgr8(p, PciCCRu); */
 			p->ccru = pcicfgr16(p, PciCCRu);
 
+			p->intl = pcicfgr8(p, PciINTL);
+
 			/*
 			 * If the device is a multi-function device adjust the
 			 * loop count so all possible functions are checked.
@@ -506,6 +523,8 @@ vid2name(int vid)
 		return "sis";
 	case 0x104b:
 		return "mylex";
+	case 0x105a:
+		return "promise";
 	case 0x105d:
 		return "number9";
 	case 0x10a9:
@@ -600,3 +619,10 @@ pcisetbme(Pcidev* p)
 	pcr |= 0x04;
 	pcicfgw16(p, PciPCR, pcr);
 }
+
+void
+pciclrbme(Pcidev* p)
+{
+	p->pcr &= ~MASen;
+	pcicfgw16(p, PciPCR, p->pcr);
+}

+ 29 - 24
sys/src/fs/pc/scsincr53c8xx.c

@@ -13,7 +13,7 @@
  * Read/write mismatch recovery may fail on 53c1010s. Really need to get a manual.
 */
 
-#define MAXTARGET	8		/* can be 8 or 16 */
+#define MAXTARGET	16		/* can be 8 or 16 */
 
 /* Define one or the other ... */
 //#define CPU
@@ -187,7 +187,7 @@ typedef struct Ncr {
 	uchar dcntl;
 
 	uchar adder[4];	/* 3c */
-	
+
 	uchar sien0;	/* 40 */
 	uchar sien1;
 	uchar sist0;
@@ -421,7 +421,7 @@ oprint(char *format, ...)
 		debuglast = debugbuf;
 	debuglast = doprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1));
 	splx(s);
-	iflush();	
+	iflush();
 }
 #endif
 
@@ -510,7 +510,7 @@ dumpncrregs(Controller *c, int intr)
 		else
 			KPRINT("\n");
 	}
-}	
+}
 
 static int
 chooserate(Controller *c, int tpf, int *scfp, int *xferpp)
@@ -830,7 +830,7 @@ softreset(Controller *c)
 
 	n->stime0 = 0xdd;		/* about 0.5 second timeout on each device */
 	n->scntl0 |= 0x8;		/* Enable parity checking */
-	
+
 	/* continued setup */
 	n->sien0 = 0x8f;
 	n->sien1 = 0x04;
@@ -940,7 +940,7 @@ msgsm(Dsa *dsa, Controller *c, int msg, int *cont, int *wakeme)
 			return;
 		}
 		break;
-		
+
 	case NeitherDone:
 	case WideDone:
 	case BothDone:
@@ -1154,14 +1154,12 @@ write_mismatch_recover(Controller *c, Ncr *n, Dsa *dsa)
 static void
 interrupt(Ureg *ur, void *a)
 {
-	uchar istat;
+	int wakeme = 0, cont = -1;
+	uchar istat, dstat;
 	ushort sist;
-	uchar dstat;
-	int wakeme = 0;
-	int cont = -1;
-	Dsa *dsa;
 	Controller *c = a;
 	Ncr *n = c->n;
+	Dsa *dsa;
 
 	USED(ur);
 	if (DEBUG(1))
@@ -1197,8 +1195,11 @@ interrupt(Ureg *ur, void *a)
 
 	sist = (n->sist1<<8)|n->sist0;	/* BUG? can two-byte read be inconsistent? */
 	dstat = n->dstat;
+
 	dsa = (Dsa *)DMASEG_TO_KADDR(legetl(n->dsa));
 	c->running = 0;
+	if (dsa == nil || dsa == (Dsa *)-1)
+		panic("53c8xx: dsa == %ld in interrupt; bad controller", (long)dsa);
 	if (istat & Sip) {
 		if (DEBUG(1))
 			IPRINT("sist = %.4x\n", sist);
@@ -1285,7 +1286,7 @@ interrupt(Ureg *ur, void *a)
 				 * 2. It's not SCSI-II compliant. The new phase will be other
 				 *    than message_in. We should also indicate that the device
 				 *    is asynchronous, if it's the SDTR that got ignored
-				 * 
+				 *
 				 * For now, if the phase switch is not to message_in, and
 				 * and it happens after IDENTIFY and before SDTR, we
 				 * notify the negotiation state machine.
@@ -1500,13 +1501,18 @@ interrupt(Ureg *ur, void *a)
 			IPRINT("%.8lux %.8lux %.8lux\n",
 			    *(ulong *)(addr - 12), *(ulong *)(addr - 8), *(ulong *)(addr - 4));
 			USED(addr, dbc);
-			dsa->p9status = STATUS_FAIL;
+			if (dsa)
+				dsa->p9status = STATUS_FAIL;
 			wakeme = 1;
 		}
 		/*else*/ if (dstat & Bf) {
-			IPRINT(PRINTPREFIX "%d/%d: Bus Fault\n", dsa->target, dsa->lun);
+			if (dsa == nil)
+				print(PRINTPREFIX "Bus Fault with dsa==0\n");
+			else {
+				print(PRINTPREFIX "%d/%d: Bus Fault\n", dsa->target, dsa->lun);
+				dsa->p9status = STATUS_FAIL;
+			}
 			dumpncrregs(c, 1);
-			dsa->p9status = STATUS_FAIL;
 			wakeme = 1;
 		}
 	}
@@ -1514,7 +1520,7 @@ interrupt(Ureg *ur, void *a)
 		ncrcontinue(c);
 	else if (cont >= 0)
 		start(c, cont);
-	if (wakeme){
+	if (wakeme && dsa){
 		if(dsa->p9status == 0xffff)
 			dsa->p9status = STATUS_FAIL;
 		wakeup(dsa);
@@ -1672,7 +1678,7 @@ io(Controller *c, uchar target, uchar lun, int rw, uchar *cmd, int cmdlen, uchar
 	d->scsi_id_buf[2] = target;
 	d->scsi_id_buf[3] = c->scntl3[target];
 	synctodsa(d, c);
-	
+
 	bc = 0;
 
 	d->msg_out[bc] = 0x80 | lun;
@@ -1741,7 +1747,7 @@ io(Controller *c, uchar target, uchar lun, int rw, uchar *cmd, int cmdlen, uchar
 			dumpwritedata(data, datalen);
 	}
 
-	setmovedata(&d->status_buf, DMASEG(&d->status), 1);	
+	setmovedata(&d->status_buf, DMASEG(&d->status), 1);
 
 	d->p9status = 0xffff;
 	d->parityerror = 0;
@@ -1828,7 +1834,7 @@ exec(Scsi *p, int rw)
 	    p->cmd.base, p->cmd.lim - p->cmd.base,
 	    p->data.base, p->data.lim - p->data.base, &transferred);
 	p->data.ptr = p->data.base + transferred;
-	return p->status;	
+	return p->status;
 }
 #endif
 
@@ -1879,7 +1885,7 @@ cribbios(Controller *c)
 {
 	c->bios.scntl3 = c->n->scntl3;
 	c->bios.stest2 = c->n->stest2;
-	print(PRINTPREFIX "bios scntl3(%.2x) stest2(%.2x)\n", c->bios.scntl3, c->bios.stest2);
+	KPRINT(PRINTPREFIX "bios scntl3(%.2x) stest2(%.2x)\n", c->bios.scntl3, c->bios.stest2);
 }
 
 static int
@@ -2002,7 +2008,6 @@ scanpci(void)
 		ap = (Adapter*)mb->data;
 		ap->pcidev = p;
 		ap->port = p->mem[0].bar & ~0x01;
-
 		if(adapter == nil)
 			adapter = mb;
 		else
@@ -2097,7 +2102,7 @@ init(Controller* ctlr, ISAConf* isa, int differential)
 		 */
 		/*
 		 * should allocate memory and copy na_script into it for
-		 * multiple controllers here 
+		 * multiple controllers here
 		 * single controller version uses the microcode in place
 		 */
 		ctlr->script = na_script;
@@ -2120,10 +2125,10 @@ init(Controller* ctlr, ISAConf* isa, int differential)
 
 	isa->port = (ulong)KADDR(regpa);
 	isa->irq = pcidev->intl;
-	
+
 	synctabinit(ctlr);
 	cribbios(ctlr);
-	/*	
+	/*
 	intrenable(isa->irq, interrupt, ctlr, pcidev->tbdf);
 	 */
 	setvec(IRQBASE + isa->irq, interrupt, ctlr);

+ 2729 - 0
sys/src/fs/pc/sdata.c

@@ -0,0 +1,2729 @@
+/*
+ * (S)ATA(PI)/(E)IDE disk driver for file server, now with DMA!
+ * derived from /sys/src/boot/pc/sdata.c and /sys/src/9/pc/sdata.c
+ *
+ * we can't write message into a ctl file on the file server, so
+ * enable dma and rwm as advertised by the drive & controller.
+ * if that doesn't work, fix the hardware or turn it off in the source.
+ *
+ * entry points:
+../fs64/9fsfs64.c:38: 	{ "hd", ataread,   ataseek,   atawrite,   setatapart, },
+../fs64/9fsfs64.c:58: 	nhd = atainit();
+../port/sub.c:1065: 			return ideread(d, b, c);
+../port/sub.c:1129: 			return idewrite(d, b, c);
+../port/sub.c:1182: 			return idesize(d);
+../port/sub.c:1362: 			ideinit(d);
+ */
+#include "all.h"
+#include "io.h"
+#include "mem.h"
+
+#include "sd.h"
+#include "compat.h"
+#undef error
+
+enum {
+	DEBUGPR = 0,
+	IDEBUG = 0,
+
+	/* old stuff carried forward */
+	NCtlr=		8,
+	NDrive=		NCtlr*2,
+	Maxxfer=	16*1024,	/* maximum transfer size/cmd */
+
+	Read = 0,
+	Write,
+};
+
+#define DPRINT	if(DEBUGPR)print
+#define IDPRINT if(IDEBUG)print
+
+extern SDifc sdataifc;
+
+enum {
+	DbgCONFIG	= 0x0001,	/* detected drive config info */
+	DbgIDENTIFY	= 0x0002,	/* detected drive identify info */
+	DbgSTATE	= 0x0004,	/* dump state on panic */
+	DbgPROBE	= 0x0008,	/* trace device probing */
+	DbgDEBUG	= 0x0080,	/* the current problem... */
+	DbgINL		= 0x0100,	/* That Inil20+ message we hate */
+	Dbg48BIT	= 0x0200,	/* 48-bit LBA */
+	DbgBsy		= 0x0400,	/* interrupt but Bsy (shared IRQ) */
+};
+
+/* adjust to taste */
+#define DEBUG		(DbgDEBUG|DbgCONFIG)
+
+enum {					/* I/O ports */
+	Data		= 0,
+	Error		= 1,		/* (read) */
+	Features	= 1,		/* (write) */
+	Count		= 2,		/* sector count<7-0>, sector count<15-8> */
+	Ir		= 2,		/* interrupt reason (PACKET) */
+	Sector		= 3,		/* sector number */
+	Lbalo		= 3,		/* LBA<7-0>, LBA<31-24> */
+	Cyllo		= 4,		/* cylinder low */
+	Bytelo		= 4,		/* byte count low (PACKET) */
+	Lbamid		= 4,		/* LBA<15-8>, LBA<39-32> */
+	Cylhi		= 5,		/* cylinder high */
+	Bytehi		= 5,		/* byte count hi (PACKET) */
+	Lbahi		= 5,		/* LBA<23-16>, LBA<47-40> */
+	Dh		= 6,		/* Device/Head, LBA<32-14> */
+	Status		= 7,		/* (read) */
+	Cmd		= 7,		/* (write) */
+
+	As		= 2,		/* Alternate Status (read) */
+	Dc		= 2,		/* Device Control (write) */
+};
+
+enum {					/* Error */
+	Med		= 0x01,		/* Media error */
+	Ili		= 0x01,		/* command set specific (PACKET) */
+	Nm		= 0x02,		/* No Media */
+	Eom		= 0x02,		/* command set specific (PACKET) */
+	Abrt		= 0x04,		/* Aborted command */
+	Mcr		= 0x08,		/* Media Change Request */
+	Idnf		= 0x10,		/* no user-accessible address */
+	Mc		= 0x20,		/* Media Change */
+	Unc		= 0x40,		/* Uncorrectable data error */
+	Wp		= 0x40,		/* Write Protect */
+	Icrc		= 0x80,		/* Interface CRC error */
+};
+
+enum {					/* Features */
+	Dma		= 0x01,		/* data transfer via DMA (PACKET) */
+	Ovl		= 0x02,		/* command overlapped (PACKET) */
+};
+
+enum {					/* Interrupt Reason */
+	Cd		= 0x01,		/* Cmd/Data */
+	Io		= 0x02,		/* I/O direction */
+	Rel		= 0x04,		/* Bus Release */
+};
+
+enum {					/* Device/Head */
+	Dev0		= 0xA0,		/* Master */
+	Dev1		= 0xB0,		/* Slave */
+	Lba		= 0x40,		/* LBA mode */
+};
+
+enum {					/* internal flags */
+	Lba48		= 0x1,		/* LBA48 mode */
+	Lba48always	= 0x2,		/* ... */
+};
+
+enum {					/* Status, Alternate Status */
+	Err		= 0x01,		/* Error */
+	Chk		= 0x01,		/* Check error (PACKET) */
+	Drq		= 0x08,		/* Data Request */
+	Dsc		= 0x10,		/* Device Seek Complete */
+	Serv		= 0x10,		/* Service */
+	Df		= 0x20,		/* Device Fault */
+	Dmrd		= 0x20,		/* DMA ready (PACKET) */
+	Drdy		= 0x40,		/* Device Ready */
+	Bsy		= 0x80,		/* Busy */
+};
+
+enum {					/* Cmd */
+	Cnop		= 0x00,		/* NOP */
+	Cdr		= 0x08,		/* Device Reset */
+	Crs		= 0x20,		/* Read Sectors */
+	Crs48		= 0x24,		/* Read Sectors Ext */
+	Crd48		= 0x25,		/* Read w/ DMA Ext */
+	Crdq48		= 0x26,		/* Read w/ DMA Queued Ext */
+	Crsm48		= 0x29,		/* Read Multiple Ext */
+	Cws		= 0x30,		/* Write Sectors */
+	Cws48		= 0x34,		/* Write Sectors Ext */
+	Cwd48		= 0x35,		/* Write w/ DMA Ext */
+	Cwdq48		= 0x36,		/* Write w/ DMA Queued Ext */
+	Cwsm48		= 0x39,		/* Write Multiple Ext */
+	Cedd		= 0x90,		/* Execute Device Diagnostics */
+	Cpkt		= 0xA0,		/* Packet */
+	Cidpkt		= 0xA1,		/* Identify Packet Device */
+	Crsm		= 0xC4,		/* Read Multiple */
+	Cwsm		= 0xC5,		/* Write Multiple */
+	Csm		= 0xC6,		/* Set Multiple */
+	Crdq		= 0xC7,		/* Read DMA queued */
+	Crd		= 0xC8,		/* Read DMA */
+	Cwd		= 0xCA,		/* Write DMA */
+	Cwdq		= 0xCC,		/* Write DMA queued */
+	Cstandby	= 0xE2,		/* Standby */
+	Cid		= 0xEC,		/* Identify Device */
+	Csf		= 0xEF,		/* Set Features */
+};
+
+enum {					/* Device Control */
+	Nien		= 0x02,		/* (not) Interrupt Enable */
+	Srst		= 0x04,		/* Software Reset */
+	Hob		= 0x80,		/* High Order Bit [sic] */
+};
+
+enum {					/* PCI Configuration Registers */
+	Bmiba		= 0x20,		/* Bus Master Interface Base Address */
+	Idetim		= 0x40,		/* IE Timing */
+	Sidetim		= 0x44,		/* Slave IE Timing */
+	Udmactl		= 0x48,		/* Ultra DMA/33 Control */
+	Udmatim		= 0x4A,		/* Ultra DMA/33 Timing */
+};
+
+enum {					/* Bus Master IDE I/O Ports */
+	Bmicx		= 0,		/* Cmd */
+	Bmisx		= 2,		/* Status */
+	Bmidtpx		= 4,		/* Descriptor Table Pointer */
+};
+
+enum {					/* Bmicx */
+	Ssbm		= 0x01,		/* Start/Stop Bus Master */
+	Rwcon		= 0x08,		/* Read/Write Control */
+};
+
+enum {					/* Bmisx */
+	Bmidea		= 0x01,		/* Bus Master IDE Active */
+	Idedmae		= 0x02,		/* IDE DMA Error  (R/WC) */
+	Ideints		= 0x04,		/* IDE Interrupt Status (R/WC) */
+	Dma0cap		= 0x20,		/* Drive 0 DMA Capable */
+	Dma1cap		= 0x40,		/* Drive 0 DMA Capable */
+};
+enum {					/* Physical Region Descriptor */
+	PrdEOT		= 0x80000000,	/* Bus Master IDE Active */
+};
+
+enum {					/* offsets into the identify info. */
+	Iconfig		= 0,		/* general configuration */
+	Ilcyl		= 1,		/* logical cylinders */
+	Ilhead		= 3,		/* logical heads */
+	Ilsec		= 6,		/* logical sectors per logical track */
+	Iserial		= 10,		/* serial number */
+	Ifirmware	= 23,		/* firmware revision */
+	Imodel		= 27,		/* model number */
+	Imaxrwm		= 47,		/* max. read/write multiple sectors */
+	Icapabilities	= 49,		/* capabilities */
+	Istandby	= 50,		/* device specific standby timer */
+	Ipiomode	= 51,		/* PIO data transfer mode number */
+	Ivalid		= 53,
+	Iccyl		= 54,		/* cylinders if (valid&0x01) */
+	Ichead		= 55,		/* heads if (valid&0x01) */
+	Icsec		= 56,		/* sectors if (valid&0x01) */
+	Iccap		= 57,		/* capacity if (valid&0x01) */
+	Irwm		= 59,		/* read/write multiple */
+	Ilba		= 60,		/* LBA size */
+	Imwdma		= 63,		/* multiword DMA mode */
+	Iapiomode	= 64,		/* advanced PIO modes supported */
+	Iminmwdma	= 65,		/* min. multiword DMA cycle time */
+	Irecmwdma	= 66,		/* rec. multiword DMA cycle time */
+	Iminpio		= 67,		/* min. PIO cycle w/o flow control */
+	Iminiordy	= 68,		/* min. PIO cycle with IORDY */
+	Ipcktbr		= 71,		/* time from PACKET to bus release */
+	Iserbsy		= 72,		/* time from SERVICE to !Bsy */
+	Iqdepth		= 75,		/* max. queue depth */
+	Imajor		= 80,		/* major version number */
+	Iminor		= 81,		/* minor version number */
+	Icsfs		= 82,		/* command set/feature supported */
+	Icsfe		= 85,		/* command set/feature enabled */
+	Iudma		= 88,		/* ultra DMA mode */
+	Ierase		= 89,		/* time for security erase */
+	Ieerase		= 90,		/* time for enhanced security erase */
+	Ipower		= 91,		/* current advanced power management */
+	Ilba48		= 100,		/* 48-bit LBA size (64 bits in 100-103) */
+	Irmsn		= 127,		/* removable status notification */
+	Isecstat	= 128,		/* security status */
+	Icfapwr		= 160,		/* CFA power mode */
+	Imediaserial	= 176,		/* current media serial number */
+	Icksum		= 255,		/* checksum */
+};
+
+enum {					/* bit masks for config identify info */
+	Mpktsz		= 0x0003,	/* packet command size */
+	Mincomplete	= 0x0004,	/* incomplete information */
+	Mdrq		= 0x0060,	/* DRQ type */
+	Mrmdev		= 0x0080,	/* device is removable */
+	Mtype		= 0x1F00,	/* device type */
+	Mproto		= 0x8000,	/* command protocol */
+};
+
+enum {					/* bit masks for capabilities identify info */
+	Mdma		= 0x0100,	/* DMA supported */
+	Mlba		= 0x0200,	/* LBA supported */
+	Mnoiordy	= 0x0400,	/* IORDY may be disabled */
+	Miordy		= 0x0800,	/* IORDY supported */
+	Msoftrst	= 0x1000,	/* needs soft reset when Bsy */
+	Mstdby		= 0x2000,	/* standby supported */
+	Mqueueing	= 0x4000,	/* queueing overlap supported */
+	Midma		= 0x8000,	/* interleaved DMA supported */
+};
+
+enum {					/* bit masks for supported/enabled features */
+	Msmart		= 0x0001,
+	Msecurity	= 0x0002,
+	Mrmmedia	= 0x0004,
+	Mpwrmgmt	= 0x0008,
+	Mpkt		= 0x0010,
+	Mwcache		= 0x0020,
+	Mlookahead	= 0x0040,
+	Mrelirq		= 0x0080,
+	Msvcirq		= 0x0100,
+	Mreset		= 0x0200,
+	Mprotected	= 0x0400,
+	Mwbuf		= 0x1000,
+	Mrbuf		= 0x2000,
+	Mnop		= 0x4000,
+	Mmicrocode	= 0x0001,
+	Mqueued		= 0x0002,
+	Mcfa		= 0x0004,
+	Mapm		= 0x0008,
+	Mnotify		= 0x0010,
+	Mstandby	= 0x0020,
+	Mspinup		= 0x0040,
+	Mmaxsec		= 0x0100,
+	Mautoacoustic	= 0x0200,
+	Maddr48		= 0x0400,
+	Mdevconfov	= 0x0800,
+	Mflush		= 0x1000,
+	Mflush48	= 0x2000,
+	Msmarterror	= 0x0001,
+	Msmartselftest	= 0x0002,
+	Mmserial	= 0x0004,
+	Mmpassthru	= 0x0008,
+	Mlogging	= 0x0020,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Drive Drive;
+
+typedef struct Prd {
+	ulong	pa;			/* Physical Base Address */
+	int	count;
+} Prd;
+
+enum {
+	Nprd		= SDmaxio/(64*1024)+2,
+};
+
+typedef struct Ctlr {
+	int	cmdport;
+	int	ctlport;
+	int	irq;
+	int	tbdf;
+	int	bmiba;			/* bus master interface base address */
+
+	Pcidev*	pcidev;
+	void	(*ienable)(Ctlr*);
+	void	(*idisable)(Ctlr*);
+	SDev*	sdev;
+
+	Drive*	drive[2];
+
+	Prd*	prdt;			/* physical region descriptor table */
+	void*	prdtbase;
+
+	QLock;				/* current command */
+	Drive*	curdrive;
+	int	command;		/* last command issued (debugging) */
+	Rendez;
+	int	done;
+
+	Lock;				/* register access */
+
+	/* old stuff carried forward */
+	QLock	idelock;		/* make seek & i/o atomic in ide* routines */
+	uchar	buf[RBUFSIZE];
+} Ctlr;
+
+typedef struct Drive {
+	Ctlr*	ctlr;
+
+	int	dev;
+	ushort	info[256];
+	int	c;			/* cylinder */
+	int	h;			/* head */
+	int	s;			/* sector */
+	Devsize	sectors;		/* total sectors */
+	int	secsize;		/* sector size */
+
+	int	dma;			/* DMA R/W possible */
+	int	dmactl;
+	int	rwm;			/* read/write multiple possible */
+	int	rwmctl;
+
+	int	pkt;			/* PACKET device, length of pktcmd */
+	uchar	pktcmd[16];
+	int	pktdma;			/* this PACKET command using dma */
+
+	uchar	sense[18];
+	uchar	inquiry[48];
+
+	QLock;				/* drive access */
+	int	command;		/* current command */
+	int	write;
+	uchar*	data;
+	int	dlen;
+	uchar*	limit;
+	int	count;			/* sectors */
+	int	block;			/* R/W bytes per block */
+	int	status;
+	int	error;
+	int	flags;			/* internal flags */
+
+	/* for ata* routines */
+	int	online;
+	Devsize	offset;
+	int	driveno;		/* ctlr*2 + unit */
+
+	char	lba;		/* true if drive has logical block addressing */
+	char	multi;		/* non-0 if drive does multiple block xfers */
+} Drive;
+
+/* file-server-specific data */
+
+static Ctlr *atactlr[NCtlr];
+static SDev sdevs[NCtlr];
+
+static Drive *atadrive[NDrive];
+static SDunit sdunits[NDrive];
+
+static Drive	*atadriveprobe(int driveno);
+
+
+void
+presleep(Rendez *r, int (*fn)(void*), void *v)
+{
+	int x;
+
+	if (u != nil) {
+		sleep(r, fn, v);
+		return;
+	}
+	/* else we're in predawn with no u */
+	x = spllo();
+	while (!fn(v))
+		continue;
+	splx(x);
+}
+
+void
+pretsleep(Rendez *r, int (*fn)(void*), void *v, int msec)
+{
+	int x;
+	ulong start;
+
+	if (u != nil) {
+		tsleep(r, fn, v, msec);
+		return;
+	}
+	/* else we're in predawn with no u */
+	x = spllo();
+	for (start = m->ticks; TK2MS(m->ticks - start) < msec &&
+	    !fn(v); )
+		continue;
+	splx(x);
+}
+
+#define sleep	presleep
+#define tsleep	pretsleep
+
+static void
+pc87415ienable(Ctlr* ctlr)
+{
+	Pcidev *p;
+	int x;
+
+	p = ctlr->pcidev;
+	if(p == nil)
+		return;
+
+	x = pcicfgr32(p, 0x40);
+	if(ctlr->cmdport == p->mem[0].bar)
+		x &= ~0x00000100;
+	else
+		x &= ~0x00000200;
+	pcicfgw32(p, 0x40, x);
+}
+
+static void
+atadumpstate(Drive* drive, uchar* cmd, Devsize lba, int count)
+{
+	Prd *prd;
+	Pcidev *p;
+	Ctlr *ctlr;
+	int i, bmiba;
+
+	if(!(DEBUG & DbgSTATE)){
+		USED(drive, cmd, lba, count);
+		return;
+	}
+
+	ctlr = drive->ctlr;
+	print("command %2.2uX\n", ctlr->command);
+	print("data %8.8p limit %8.8p dlen %d status %uX error %uX\n",
+		drive->data, drive->limit, drive->dlen,
+		drive->status, drive->error);
+	if(cmd != nil){
+		print("lba %d -> %lld, count %d -> %d (%d)\n",
+			(cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5],
+			(Wideoff)lba,
+			(cmd[7]<<8)|cmd[8], count, drive->count);
+	}
+	if(!(inb(ctlr->ctlport+As) & Bsy)){
+		for(i = 1; i < 7; i++)
+			print(" 0x%2.2uX", inb(ctlr->cmdport+i));
+		print(" 0x%2.2uX\n", inb(ctlr->ctlport+As));
+	}
+	if(drive->command == Cwd || drive->command == Crd){
+		bmiba = ctlr->bmiba;
+		prd = ctlr->prdt;
+		print("bmicx %2.2uX bmisx %2.2uX prdt %8.8p\n",
+			inb(bmiba+Bmicx), inb(bmiba+Bmisx), prd);
+		for(;;){
+			print("pa 0x%8.8luX count %8.8uX\n",
+				prd->pa, prd->count);
+			if(prd->count & PrdEOT)
+				break;
+			prd++;
+		}
+	}
+	if(ctlr->pcidev && ctlr->pcidev->vid == 0x8086){
+		p = ctlr->pcidev;
+		print("0x40: %4.4uX 0x42: %4.4uX",
+			pcicfgr16(p, 0x40), pcicfgr16(p, 0x42));
+		print("0x48: %2.2uX\n", pcicfgr8(p, 0x48));
+		print("0x4A: %4.4uX\n", pcicfgr16(p, 0x4A));
+	}
+}
+
+static int
+atadebug(int cmdport, int ctlport, char* fmt, ...)
+{
+	int i, n;
+	va_list arg;
+	char buf[PRINTSIZE];
+
+	if(!(DEBUG & DbgPROBE)){
+		USED(cmdport, ctlport, fmt);
+		return 0;
+	}
+
+	va_start(arg, fmt);
+	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+	va_end(arg);
+
+	if(cmdport){
+		if(buf[n-1] == '\n')
+			n--;
+		n += snprint(buf+n, PRINTSIZE-n, " ataregs 0x%uX:",
+			cmdport);
+		for(i = Features; i < Cmd; i++)
+			n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX",
+				inb(cmdport+i));
+		if(ctlport)
+			n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX",
+				inb(ctlport+As));
+		n += snprint(buf+n, PRINTSIZE-n, "\n");
+	}
+	putstrn(buf, n);
+
+	return n;
+}
+
+static int
+ataready(int cmdport, int ctlport, int dev, int reset, int ready, int micro)
+{
+	int as;
+
+	atadebug(cmdport, ctlport, "ataready: dev %uX reset %uX ready %uX",
+		dev, reset, ready);
+
+	for(;;){
+		/*
+		 * Wait for the controller to become not busy and
+		 * possibly for a status bit to become true (usually
+		 * Drdy). Must change to the appropriate device
+		 * register set if necessary before testing for ready.
+		 * Always run through the loop at least once so it
+		 * can be used as a test for !Bsy.
+		 */
+		as = inb(ctlport+As);
+		if(as & reset){
+			/* nothing to do */
+		}
+		else if(dev){
+			outb(cmdport+Dh, dev);
+			dev = 0;
+		}
+		else if(ready == 0 || (as & ready)){
+			atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as);
+			return as;
+		}
+
+		if(micro-- <= 0){
+			atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as);
+			break;
+		}
+		microdelay(1);
+	}
+	atadebug(cmdport, ctlport, "ataready: timeout");
+
+	return -1;
+}
+
+/*
+static int
+atacsf(Drive* drive, vlong csf, int supported)
+{
+	ushort *info;
+	int cmdset, i, x;
+
+	if(supported)
+		info = &drive->info[Icsfs];
+	else
+		info = &drive->info[Icsfe];
+
+	for(i = 0; i < 3; i++){
+		x = (csf>>(16*i)) & 0xFFFF;
+		if(x == 0)
+			continue;
+		cmdset = info[i];
+		if(cmdset == 0 || cmdset == 0xFFFF)
+			return 0;
+		return cmdset & x;
+	}
+
+	return 0;
+}
+*/
+
+static int
+atadone(void* arg)
+{
+	return ((Ctlr*)arg)->done;
+}
+
+static int
+atarwmmode(Drive* drive, int cmdport, int ctlport, int dev)
+{
+	int as, maxrwm, rwm;
+
+	maxrwm = (drive->info[Imaxrwm] & 0xFF);
+	if(maxrwm == 0)
+		return 0;
+
+	/*
+	 * Sometimes drives come up with the current count set
+	 * to 0; if so, set a suitable value, otherwise believe
+	 * the value in Irwm if the 0x100 bit is set.
+	 */
+	if(drive->info[Irwm] & 0x100)
+		rwm = (drive->info[Irwm] & 0xFF);
+	else
+		rwm = 0;
+	if(rwm == 0)
+		rwm = maxrwm;
+	if(rwm > 16)
+		rwm = 16;
+	if(ataready(cmdport, ctlport, dev, Bsy|Drq, Drdy, 102*1000) < 0)
+		return 0;
+	outb(cmdport+Count, rwm);
+	outb(cmdport+Cmd, Csm);
+	microdelay(1);
+	as = ataready(cmdport, ctlport, 0, Bsy, Drdy|Df|Err, 1000);
+	inb(cmdport+Status);
+	if(as < 0 || (as & (Df|Err)))
+		return 0;
+
+	drive->rwm = rwm;
+	if (conf.idedma)
+		drive->rwmctl = drive->rwm;	/* FS special */
+	return rwm;
+}
+
+static int
+atadmamode(Drive* drive)
+{
+	int dma;
+
+	/*
+	 * Check if any DMA mode enabled.
+	 * Assumes the BIOS has picked and enabled the best.
+	 * This is completely passive at the moment, no attempt is
+	 * made to ensure the hardware is correctly set up.
+	 */
+	dma = drive->info[Imwdma] & 0x0707;
+	drive->dma = (dma>>8) & dma;
+	if(drive->dma == 0 && (drive->info[Ivalid] & 0x04)){
+		dma = drive->info[Iudma] & 0x7F7F;
+		drive->dma = (dma>>8) & dma;
+		if(drive->dma)
+			drive->dma |= 'U'<<16;
+	}
+	if (conf.idedma)
+		drive->dmactl = drive->dma;	/* FS special */
+	return dma;
+}
+
+static int
+ataidentify(int cmdport, int ctlport, int dev, int pkt, void* info)
+{
+	int as, command, drdy;
+
+	if(pkt){
+		command = Cidpkt;
+		drdy = 0;
+	}
+	else{
+		command = Cid;
+		drdy = Drdy;
+	}
+	as = ataready(cmdport, ctlport, dev, Bsy|Drq, drdy, 103*1000);
+	if(as < 0)
+		return as;
+	outb(cmdport+Cmd, command);
+	microdelay(1);
+
+	as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 400*1000);
+	if(as < 0)
+		return -1;
+	if(as & Err)
+		return as;
+
+	memset(info, 0, 512);
+	inss(cmdport+Data, info, 256);
+	inb(cmdport+Status);
+
+	if(DEBUG & DbgIDENTIFY){
+		int i;
+		ushort *sp;
+
+		sp = (ushort*)info;
+		for(i = 0; i < 256; i++){
+			if(i && (i%16) == 0)
+				print("\n");
+			print(" %4.4uX", *sp);
+			sp++;
+		}
+		print("\n");
+	}
+
+	return 0;
+}
+
+/*
+ * DEBUGGING only.
+ * write, read and verify block 1 (never used in an fs otherwise)
+ * to see if dma and rwm actually work.
+ * if not, turn them off, though the kernel could be corrupt by then.
+ */
+static void
+ataverify(Drive *dp)
+{
+	int n, nb, dev = dp->driveno;
+	uchar *buf = dp->ctlr->buf;
+
+	if (dp->ctlr == nil)
+		panic("ataverify: nil ctlr for drive");
+	atadriveprobe(dev);
+	print("ataverify h%d...", dev);
+	for (n = 0; n < RBUFSIZE; n++)
+		buf[n] = n;
+	if (ataseek(dev, RBUFSIZE) < 0)
+		panic("ataverify: seek 1");
+	nb = atawrite(dev, buf, RBUFSIZE);
+	if (nb != RBUFSIZE)
+		print("short write of %d bytes to block 1\n", nb);
+	else {
+		for (n = 0; n < RBUFSIZE; n++)
+			buf[n] = ~n;
+		if (ataseek(dev, RBUFSIZE) < 0)
+			panic("ataverify: seek 1");
+		nb = ataread(dev, buf, RBUFSIZE);
+		if (nb != RBUFSIZE)
+			print("short read of %d bytes to block 1\n", nb);
+		else {
+			for (n = 0; n < RBUFSIZE; n++)
+				if ((uchar)buf[n] != (uchar)n)
+					break;
+			if (n == RBUFSIZE) {
+				print("OK\n");
+				return;			/* verified OK */
+			}
+			print("byte comparison failed\n");
+		}
+	}
+	print("ataverify: disabling dma and rwm\n");
+	dp->dmactl = dp->rwmctl = 0;
+}
+
+static Drive*
+atagetdrive(int cmdport, int ctlport, int dev)
+{
+	Drive *drive;
+	int as, i, pkt;
+	uchar buf[512], *p;
+	ushort iconfig, *sp;
+
+	atadebug(0, 0, "identify: port 0x%uX dev 0x%2.2uX\n", cmdport, dev);
+	pkt = 1;
+retry:
+	as = ataidentify(cmdport, ctlport, dev, pkt, buf);
+	if(as < 0)
+		return nil;
+	if(as & Err){
+		if(pkt == 0)
+			return nil;
+		pkt = 0;
+		goto retry;
+	}
+
+	if((drive = malloc(sizeof(Drive))) == nil)
+		return nil;
+	drive->dev = dev;
+	drive->driveno = -1;				/* unset */
+	memmove(drive->info, buf, sizeof(drive->info));
+	drive->sense[0] = 0x70;
+	drive->sense[7] = sizeof(drive->sense)-7;
+
+	drive->inquiry[2] = 2;
+	drive->inquiry[3] = 2;
+	drive->inquiry[4] = sizeof(drive->inquiry)-4;
+	p = &drive->inquiry[8];
+	sp = &drive->info[Imodel];
+	for(i = 0; i < 20; i++){
+		*p++ = *sp>>8;
+		*p++ = *sp++;
+	}
+
+	drive->secsize = 512;
+
+	/*
+	 * Beware the CompactFlash Association feature set.
+	 * Now, why this value in Iconfig just walks all over the bit
+	 * definitions used in the other parts of the ATA/ATAPI standards
+	 * is a mystery and a sign of true stupidity on someone's part.
+	 * Anyway, the standard says if this value is 0x848A then it's
+	 * CompactFlash and it's NOT a packet device.
+	 */
+	iconfig = drive->info[Iconfig];
+	if(iconfig != 0x848A && (iconfig & 0xC000) == 0x8000){
+		if(iconfig & 0x01)
+			drive->pkt = 16;
+		else
+			drive->pkt = 12;
+	}
+	else{
+		if(drive->info[Ivalid] & 0x0001){
+			drive->c = drive->info[Iccyl];
+			drive->h = drive->info[Ichead];
+			drive->s = drive->info[Icsec];
+		}else{
+			drive->c = drive->info[Ilcyl];
+			drive->h = drive->info[Ilhead];
+			drive->s = drive->info[Ilsec];
+		}
+		if(drive->info[Icapabilities] & Mlba){
+			if(drive->info[Icsfs+1] & Maddr48){
+				drive->sectors = drive->info[Ilba48]
+					| (drive->info[Ilba48+1]<<16)
+					| ((Devsize)drive->info[Ilba48+2]<<32);
+				drive->flags |= Lba48;
+			}else
+				drive->sectors = (drive->info[Ilba+1]<<16)
+					 |drive->info[Ilba];
+			drive->dev |= Lba;
+			drive->lba = 1;
+		}else
+			drive->sectors = drive->c * drive->h * drive->s;
+		atarwmmode(drive, cmdport, ctlport, dev);
+	}
+	atadmamode(drive);
+
+	if(DEBUG & DbgCONFIG){
+		print("ata: dev %2.2uX port %uX config %4.4uX capabilities %4.4uX",
+			dev, cmdport, iconfig, drive->info[Icapabilities]);
+		print(" mwdma %4.4uX", drive->info[Imwdma]);
+		if(drive->info[Ivalid] & 0x04)
+			print(" udma %4.4uX", drive->info[Iudma]);
+		print(" dma %8.8uX rwm %ud\n", drive->dma, drive->rwm);
+		if(drive->flags&Lba48)
+			print("\tLLBA sectors %lld\n", (Wideoff)drive->sectors);
+	}
+
+	return drive;
+}
+
+static void
+atasrst(int ctlport)
+{
+	/*
+	 * Srst is a big stick and may cause problems if further
+	 * commands are tried before the drives become ready again.
+	 * Also, there will be problems here if overlapped commands
+	 * are ever supported.
+	 */
+	microdelay(5);
+	outb(ctlport+Dc, Srst);
+	microdelay(5);
+	outb(ctlport+Dc, 0);
+	microdelay(2*1000);
+}
+
+static SDev*
+ataprobe(int cmdport, int ctlport, int irq)
+{
+	Ctlr* ctlr;
+	SDev *sdev;
+	Drive *drive;
+	int i, dev, error, rhi, rlo;
+	static int drivenum = 0;	/* hope that we probe in order */
+
+	if(ioalloc(cmdport, 8, 0, "atacmd") < 0) {
+		print("ataprobe: Cannot allocate %X\n", cmdport);
+		return nil;
+	}
+	if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){
+		print("ataprobe: Cannot allocate %X\n", ctlport + As);
+		iofree(cmdport);
+		return nil;
+	}
+
+	/*
+	 * Try to detect a floating bus.
+	 * Bsy should be cleared. If not, see if the cylinder registers
+	 * are read/write capable.
+	 * If the master fails, try the slave to catch slave-only
+	 * configurations.
+	 * There's no need to restore the tested registers as they will
+	 * be reset on any detected drives by the Cedd command.
+	 * All this indicates is that there is at least one drive on the
+	 * controller; when the non-existent drive is selected in a
+	 * single-drive configuration the registers of the existing drive
+	 * are often seen, only command execution fails.
+	 */
+	dev = Dev0;
+	if(inb(ctlport+As) & Bsy){
+		outb(cmdport+Dh, dev);
+		microdelay(1);
+trydev1:
+		atadebug(cmdport, ctlport, "ataprobe bsy");
+		outb(cmdport+Cyllo, 0xAA);
+		outb(cmdport+Cylhi, 0x55);
+		outb(cmdport+Sector, 0xFF);
+		rlo = inb(cmdport+Cyllo);
+		rhi = inb(cmdport+Cylhi);
+		if(rlo != 0xAA && (rlo == 0xFF || rhi != 0x55)){
+			if(dev == Dev1){
+release:
+				iofree(cmdport);
+				iofree(ctlport+As);
+				return nil;
+			}
+			dev = Dev1;
+			if(ataready(cmdport, ctlport, dev, Bsy, 0, 20*1000) < 0)
+				goto trydev1;
+		}
+	}
+
+	/*
+	 * Disable interrupts on any detected controllers.
+	 */
+	outb(ctlport+Dc, Nien);
+tryedd1:
+	if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 105*1000) < 0){
+		/*
+		 * There's something there, but it didn't come up clean,
+		 * so try hitting it with a big stick. The timing here is
+		 * wrong but this is a last-ditch effort and it sometimes
+		 * gets some marginal hardware back online.
+		 */
+		atasrst(ctlport);
+		if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 106*1000) < 0)
+			goto release;
+	}
+
+	/*
+	 * Can only get here if controller is not busy.
+	 * If there are drives Bsy will be set within 400nS,
+	 * must wait 2mS before testing Status.
+	 * Wait for the command to complete (6 seconds max).
+	 */
+	outb(cmdport+Cmd, Cedd);
+	delay(2);
+	if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 6*1000*1000) < 0)
+		goto release;
+
+	/*
+	 * If bit 0 of the error register is set then the selected drive
+	 * exists. This is enough to detect single-drive configurations.
+	 * However, if the master exists there is no way short of executing
+	 * a command to determine if a slave is present.
+	 * It appears possible to get here testing Dev0 although it doesn't
+	 * exist and the EDD won't take, so try again with Dev1.
+	 */
+	error = inb(cmdport+Error);
+	atadebug(cmdport, ctlport, "ataprobe: dev %uX", dev);
+	if((error & ~0x80) != 0x01){
+		if(dev == Dev1)
+			goto release;
+		dev = Dev1;
+		goto tryedd1;
+	}
+
+	/*
+	 * At least one drive is known to exist, try to
+	 * identify it. If that fails, don't bother checking
+	 * any further.
+	 * If the one drive found is Dev0 and the EDD command
+	 * didn't indicate Dev1 doesn't exist, check for it.
+	 */
+	if((drive = atagetdrive(cmdport, ctlport, dev)) == nil)
+		goto release;
+	if((ctlr = malloc(sizeof(Ctlr))) == nil){
+		free(drive);
+		goto release;
+	}
+	memset(ctlr, 0, sizeof(Ctlr));
+	if((sdev = malloc(sizeof(SDev))) == nil){
+		free(ctlr);
+		free(drive);
+		goto release;
+	}
+	memset(sdev, 0, sizeof(SDev));
+	drive->ctlr = ctlr;
+	atactlr[drivenum/2] = ctlr;
+	atadrive[drivenum++] = drive;
+	if(dev == Dev0){
+		ctlr->drive[0] = drive;
+		if(!(error & 0x80)){
+			/*
+			 * Always leave Dh pointing to a valid drive,
+			 * otherwise a subsequent call to ataready on
+			 * this controller may try to test a bogus Status.
+			 * Ataprobe is the only place possibly invalid
+			 * drives should be selected.
+			 */
+			drive = atagetdrive(cmdport, ctlport, Dev1);
+			if(drive != nil){
+				drive->ctlr = ctlr;
+				ctlr->drive[1] = drive;
+			}
+			else{
+				outb(cmdport+Dh, Dev0);
+				microdelay(1);
+			}
+			atadrive[drivenum] = drive;
+		}
+	}
+	else
+		ctlr->drive[1] = drive;
+	drivenum++;
+
+	print("ata%d: cmd 0x%ux ctl 0x%ux irq %d\n",
+		(drivenum-1)/2, cmdport, ctlport, irq);
+	ctlr->cmdport = cmdport;
+	ctlr->ctlport = ctlport;
+	ctlr->irq = irq;
+	ctlr->tbdf = BUSUNKNOWN;
+	ctlr->command = Cedd;		/* debugging */
+
+	sdev->ifc = &sdataifc;
+	sdev->ctlr = ctlr;
+	sdev->nunit = 2;
+	ctlr->sdev = sdev;
+
+	if (0)
+		for (i = drivenum - 2; i < drivenum; i++)
+			if (atadrive[i])
+				ataverify(atadrive[i]);
+	return sdev;
+}
+
+static void
+ataclear(SDev *sdev)
+{
+	Ctlr* ctlr;
+
+	ctlr = sdev->ctlr;
+	iofree(ctlr->cmdport);
+	iofree(ctlr->ctlport + As);
+
+	if (ctlr->drive[0])
+		free(ctlr->drive[0]);
+	if (ctlr->drive[1])
+		free(ctlr->drive[1]);
+	if (sdev->name)
+		free(sdev->name);
+#ifdef notdef
+	/* TODO: WTF is this? */
+	if (sdev->unitflg)
+		free(sdev->unitflg);
+	if (sdev->unit)
+		free(sdev->unit);
+#endif
+	free(ctlr);
+	free(sdev);
+}
+
+static char *
+atastat(SDev *sdev, char *p, char *e)
+{
+	Ctlr *ctlr = sdev->ctlr;
+
+	return seprint(p, e, "%s ata port %X ctl %X irq %d\n",
+		    	       sdev->name, ctlr->cmdport, ctlr->ctlport, ctlr->irq);
+}
+
+#ifndef FS
+static SDev*
+ataprobew(DevConf *cf)
+{
+	if (cf->nports != 2)
+		error(Ebadarg);
+
+	return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->intnum);
+}
+#endif
+
+static int
+atasetsense(Drive* drive, int status, int key, int asc, int ascq)
+{
+	drive->sense[2] = key;
+	drive->sense[12] = asc;
+	drive->sense[13] = ascq;
+
+	return status;
+}
+
+static int
+atastandby(Drive* drive, int period)
+{
+	Ctlr* ctlr;
+	int cmdport, done;
+
+	ctlr = drive->ctlr;
+	drive->command = Cstandby;
+	qlock(ctlr);
+
+	cmdport = ctlr->cmdport;
+	ilock(ctlr);
+	outb(cmdport+Count, period);
+	outb(cmdport+Dh, drive->dev);
+	ctlr->done = 0;
+	ctlr->curdrive = drive;
+	ctlr->command = Cstandby;	/* debugging */
+	outb(cmdport+Cmd, Cstandby);
+	iunlock(ctlr);
+
+	while(waserror())
+		;
+	tsleep(ctlr, atadone, ctlr, 30*1000);
+	poperror();
+
+	done = ctlr->done;
+	qunlock(ctlr);
+
+	if(!done || (drive->status & Err))
+		return atasetsense(drive, SDcheck, 4, 8, drive->error);
+	return SDok;
+}
+
+static int
+atamodesense(Drive* drive, uchar* cmd)
+{
+	int len;
+
+	/*
+	 * Fake a vendor-specific request with page code 0,
+	 * return the drive info.
+	 */
+	if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F)
+		return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
+	len = (cmd[7]<<8)|cmd[8];
+	if(len == 0)
+		return SDok;
+	if(len < 8+sizeof(drive->info))
+		return atasetsense(drive, SDcheck, 0x05, 0x1A, 0);
+	if(drive->data == nil || drive->dlen < len)
+		return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
+	memset(drive->data, 0, 8);
+	drive->data[0] = sizeof(drive->info)>>8;
+	drive->data[1] = sizeof(drive->info);
+	memmove(drive->data+8, drive->info, sizeof(drive->info));
+	drive->data += 8+sizeof(drive->info);
+
+	return SDok;
+}
+
+static void
+atanop(Drive* drive, int subcommand)
+{
+	Ctlr* ctlr;
+	int as, cmdport, ctlport, timeo;
+
+	/*
+	 * Attempt to abort a command by using NOP.
+	 * In response, the drive is supposed to set Abrt
+	 * in the Error register, set (Drdy|Err) in Status
+	 * and clear Bsy when done. However, some drives
+	 * (e.g. ATAPI Zip) just go Bsy then clear Status
+	 * when done, hence the timeout loop only on Bsy
+	 * and the forced setting of drive->error.
+	 */
+	ctlr = drive->ctlr;
+	cmdport = ctlr->cmdport;
+	outb(cmdport+Features, subcommand);
+	outb(cmdport+Dh, drive->dev);
+	ctlr->command = Cnop;		/* debugging */
+	outb(cmdport+Cmd, Cnop);
+
+	microdelay(1);
+	ctlport = ctlr->ctlport;
+	for(timeo = 0; timeo < 1000; timeo++){
+		as = inb(ctlport+As);
+		if(!(as & Bsy))
+			break;
+		microdelay(1);
+	}
+	drive->error |= Abrt;
+}
+
+static void
+ataabort(Drive* drive, int dolock)
+{
+	/*
+	 * If NOP is available (packet commands) use it otherwise
+	 * must try a software reset.
+	 */
+	if(dolock)
+		ilock(drive->ctlr);
+	if(drive->info[Icsfs] & Mnop)
+		atanop(drive, 0);
+	else{
+		atasrst(drive->ctlr->ctlport);
+		drive->error |= Abrt;
+	}
+	if(dolock)
+		iunlock(drive->ctlr);
+}
+
+static int
+atadmasetup(Drive* drive, int len)
+{
+	Prd *prd;
+	ulong pa;
+	Ctlr *ctlr;
+	int bmiba, bmisx, count;
+
+	pa = PCIWADDR(drive->data);
+	if(pa & 0x03)
+		return -1;
+	ctlr = drive->ctlr;
+	prd = ctlr->prdt;
+
+	/*
+	 * Sometimes drives identify themselves as being DMA capable
+	 * although they are not on a busmastering controller.
+	 */
+	if(prd == nil){
+		drive->dmactl = 0;
+		print("h%d: disabling dma: not on a busmastering controller\n",
+			drive->driveno);
+		return -1;
+	}
+
+	for(;;){
+		prd->pa = pa;
+		count = 64*1024 - (pa & 0xFFFF);
+		if(count >= len){
+			prd->count = PrdEOT|(len & 0xFFFF);
+			break;
+		}
+		prd->count = count;
+		len -= count;
+		pa += count;
+		prd++;
+	}
+
+	bmiba = ctlr->bmiba;
+	outl(bmiba+Bmidtpx, PCIWADDR(ctlr->prdt));
+	if(drive->write)
+		outb(ctlr->bmiba+Bmicx, 0);
+	else
+		outb(ctlr->bmiba+Bmicx, Rwcon);
+	bmisx = inb(bmiba+Bmisx);
+	outb(bmiba+Bmisx, bmisx|Ideints|Idedmae);
+
+	return 0;
+}
+
+static void
+atadmastart(Ctlr* ctlr, int write)
+{
+	if(write)
+		outb(ctlr->bmiba+Bmicx, Ssbm);
+	else
+		outb(ctlr->bmiba+Bmicx, Rwcon|Ssbm);
+}
+
+static int
+atadmastop(Ctlr* ctlr)
+{
+	int bmiba;
+
+	bmiba = ctlr->bmiba;
+	outb(bmiba+Bmicx, inb(bmiba+Bmicx) & ~Ssbm);
+
+	return inb(bmiba+Bmisx);
+}
+
+static void
+atadmainterrupt(Drive* drive, int count)
+{
+	Ctlr* ctlr;
+	int bmiba, bmisx;
+
+	ctlr = drive->ctlr;
+	bmiba = ctlr->bmiba;
+	bmisx = inb(bmiba+Bmisx);
+	switch(bmisx & (Ideints|Idedmae|Bmidea)){
+	case Bmidea:
+		/*
+		 * Data transfer still in progress, nothing to do
+		 * (this should never happen).
+		 */
+		return;
+
+	case Ideints:
+	case Ideints|Bmidea:
+		/*
+		 * Normal termination, tidy up.
+		 */
+		drive->data += count;
+		break;
+
+	default:
+		/*
+		 * What's left are error conditions (memory transfer
+		 * problem) and the device is not done but the PRD is
+		 * exhausted. For both cases must somehow tell the
+		 * drive to abort.
+		 */
+		ataabort(drive, 0);
+		break;
+	}
+	atadmastop(ctlr);
+	ctlr->done = 1;
+}
+
+static void
+atapktinterrupt(Drive* drive)
+{
+	Ctlr* ctlr;
+	int cmdport, len;
+
+	ctlr = drive->ctlr;
+	cmdport = ctlr->cmdport;
+	switch(inb(cmdport+Ir) & (/*Rel|*/Io|Cd)){
+	case Cd:
+		outss(cmdport+Data, drive->pktcmd, drive->pkt/2);
+		break;
+
+	case 0:
+		len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo);
+		if(drive->data+len > drive->limit){
+			atanop(drive, 0);
+			break;
+		}
+		outss(cmdport+Data, drive->data, len/2);
+		drive->data += len;
+		break;
+
+	case Io:
+		len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo);
+		if(drive->data+len > drive->limit){
+			atanop(drive, 0);
+			break;
+		}
+		inss(cmdport+Data, drive->data, len/2);
+		drive->data += len;
+		break;
+
+	case Io|Cd:
+		if(drive->pktdma)
+			atadmainterrupt(drive, drive->dlen);
+		else
+			ctlr->done = 1;
+		break;
+	}
+}
+
+static int
+atapktio(Drive* drive, uchar* cmd, int clen)
+{
+	Ctlr *ctlr;
+	int as, cmdport, ctlport, len, r, timeo;
+
+	if(cmd[0] == 0x5A && (cmd[2] & 0x3F) == 0)
+		return atamodesense(drive, cmd);
+
+	r = SDok;
+
+	drive->command = Cpkt;
+	memmove(drive->pktcmd, cmd, clen);
+	memset(drive->pktcmd+clen, 0, drive->pkt-clen);
+	drive->limit = drive->data+drive->dlen;
+
+	ctlr = drive->ctlr;
+	cmdport = ctlr->cmdport;
+	ctlport = ctlr->ctlport;
+
+	qlock(ctlr);
+
+	if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 107*1000) < 0){
+		qunlock(ctlr);
+		return -1;
+	}
+
+	ilock(ctlr);
+	if(drive->dlen && drive->dmactl && !atadmasetup(drive, drive->dlen))
+		drive->pktdma = Dma;
+	else
+		drive->pktdma = 0;
+
+	outb(cmdport+Features, drive->pktdma);
+	outb(cmdport+Count, 0);
+	outb(cmdport+Sector, 0);
+	len = 16*drive->secsize;
+	outb(cmdport+Bytelo, len);
+	outb(cmdport+Bytehi, len>>8);
+	outb(cmdport+Dh, drive->dev);
+	ctlr->done = 0;
+	ctlr->curdrive = drive;
+	ctlr->command = Cpkt;		/* debugging */
+	if(drive->pktdma)
+		atadmastart(ctlr, drive->write);
+	outb(cmdport+Cmd, Cpkt);
+
+	if((drive->info[Iconfig] & Mdrq) != 0x0020){
+		microdelay(1);
+		as = ataready(cmdport, ctlport, 0, Bsy, Drq|Chk, 4*1000);
+		if(as < 0)
+			r = SDtimeout;
+		else if(as & Chk)
+			r = SDcheck;
+		else
+			atapktinterrupt(drive);
+	}
+	iunlock(ctlr);
+
+	while(waserror())
+		;
+	if(!drive->pktdma)
+		sleep(ctlr, atadone, ctlr);
+	else for(timeo = 0; !ctlr->done; timeo++){
+		tsleep(ctlr, atadone, ctlr, 1000);
+		if(ctlr->done)
+			break;
+		ilock(ctlr);
+		atadmainterrupt(drive, 0);
+		if(!drive->error && timeo > 10){
+			ataabort(drive, 0);
+			atadmastop(ctlr);
+			drive->dmactl = 0;
+			drive->error |= Abrt;
+		}
+		if(drive->error){
+			drive->status |= Chk;
+			ctlr->curdrive = nil;
+		}
+		iunlock(ctlr);
+	}
+	poperror();
+
+	qunlock(ctlr);
+
+	if(drive->status & Chk)
+		r = SDcheck;
+
+	return r;
+}
+
+static uchar cmd48[256] = {
+	[Crs]	Crs48,
+	[Crd]	Crd48,
+	[Crdq]	Crdq48,
+	[Crsm]	Crsm48,
+	[Cws]	Cws48,
+	[Cwd]	Cwd48,
+	[Cwdq]	Cwdq48,
+	[Cwsm]	Cwsm48,
+};
+
+static int
+atageniostart(Drive* drive, Devsize lba)
+{
+	Ctlr *ctlr;
+	uchar cmd;
+	int as, c, cmdport, ctlport, h, len, s, use48;
+
+	use48 = 0;
+	if((drive->flags&Lba48always) || (lba>>28) || drive->count > 256){
+		if(!(drive->flags & Lba48))
+			return -1;
+		use48 = 1;
+		c = h = s = 0;
+	}else if(drive->dev & Lba){
+		c = (lba>>8) & 0xFFFF;
+		h = (lba>>24) & 0x0F;
+		s = lba & 0xFF;
+	}else{
+		c = lba/(drive->s*drive->h);
+		h = ((lba/drive->s) % drive->h);
+		s = (lba % drive->s) + 1;
+	}
+
+	ctlr = drive->ctlr;
+	cmdport = ctlr->cmdport;
+	ctlport = ctlr->ctlport;
+	if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 101*1000) < 0)
+		return -1;
+
+	ilock(ctlr);
+	if(drive->dmactl && !atadmasetup(drive, drive->count*drive->secsize)){
+		if(drive->write)
+			drive->command = Cwd;
+		else
+			drive->command = Crd;
+	}
+	else if(drive->rwmctl){
+		drive->block = drive->rwm*drive->secsize;
+		if(drive->write)
+			drive->command = Cwsm;
+		else
+			drive->command = Crsm;
+	}
+	else{
+		drive->block = drive->secsize;
+		if(drive->write)
+			drive->command = Cws;
+		else
+			drive->command = Crs;
+	}
+	drive->limit = drive->data + drive->count*drive->secsize;
+	cmd = drive->command;
+	if(use48){
+		outb(cmdport+Count, (drive->count>>8) & 0xFF);
+		outb(cmdport+Count, drive->count & 0XFF);
+		outb(cmdport+Lbalo, (lba>>24) & 0xFF);
+		outb(cmdport+Lbalo, lba & 0xFF);
+		outb(cmdport+Lbamid, (lba>>32) & 0xFF);
+		outb(cmdport+Lbamid, (lba>>8) & 0xFF);
+		outb(cmdport+Lbahi, (lba>>40) & 0xFF);
+		outb(cmdport+Lbahi, (lba>>16) & 0xFF);
+		outb(cmdport+Dh, drive->dev|Lba);
+		cmd = cmd48[cmd];
+
+		if(DEBUG & Dbg48BIT)
+			print("using 48-bit commands\n");
+	}else{
+		outb(cmdport+Count, drive->count);
+		outb(cmdport+Sector, s);
+		outb(cmdport+Cyllo, c);
+		outb(cmdport+Cylhi, c>>8);
+		outb(cmdport+Dh, drive->dev|h);
+	}
+	ctlr->done = 0;
+	ctlr->curdrive = drive;
+	ctlr->command = drive->command;	/* debugging */
+	outb(cmdport+Cmd, cmd);
+
+	switch(drive->command){
+	case Cws:
+	case Cwsm:
+		microdelay(1);
+		as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 1000);
+		if(as < 0 || (as & Err)){
+			iunlock(ctlr);
+			return -1;
+		}
+		len = drive->block;
+		if(drive->data+len > drive->limit)
+			len = drive->limit-drive->data;
+		outss(cmdport+Data, drive->data, len/2);
+		break;
+
+	case Crd:
+	case Cwd:
+		atadmastart(ctlr, drive->write);
+		break;
+	}
+	iunlock(ctlr);
+
+	return 0;
+}
+
+static int
+atagenioretry(Drive* drive)
+{
+	if(drive->dmactl){
+		drive->dmactl = 0;
+		print("atagenioretry: disabling dma\n");
+	}
+	else if(drive->rwmctl)
+		drive->rwmctl = 0;
+	else
+		return atasetsense(drive, SDcheck, 4, 8, drive->error);
+
+	return SDretry;
+}
+
+static int
+atagenio(Drive* drive, uchar* cmd, int)
+{
+	uchar *p;
+	Ctlr *ctlr;
+	int count, max;
+	Devsize lba, len;
+
+	/*
+	 * Map SCSI commands into ATA commands for discs.
+	 * Fail any command with a LUN except INQUIRY which
+	 * will return 'logical unit not supported'.
+	 */
+	if((cmd[1]>>5) && cmd[0] != 0x12)
+		return atasetsense(drive, SDcheck, 0x05, 0x25, 0);
+
+	switch(cmd[0]){
+	default:
+		return atasetsense(drive, SDcheck, 0x05, 0x20, 0);
+
+	case 0x00:			/* test unit ready */
+		return SDok;
+
+	case 0x03:			/* request sense */
+		if(cmd[4] < sizeof(drive->sense))
+			len = cmd[4];
+		else
+			len = sizeof(drive->sense);
+		if(drive->data && drive->dlen >= len){
+			memmove(drive->data, drive->sense, len);
+			drive->data += len;
+		}
+		return SDok;
+
+	case 0x12:			/* inquiry */
+		if(cmd[4] < sizeof(drive->inquiry))
+			len = cmd[4];
+		else
+			len = sizeof(drive->inquiry);
+		if(drive->data && drive->dlen >= len){
+			memmove(drive->data, drive->inquiry, len);
+			drive->data += len;
+		}
+		return SDok;
+
+	case 0x1B:			/* start/stop unit */
+		/*
+		 * NOP for now, can use the power management feature
+		 * set later.
+		 */
+		return SDok;
+
+	case 0x25:			/* read capacity */
+		if((cmd[1] & 0x01) || cmd[2] || cmd[3])
+			return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
+		if(drive->data == nil || drive->dlen < 8)
+			return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
+		/*
+		 * Read capacity returns the LBA of the last sector.
+		 */
+		len = drive->sectors-1;
+		p = drive->data;
+		*p++ = len>>24;
+		*p++ = len>>16;
+		*p++ = len>>8;
+		*p++ = len;
+		len = drive->secsize;
+		*p++ = len>>24;
+		*p++ = len>>16;
+		*p++ = len>>8;
+		*p = len;
+		drive->data += 8;
+		return SDok;
+
+	case 0x9E:			/* long read capacity */
+		if((cmd[1] & 0x01) || cmd[2] || cmd[3])
+			return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
+		if(drive->data == nil || drive->dlen < 8)
+			return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
+		/*
+		 * Read capacity returns the LBA of the last sector.
+		 */
+		len = drive->sectors-1;
+		p = drive->data;
+		*p++ = len>>56;
+		*p++ = len>>48;
+		*p++ = len>>40;
+		*p++ = len>>32;
+		*p++ = len>>24;
+		*p++ = len>>16;
+		*p++ = len>>8;
+		*p++ = len;
+		len = drive->secsize;
+		*p++ = len>>24;
+		*p++ = len>>16;
+		*p++ = len>>8;
+		*p = len;
+		drive->data += 8;
+		return SDok;
+
+	case 0x28:			/* read */
+	case 0x2A:			/* write */
+		break;
+
+	case 0x5A:
+		return atamodesense(drive, cmd);
+	}
+
+	ctlr = drive->ctlr;
+	lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5];
+	count = (cmd[7]<<8)|cmd[8];
+	if(drive->data == nil)
+		return SDok;
+	if(drive->dlen < count*drive->secsize)
+		count = drive->dlen/drive->secsize;
+	qlock(ctlr);
+	while(count){
+		max = (drive->flags&Lba48) ? 65536 : 256;
+		if(count > max)
+			drive->count = max;
+		else
+			drive->count = count;
+		if(atageniostart(drive, lba)){
+			ilock(ctlr);
+			atanop(drive, 0);
+			iunlock(ctlr);
+			qunlock(ctlr);
+			return atagenioretry(drive);
+		}
+
+		while(waserror())
+			;
+		tsleep(ctlr, atadone, ctlr, 30*1000);
+		poperror();
+		if(!ctlr->done){
+			/*
+			 * What should the above timeout be? In
+			 * standby and sleep modes it could take as
+			 * long as 30 seconds for a drive to respond.
+			 * Very hard to get out of this cleanly.
+			 */
+			atadumpstate(drive, cmd, lba, count);
+			ataabort(drive, 1);
+			qunlock(ctlr);
+			return atagenioretry(drive);
+		}
+
+		if(drive->status & Err){
+			qunlock(ctlr);
+			return atasetsense(drive, SDcheck, 4, 8, drive->error);
+		}
+		count -= drive->count;
+		lba += drive->count;
+	}
+	qunlock(ctlr);
+
+	return SDok;
+}
+
+static int
+atario(SDreq* r)
+{
+	Ctlr *ctlr;
+	Drive *drive;
+	SDunit *unit;
+	uchar cmd10[10], *cmdp, *p;
+	int clen, reqstatus, status;
+
+	unit = r->unit;
+	if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil){
+		r->status = SDtimeout;
+		return SDtimeout;
+	}
+	drive = ctlr->drive[unit->subno];
+
+	/*
+	 * Most SCSI commands can be passed unchanged except for
+	 * the padding on the end. The few which require munging
+	 * are not used internally. Mode select/sense(6) could be
+	 * converted to the 10-byte form but it's not worth the
+	 * effort. Read/write(6) are easy.
+	 */
+	switch(r->cmd[0]){
+	case 0x08:			/* read */
+	case 0x0A:			/* write */
+		cmdp = cmd10;
+		memset(cmdp, 0, sizeof(cmd10));
+		cmdp[0] = r->cmd[0]|0x20;
+		cmdp[1] = r->cmd[1] & 0xE0;
+		cmdp[5] = r->cmd[3];
+		cmdp[4] = r->cmd[2];
+		cmdp[3] = r->cmd[1] & 0x0F;
+		cmdp[8] = r->cmd[4];
+		clen = sizeof(cmd10);
+		break;
+
+	default:
+		cmdp = r->cmd;
+		clen = r->clen;
+		break;
+	}
+
+	qlock(drive);
+retry:
+	drive->write = r->write;
+	drive->data = r->data;
+	drive->dlen = r->dlen;
+
+	drive->status = 0;
+	drive->error = 0;
+	if(drive->pkt)
+		status = atapktio(drive, cmdp, clen);
+	else
+		status = atagenio(drive, cmdp, clen);
+	if(status == SDretry){
+		if(DbgDEBUG)
+			print("%s: retry: dma %8.8uX rwm %4.4uX\n",
+				unit->name, drive->dmactl, drive->rwmctl);
+		goto retry;
+	}
+	if(status == SDok){
+		atasetsense(drive, SDok, 0, 0, 0);
+		if(drive->data){
+			p = r->data;
+			r->rlen = drive->data - p;
+		}
+		else
+			r->rlen = 0;
+	}
+	else if(status == SDcheck && !(r->flags & SDnosense)){
+		drive->write = 0;
+		memset(cmd10, 0, sizeof(cmd10));
+		cmd10[0] = 0x03;
+		cmd10[1] = r->lun<<5;
+		cmd10[4] = sizeof(r->sense)-1;
+		drive->data = r->sense;
+		drive->dlen = sizeof(r->sense)-1;
+		drive->status = 0;
+		drive->error = 0;
+		if(drive->pkt)
+			reqstatus = atapktio(drive, cmd10, 6);
+		else
+			reqstatus = atagenio(drive, cmd10, 6);
+		if(reqstatus == SDok){
+			r->flags |= SDvalidsense;
+			atasetsense(drive, SDok, 0, 0, 0);
+		}
+	}
+	qunlock(drive);
+	r->status = status;
+	if(status != SDok)
+		return status;
+
+	/*
+	 * Fix up any results.
+	 * Many ATAPI CD-ROMs ignore the LUN field completely and
+	 * return valid INQUIRY data. Patch the response to indicate
+	 * 'logical unit not supported' if the LUN is non-zero.
+	 */
+	switch(cmdp[0]){
+	case 0x12:			/* inquiry */
+		if((p = r->data) == nil)
+			break;
+		if((cmdp[1]>>5) && (!drive->pkt || (p[0] & 0x1F) == 0x05))
+			p[0] = 0x7F;
+		/*FALLTHROUGH*/
+	default:
+		break;
+	}
+
+	return SDok;
+}
+
+static void
+atainterrupt(Ureg*, void* arg)
+{
+	Ctlr *ctlr;
+	Drive *drive;
+	int cmdport, len, status;
+
+	ctlr = arg;
+
+	ilock(ctlr);
+	if(inb(ctlr->ctlport+As) & Bsy){
+		iunlock(ctlr);
+		if(DEBUG & DbgBsy)
+			print("IBsy+");
+		return;
+	}
+	cmdport = ctlr->cmdport;
+	status = inb(cmdport+Status);
+	if((drive = ctlr->curdrive) == nil){
+		iunlock(ctlr);
+		if((DEBUG & DbgINL) && ctlr->command != Cedd)
+			print("Inil%2.2uX+", ctlr->command);
+		return;
+	}
+
+	if(status & Err)
+		drive->error = inb(cmdport+Error);
+	else switch(drive->command){
+	default:
+		drive->error = Abrt;
+		break;
+
+	case Crs:
+	case Crsm:
+		if(!(status & Drq)){
+			drive->error = Abrt;
+			break;
+		}
+		len = drive->block;
+		if(drive->data+len > drive->limit)
+			len = drive->limit-drive->data;
+		inss(cmdport+Data, drive->data, len/2);
+		drive->data += len;
+		if(drive->data >= drive->limit)
+			ctlr->done = 1;
+		break;
+
+	case Cws:
+	case Cwsm:
+		len = drive->block;
+		if(drive->data+len > drive->limit)
+			len = drive->limit-drive->data;
+		drive->data += len;
+		if(drive->data >= drive->limit){
+			ctlr->done = 1;
+			break;
+		}
+		if(!(status & Drq)){
+			drive->error = Abrt;
+			break;
+		}
+		len = drive->block;
+		if(drive->data+len > drive->limit)
+			len = drive->limit-drive->data;
+		outss(cmdport+Data, drive->data, len/2);
+		break;
+
+	case Cpkt:
+		atapktinterrupt(drive);
+		break;
+
+	case Crd:
+	case Cwd:
+		atadmainterrupt(drive, drive->count*drive->secsize);
+		break;
+
+	case Cstandby:
+		ctlr->done = 1;
+		break;
+	}
+	iunlock(ctlr);
+
+	if(drive->error){
+		status |= Err;
+		ctlr->done = 1;
+	}
+
+	if(ctlr->done){
+		ctlr->curdrive = nil;
+		drive->status = status;
+		wakeup(ctlr);
+	}
+}
+
+static SDev*
+atapnp(void)
+{
+	Ctlr *ctlr;
+	Pcidev *p;
+	int channel, ispc87415, pi, r;
+	SDev *legacy[2], *sdev, *head, *tail;
+
+	legacy[0] = legacy[1] = head = tail = nil;
+	if(sdev = ataprobe(0x1F0, 0x3F4, IrqATA0)){
+		head = tail = sdev;
+		legacy[0] = sdev;
+	}
+	if(sdev = ataprobe(0x170, 0x374, IrqATA1)){
+		if(head != nil)
+			tail->next = sdev;
+		else
+			head = sdev;
+		tail = sdev;
+		legacy[1] = sdev;
+	}
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		/*
+		 * Look for devices with the correct class and sub-class
+		 * code and known device and vendor ID; add native-mode
+		 * channels to the list to be probed, save info for the
+		 * compatibility mode channels.
+		 * Note that the legacy devices should not be considered
+		 * PCI devices by the interrupt controller.
+		 * For both native and legacy, save info for busmastering
+		 * if capable.
+		 * Promise Ultra ATA/66 (PDC20262) appears to
+		 * 1) give a sub-class of 'other mass storage controller'
+		 *    instead of 'IDE controller', regardless of whether it's
+		 *    the only controller or not;
+		 * 2) put 0 in the programming interface byte (probably
+		 *    as a consequence of 1) above).
+		 * Sub-class code 0x04 is 'RAID controller', e.g. VIA VT8237.
+		 */
+		if(p->ccrb != 0x01)
+			continue;
+
+		/*
+		 * file server special: ccru is a short in the FS kernel,
+		 * thus the cast to uchar.
+		 */
+		switch ((uchar)p->ccru) {
+		case 1:
+		case 4:
+		case 0x80:
+			break;
+		default:
+			continue;
+		}
+
+		pi = p->ccrp;
+		ispc87415 = 0;
+
+		switch((p->did<<16)|p->vid){
+		default:
+			continue;
+
+		case (0x0002<<16)|0x100B:	/* NS PC87415 */
+			/*
+			 * Disable interrupts on both channels until
+			 * after they are probed for drives.
+			 * This must be called before interrupts are
+			 * enabled because the IRQ may be shared.
+			 */
+			ispc87415 = 1;
+			pcicfgw32(p, 0x40, 0x00000300);
+			break;
+		case (0x1000<<16)|0x1042:	/* PC-Tech RZ1000 */
+			/*
+			 * Turn off prefetch. Overkill, but cheap.
+			 */
+			r = pcicfgr32(p, 0x40);
+			r &= ~0x2000;
+			pcicfgw32(p, 0x40, r);
+			break;
+		case (0x4D38<<16)|0x105A:	/* Promise PDC20262 */
+		case (0x4D30<<16)|0x105A:	/* Promise PDC202xx */
+		case (0x4D68<<16)|0x105A:	/* Promise PDC20268 */
+		case (0x4D69<<16)|0x105A:	/* Promise Ultra/133 TX2 */
+		case (0x3373<<16)|0x105A:	/* Promise 20378 RAID */
+		case (0x3149<<16)|0x1106:	/* VIA VT8237 SATA/RAID */
+			pi = 0x85;
+			break;
+		case (0x0004<<16)|0x1103:	/* HighPoint HPT-370 */
+			pi = 0x85;
+			/*
+			 * Turn off fast interrupt prediction.
+			 */
+			if((r = pcicfgr8(p, 0x51)) & 0x80)
+				pcicfgw8(p, 0x51, r & ~0x80);
+			if((r = pcicfgr8(p, 0x55)) & 0x80)
+				pcicfgw8(p, 0x55, r & ~0x80);
+			break;
+		case (0x0640<<16)|0x1095:	/* CMD 640B */
+			/*
+			 * Bugfix code here...
+			 */
+			break;
+		case (0x7441<<16)|0x1022:	/* AMD 768 */
+			/*
+			 * Set:
+			 *	0x41	prefetch, postwrite;
+			 *	0x43	FIFO configuration 1/2 and 1/2;
+			 *	0x44	status register read retry;
+			 *	0x46	DMA read and end of sector flush.
+			 */
+			r = pcicfgr8(p, 0x41);
+			pcicfgw8(p, 0x41, r|0xF0);
+			r = pcicfgr8(p, 0x43);
+			pcicfgw8(p, 0x43, (r & 0x90)|0x2A);
+			r = pcicfgr8(p, 0x44);
+			pcicfgw8(p, 0x44, r|0x08);
+			r = pcicfgr8(p, 0x46);
+			pcicfgw8(p, 0x46, (r & 0x0C)|0xF0);
+		case (0x7469<<16)|0x1022:	/* AMD 3111 */
+			break;
+		case (0x0646<<16)|0x1095:	/* CMD 646 */
+		case (0x0571<<16)|0x1106:	/* VIA 82C686 */
+		case (0x0211<<16)|0x1166:	/* ServerWorks IB6566 */
+		case (0x1230<<16)|0x8086:	/* 82371FB (PIIX) */
+		case (0x7010<<16)|0x8086:	/* 82371SB (PIIX3) */
+		case (0x7111<<16)|0x8086:	/* 82371[AE]B (PIIX4[E]) */
+		case (0x2411<<16)|0x8086:	/* 82801AA (ICH) */
+		case (0x2421<<16)|0x8086:	/* 82801AB (ICH0) */
+		case (0x244A<<16)|0x8086:	/* 82801BA (ICH2, Mobile) */
+		case (0x244B<<16)|0x8086:	/* 82801BA (ICH2, High-End) */
+		case (0x248A<<16)|0x8086:	/* 82801CA (ICH3, Mobile) */
+		case (0x248B<<16)|0x8086:	/* 82801CA (ICH3, High-End) */
+		case (0x24CA<<16)|0x8086:	/* 82801DBM (ICH4, Mobile) */
+		case (0x24CB<<16)|0x8086:	/* 82801DB (ICH4, High-End) */
+		case (0x24DB<<16)|0x8086:	/* 82801EB (ICH5) */
+			break;
+		}
+
+		for(channel = 0; channel < 2; channel++){
+			if(pi & (1<<(2*channel))){
+				sdev = ataprobe(p->mem[0+2*channel].bar & ~0x01,
+						p->mem[1+2*channel].bar & ~0x01,
+						p->intl);
+				if(sdev == nil)
+					continue;
+
+				ctlr = sdev->ctlr;
+				if(ispc87415) {
+					ctlr->ienable = pc87415ienable;
+					print("pc87415disable: not yet implemented\n");
+				}
+
+				if(head != nil)
+					tail->next = sdev;
+				else
+					head = sdev;
+				tail = sdev;
+				ctlr->tbdf = p->tbdf;
+			}
+			else if((sdev = legacy[channel]) == nil)
+				continue;
+			else
+				ctlr = sdev->ctlr;
+
+			ctlr->pcidev = p;
+			if(!(pi & 0x80))
+				continue;
+			ctlr->bmiba = (p->mem[4].bar & ~0x01) + channel*8;
+		}
+	}
+
+#ifdef notdef
+if(0){
+	int port;
+	ISAConf isa;
+
+	/*
+	 * Hack for PCMCIA drives.
+	 * This will be tidied once we figure out how the whole
+	 * removeable device thing is going to work.
+	 */
+	memset(&isa, 0, sizeof(isa));
+	isa.port = 0x180;		/* change this for your machine */
+	isa.irq = 11;			/* change this for your machine */
+
+	port = isa.port+0x0C;
+	channel = pcmspecial("MK2001MPL", &isa);
+	if(channel == -1)
+		channel = pcmspecial("SunDisk", &isa);
+	if(channel == -1){
+		isa.irq = 10;
+		channel = pcmspecial("CF", &isa);
+	}
+	if(channel == -1){
+		isa.irq = 10;
+		channel = pcmspecial("OLYMPUS", &isa);
+	}
+	if(channel == -1){
+		port = isa.port+0x204;
+		channel = pcmspecial("ATA/ATAPI", &isa);
+	}
+	if(channel >= 0 && (sdev = ataprobe(isa.port, port, isa.irq)) != nil){
+		if(head != nil)
+			tail->next = sdev;
+		else
+			head = sdev;
+	}
+}
+#endif
+	return head;
+}
+
+static SDev*
+atalegacy(int port, int irq)
+{
+	return ataprobe(port, port+0x204, irq);
+}
+
+static SDev*
+ataid(SDev* sdev)
+{
+	int i;
+	Ctlr *ctlr;
+	char name[32];
+
+	/*
+	 * Legacy controllers are always 'C' and 'D' and if
+	 * they exist and have drives will be first in the list.
+	 * If there are no active legacy controllers, native
+	 * controllers start at 'C'.
+	 */
+	if(sdev == nil)
+		return nil;
+	ctlr = sdev->ctlr;
+	if(ctlr->cmdport == 0x1F0 || ctlr->cmdport == 0x170)
+		i = 2;
+	else
+		i = 0;
+	while(sdev){
+		if(sdev->ifc == &sdataifc){
+			ctlr = sdev->ctlr;
+			if(ctlr->cmdport == 0x1F0)
+				sdev->idno = 'C';
+			else if(ctlr->cmdport == 0x170)
+				sdev->idno = 'D';
+			else{
+				sdev->idno = 'C'+i;
+				i++;
+			}
+			snprint(name, sizeof(name), "sd%c", sdev->idno);
+			kstrdup(&sdev->name, name);
+		}
+		sdev = sdev->next;
+	}
+
+	return nil;
+}
+
+static int
+ataenable(SDev* sdev)
+{
+	Ctlr *ctlr;
+	char name[32];
+
+	ctlr = sdev->ctlr;
+
+	if(ctlr->bmiba){
+#define ALIGN	(4 * 1024)
+		if(ctlr->pcidev != nil)
+			pcisetbme(ctlr->pcidev);
+		// ctlr->prdt = xspanalloc(Nprd*sizeof(Prd), 4, 4*1024);
+		ctlr->prdtbase = xalloc(Nprd * sizeof(Prd) + ALIGN);
+		ctlr->prdt = (Prd *)(((ulong)ctlr->prdtbase + ALIGN) & ~(ALIGN - 1));
+	}
+	snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name);
+	intrenable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name);
+	outb(ctlr->ctlport+Dc, 0);
+	if(ctlr->ienable)
+		ctlr->ienable(ctlr);
+
+	return 1;
+}
+
+static int
+atadisable(SDev *sdev)
+{
+	Ctlr *ctlr;
+	char name[32];
+
+	ctlr = sdev->ctlr;
+	outb(ctlr->ctlport+Dc, Nien);		/* disable interrupts */
+	if (ctlr->idisable)
+		ctlr->idisable(ctlr);
+	snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name);
+	intrdisable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name);
+	if (ctlr->bmiba) {
+		if (ctlr->pcidev)
+			pciclrbme(ctlr->pcidev);
+		xfree(ctlr->prdtbase);
+	}
+	return 0;
+}
+
+#ifndef FS
+static int
+atarctl(SDunit* unit, char* p, int l)
+{
+	int n;
+	Ctlr *ctlr;
+	Drive *drive;
+
+	if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil)
+		return 0;
+	drive = ctlr->drive[unit->subno];
+
+	qlock(drive);
+	n = snprint(p, l, "config %4.4uX capabilities %4.4uX",
+		drive->info[Iconfig], drive->info[Icapabilities]);
+	if(drive->dma)
+		n += snprint(p+n, l-n, " dma %8.8uX dmactl %8.8uX",
+			drive->dma, drive->dmactl);
+	if(drive->rwm)
+		n += snprint(p+n, l-n, " rwm %ud rwmctl %ud",
+			drive->rwm, drive->rwmctl);
+	if(drive->flags&Lba48)
+		n += snprint(p+n, l-n, " lba48always %s",
+			(drive->flags&Lba48always) ? "on" : "off");
+	n += snprint(p+n, l-n, "\n");
+	if(drive->sectors){
+		n += snprint(p+n, l-n, "geometry %lld %d",
+			(Wideoff)drive->sectors, drive->secsize);
+		if(drive->pkt == 0)
+			n += snprint(p+n, l-n, " %d %d %d",
+				drive->c, drive->h, drive->s);
+		n += snprint(p+n, l-n, "\n");
+	}
+	qunlock(drive);
+
+	return n;
+}
+
+static int
+atawctl(SDunit* unit, Cmdbuf* cb)
+{
+	int period;
+	Ctlr *ctlr;
+	Drive *drive;
+
+	if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil)
+		return 0;
+	drive = ctlr->drive[unit->subno];
+
+	qlock(drive);
+	if(waserror()){
+		qunlock(drive);
+		nexterror();
+	}
+
+	/*
+	 * Dma and rwm control is passive at the moment,
+	 * i.e. it is assumed that the hardware is set up
+	 * correctly already either by the BIOS or when
+	 * the drive was initially identified.
+	 */
+	if(strcmp(cb->f[0], "dma") == 0){
+		if(cb->nf != 2 || drive->dma == 0)
+			error(Ebadctl);
+		if(strcmp(cb->f[1], "on") == 0)
+			drive->dmactl = drive->dma;
+		else if(strcmp(cb->f[1], "off") == 0)
+			drive->dmactl = 0;
+		else
+			error(Ebadctl);
+	}
+	else if(strcmp(cb->f[0], "rwm") == 0){
+		if(cb->nf != 2 || drive->rwm == 0)
+			error(Ebadctl);
+		if(strcmp(cb->f[1], "on") == 0)
+			drive->rwmctl = drive->rwm;
+		else if(strcmp(cb->f[1], "off") == 0)
+			drive->rwmctl = 0;
+		else
+			error(Ebadctl);
+	}
+	else if(strcmp(cb->f[0], "standby") == 0){
+		switch(cb->nf){
+		default:
+			error(Ebadctl);
+		case 2:
+			period = strtol(cb->f[1], 0, 0);
+			if(period && (period < 30 || period > 240*5))
+				error(Ebadctl);
+			period /= 5;
+			break;
+		}
+		if(atastandby(drive, period) != SDok)
+			error(Ebadctl);
+	}
+	else if(strcmp(cb->f[0], "lba48always") == 0){
+		if(cb->nf != 2 || !(drive->flags&Lba48))
+			error(Ebadctl);
+		if(strcmp(cb->f[1], "on") == 0)
+			drive->flags |= Lba48always;
+		else if(strcmp(cb->f[1], "off") == 0)
+			drive->flags &= ~Lba48always;
+		else
+			error(Ebadctl);
+	}
+	else
+		error(Ebadctl);
+	qunlock(drive);
+	poperror();
+
+	return 0;
+}
+#endif
+
+SDifc sdataifc = {
+	"ata",				/* name */
+
+	atapnp,				/* pnp */
+	atalegacy,			/* legacy */
+	ataid,				/* id */
+	ataenable,			/* enable */
+	atadisable,			/* disable */
+
+	scsiverify,			/* verify */
+	scsionline,			/* online */
+	atario,				/* rio */
+	nil,	//atarctl,		/* rctl */
+	nil,	//atawctl,		/* wctl */
+
+	scsibio,			/* bio */
+#ifndef FS
+	nil,	//ataprobew,		/* probe */
+	ataclear,			/* clear */
+	atastat,			/* stat */
+#endif
+};
+
+/*
+ * file-server-specific routines
+ *
+ * ata* routines below this point are used to access nvram file,
+ * ide* routines implement the `h' device and call the ata* routines.
+ */
+
+static Drive*
+atapart(Drive *dp)
+{
+	return dp;
+}
+
+static Drive*
+atadriveprobe(int driveno)
+{
+	Drive *drive;
+
+	drive = atadrive[driveno];
+	if (drive == nil)
+		return nil;
+	drive->driveno = driveno;
+	if(drive->online == 0){
+		if(drive->lba)
+			print("h%d: LBA %llud sectors\n",
+				drive->driveno, (Wideoff)drive->sectors);
+		else
+			print("h%d: CHS %d/%d/%d %llud sectors\n",
+				drive->driveno, drive->c, drive->h, drive->s,
+				(Wideoff)drive->sectors);
+		drive->online = 1;
+	}
+	return atapart(drive);
+}
+
+/* find all the controllers, enable interrupts, set up SDevs & SDunits */
+int
+atainit(void)
+{
+	unsigned i;
+	SDev *sdp;
+	SDunit *sup;
+	static int first = 1;
+
+	if (first)
+		first = 0;
+	else
+		return 0xFF;
+
+	atapnp();
+
+	for (sdp = sdevs; sdp < sdevs + NCtlr; sdp++) {
+		i = sdp - sdevs;
+		sdp->ifc = &sdataifc;
+		sdp->nunit = 2;
+		sdp->index = i;
+		sdp->idno = 'C' + i;
+		sdp->ctlr = atactlr[i];
+		if (sdp->ctlr != nil)
+			ataenable(sdp);
+	}
+	for (sup = sdunits; sup < sdunits + NDrive; sup++) {
+		i = sup - sdunits;
+		sup->dev = sdevs + i/2;	/* controller */
+		sup->subno = i%2;	/* drive within controller */
+		snprint(sup->name, sizeof sup->name, "h%d", i);
+	}
+	return 0xFF;
+}
+
+Devsize
+ataseek(int driveno, Devsize offset)
+{
+	Drive *drive = atadrive[driveno];
+
+	if (drive == nil || !drive->online)
+		return -1;
+	drive->offset = offset;
+	return offset;
+}
+
+/* zero indicates failure; only otherinit() cares */
+int
+setatapart(int driveno, char *)
+{
+	/* atadriveprobe() sets drive->online */
+	if(atadriveprobe(driveno) == nil)
+		return 0;
+	return 1;
+}
+
+/*
+ * connect the old nvram (ata* routines) and ide* routines to sdata.c.
+ * an ugly hack.
+ */
+static long
+ataxfer(Drive *dp, void *, int inout, Devsize start, long bytes)
+{
+	unsigned driveno = dp->driveno;
+	ulong secsize = dp->secsize, sects;
+	SDunit *sdp = sdunits + driveno;
+
+	if (dp->driveno == -1)
+		panic("ataxfer: dp->driveno unset");
+	if (sdp->dev != sdevs + driveno/2)
+		panic("ataxfer: SDunit[%d].dev is wrong controller", driveno);
+	if (sdp->subno != driveno%2)
+		panic("ataxfer: SDunit[%d].subno is %d, not %d", driveno, sdp->subno, driveno%2);
+	if (sdp->sectors == 0) {
+		sdp->sectors = dp->sectors;
+		sdp->secsize = secsize;
+	}
+	sects = (bytes + secsize - 1) / secsize;	/* round up */
+	if (start%secsize != 0)
+		print("ataxfer: start offset not on sector boundary\n");
+	return scsibio(sdp, 0, inout, dp->ctlr->buf, sects, start/secsize);
+}
+
+/* ataread & atawrite do the real work; ideread and idewrite just call them */
+
+/* paranoia: only permit one outstanding I/O operation at a time */
+static QLock iolock;
+
+Off
+ataread(int driveno, void *a, long n)
+{
+	int skip;
+	Off rv, i;
+	uchar *aa = a;
+	Ctlr *cp;
+	Drive *dp;
+
+	dp = atadrive[driveno];
+	if(dp == nil || !dp->online)
+		return 0;
+
+	iolock.name = "ataio";
+	qlock(&iolock);
+	cp = dp->ctlr;
+	if (dp->secsize == 0)
+		panic("ataread: sector size of zero");
+	skip = dp->offset % dp->secsize;
+	for(rv = 0; rv < n; rv += i){
+		i = ataxfer(dp, nil, Read, dp->offset+rv-skip, n-rv+skip);
+		if(i == 0)
+			break;
+		if(i < 0) {
+			qunlock(&iolock);
+			return -1;
+		}
+		i -= skip;
+		if(i > n - rv)
+			i = n - rv;
+		memmove(aa+rv, cp->buf + skip, i);
+		skip = 0;
+	}
+	dp->offset += rv;
+	qunlock(&iolock);
+	return rv;
+}
+
+Off
+atawrite(int driveno, void *a, long n)
+{
+	Off rv, i, partial;
+	uchar *aa = a;
+	Ctlr *cp;
+	Drive *dp;
+
+	dp = atadrive[driveno];
+	if(dp == nil || !dp->online)
+		return 0;
+
+	qlock(&iolock);
+	cp = dp->ctlr;
+
+	/*
+	 *  if not starting on a sector boundary,
+	 *  read in the first sector before writing it out.
+	 */
+	if (dp->secsize == 0)
+		panic("atawrite: sector size of zero");
+	partial = dp->offset % dp->secsize;
+	if(partial){
+		if (ataxfer(dp, nil, Read, dp->offset-partial, dp->secsize) < 0){
+			qunlock(&iolock);
+			return -1;
+		}
+		if(partial+n > dp->secsize)
+			rv = dp->secsize - partial;
+		else
+			rv = n;
+		memmove(cp->buf+partial, aa, rv);
+		if(ataxfer(dp, nil, Write, dp->offset-partial, dp->secsize) < 0){
+			qunlock(&iolock);
+			return -1;
+		}
+	} else
+		rv = 0;
+
+	/*
+	 *  write out the full sectors (common case)
+	 */
+	partial = (n - rv) % dp->secsize;
+	n -= partial;
+	for(; rv < n; rv += i){
+		i = n - rv;
+		if(i > Maxxfer)
+			i = Maxxfer;
+		memmove(cp->buf, aa+rv, i);
+		i = ataxfer(dp, nil, Write, dp->offset+rv, i);
+		if(i == 0)
+			break;
+		if(i < 0) {
+			qunlock(&iolock);
+			return -1;
+		}
+	}
+
+	/*
+	 *  if not ending on a sector boundary,
+	 *  read in the last sector before writing it out.
+	 */
+	if(partial){
+		if(ataxfer(dp, nil, Read, dp->offset+rv, dp->secsize) < 0) {
+			qunlock(&iolock);
+			return -1;
+		}
+		memmove(cp->buf, aa+rv, partial);
+		if(ataxfer(dp, nil, Write, dp->offset+rv, dp->secsize) < 0) {
+			qunlock(&iolock);
+			return -1;
+		}
+		rv += partial;
+	}
+	dp->offset += rv;
+	qunlock(&iolock);
+	return rv;
+}
+
+/*
+ * normal interface
+ */
+
+/* result is size of d in blocks of RBUFSIZE bytes */
+Devsize
+idesize(Device *d)
+{
+	Drive *dp = d->private;
+
+	if (dp == nil)
+		return 0;
+	/*
+	 * dividing first is sloppy but reduces the range of intermediate
+	 * values, avoiding possible overflow.
+	 */
+	return (dp->sectors / RBUFSIZE) * dp->secsize;
+}
+
+void
+ideinit(Device *d)
+{
+	int driveno;
+	Drive *dp;
+
+	atainit();
+	if (d->private)
+		return;
+	/* call setatapart() first in case we didn't boot off this drive */
+	driveno = d->wren.ctrl*2 + d->wren.targ;
+	setatapart(driveno, "disk");
+	dp = atadriveprobe(driveno);
+	if (dp) {
+		print("ideinit(ctrl %d targ %d) driveno %d\n",
+			d->wren.ctrl, d->wren.targ, dp->driveno);
+		if (dp->driveno != driveno)
+			panic("ideinit: dp->dev != driveno");
+		d->private = dp;
+		/* print the sizes now, not later */
+		print(
+	"  idesize(driveno %d):  %llud %d-byte sectors -> %llud blocks\n",
+			dp->driveno, (Wideoff)dp->sectors, dp->secsize,
+			(Wideoff)idesize(d));
+	}
+}
+
+/*
+ * can't qlock(cp) here because atagenio() does so, & would deadlock,
+ * so we introduce a second lock (idelock).
+ */
+
+int
+ideread(Device *d, Devsize b, void *c)
+{
+	int x, driveno;
+	Drive *dp;
+	Ctlr *cp;
+
+	if (d == nil || d->private == nil)
+		return 1;
+	dp = d->private;
+	cp = dp->ctlr;
+	if (cp == nil)
+		panic("ideread: no controller for drive");
+
+	cp->idelock.name = "ideio";
+	qlock(&cp->idelock);
+	driveno = dp->driveno;
+	if (driveno == -1)
+		panic("ideread: dp->driveno unset");
+	IDPRINT("ideread(dev %lux, %lld, %lux, %d): %lux\n",
+		(ulong)d, (Wideoff)b, (ulong)c, driveno, (ulong)dp);
+	ataseek(driveno, b * RBUFSIZE);
+	x = ataread(driveno, c, RBUFSIZE) != RBUFSIZE;
+	qunlock(&cp->idelock);
+	return x;
+}
+
+int
+idewrite(Device *d, Devsize b, void *c)
+{
+	int x, driveno;
+	Drive *dp;
+	Ctlr *cp;
+
+	if (d == nil || d->private == nil)
+		return 1;
+	dp = d->private;
+	cp = dp->ctlr;
+	if (cp == nil)
+		panic("idewrite: no controller for drive");
+
+	cp->idelock.name = "ideio";
+	qlock(&cp->idelock);
+	driveno = dp->driveno;
+	if (driveno == -1)
+		panic("idewrite: dp->driveno unset");
+	IDPRINT("idewrite(%ux, %lld, %ux): driveno %d\n",
+		(int)d, (Wideoff)b, (int)c, driveno);
+	ataseek(driveno, b * RBUFSIZE);
+	x = atawrite(driveno, c, RBUFSIZE) != RBUFSIZE;
+	qunlock(&cp->idelock);
+	return x;
+}

+ 375 - 0
sys/src/fs/pc/sdscsi.c

@@ -0,0 +1,375 @@
+#include "all.h"
+#include "io.h"
+#include "mem.h"
+
+#include "sd.h"
+#include "../ip/ip.h"	/* for Ether */
+#include "etherif.h"	/* for Ether */
+#include "compat.h"
+#undef error
+
+static int
+scsitest(SDreq* r)
+{
+	r->write = 0;
+	memset(r->cmd, 0, sizeof(r->cmd));
+	r->cmd[1] = r->lun<<5;
+	r->clen = 6;
+	r->data = nil;
+	r->dlen = 0;
+	r->flags = 0;
+
+	r->status = ~0;
+
+// cgascreenputs("A", 1);
+	return r->unit->dev->ifc->rio(r);
+}
+
+int
+scsiverify(SDunit* unit)
+{
+	static SDreq *r;
+	int i, status;
+	static uchar *inquiry;
+
+	if((r = sdmalloc(r, sizeof(SDreq))) == nil)
+		return 0;
+
+	if((inquiry = sdmalloc(inquiry, sizeof(unit->inquiry))) == nil)
+		return 0;
+
+	r->unit = unit;
+	r->lun = 0;		/* ??? */
+
+	memset(unit->inquiry, 0, sizeof(unit->inquiry));
+	r->write = 0;
+	r->cmd[0] = 0x12;
+	r->cmd[1] = r->lun<<5;
+	r->cmd[4] = sizeof(unit->inquiry)-1;
+	r->clen = 6;
+	r->data = inquiry;
+	r->dlen = sizeof(unit->inquiry)-1;
+	r->flags = 0;
+
+	r->status = ~0;
+// cgascreenputs("B", 1);
+	if(unit->dev->ifc->rio(r) != SDok){
+		return 0;
+	}
+	memmove(unit->inquiry, inquiry, r->dlen);
+
+	SET(status);
+	for(i = 0; i < 3; i++){
+		while((status = scsitest(r)) == SDbusy)
+			;
+		if(status == SDok || status != SDcheck)
+			break;
+		if(!(r->flags & SDvalidsense))
+			break;
+		if((r->sense[2] & 0x0F) != 0x02)
+			continue;
+
+		/*
+		 * Unit is 'not ready'.
+		 * If it is in the process of becoming ready or needs
+		 * an initialising command, set status so it will be spun-up
+		 * below.
+		 * If there's no medium, that's OK too, but don't
+		 * try to spin it up.
+		 */
+		if(r->sense[12] == 0x04){
+			if(r->sense[13] == 0x02 || r->sense[13] == 0x01){
+				status = SDok;
+				break;
+			}
+		}
+		if(r->sense[12] == 0x3A)
+			break;
+	}
+
+	if(status == SDok){
+		/*
+		 * Try to ensure a direct-access device is spinning.
+		 * Ignore the result.
+		 */
+		if((unit->inquiry[0] & 0x1F) == 0){
+			memset(r->cmd, 0, sizeof(r->cmd));
+			r->write = 0;
+			r->cmd[0] = 0x1B;
+			r->cmd[1] = r->lun<<5;
+			r->cmd[4] = 1;
+			r->clen = 6;
+			r->data = nil;
+			r->dlen = 0;
+			r->flags = 0;
+
+			r->status = ~0;
+			unit->dev->ifc->rio(r);
+		}
+		return 1;
+	}
+	return 0;
+}
+
+int
+return0(void*)
+{
+	return 0;
+}
+
+static int
+scsirio(SDreq* r)
+{
+	/*
+	 * Perform an I/O request, returning
+	 *	-1	failure
+	 *	 0	ok
+	 *	 2	retry
+	 * The contents of r may be altered so the
+	 * caller should re-initialise if necesary.
+	 */
+	r->status = ~0;
+// cgascreenputs("C", 1);
+	switch(r->unit->dev->ifc->rio(r)){
+	default:
+		return -1;
+	case SDcheck:
+		if(!(r->flags & SDvalidsense))
+			return -1;
+		switch(r->sense[2] & 0x0F){
+		case 0x00:		/* no sense */
+		case 0x01:		/* recovered error */
+			return 2;
+		case 0x06:		/* check condition */
+			/*
+			 * 0x28 - not ready to ready transition,
+			 *	  medium may have changed.
+			 * 0x29 - power on or some type of reset.
+			 */
+			if(r->sense[12] == 0x28 && r->sense[13] == 0)
+				return 2;
+			if(r->sense[12] == 0x29)
+				return 2;
+			return -1;
+		case 0x02:		/* not ready */
+			/*
+			 * If no medium present, bail out.
+			 * If unit is becoming ready, rather than not
+			 * not ready, wait a little then poke it again. 							 */
+			if(r->sense[12] == 0x3A)
+				return -1;
+			if(r->sense[12] != 0x04 || r->sense[13] != 0x01)
+				return -1;
+
+			tsleep(nil, return0, 0, 500);
+			scsitest(r);
+			return 2;
+		default:
+			return -1;
+		}
+		return -1;
+	case SDok:
+		return 0;
+	}
+	return -1;
+}
+
+int
+scsionline(SDunit* unit)
+{
+	int ok;
+	static SDreq *r;
+	static uchar *p;
+
+	if((r = sdmalloc(r, sizeof(SDreq))) == nil)
+		return 0;
+
+	if((p = sdmalloc(p, 8)) == nil)
+		return 0;
+
+	ok = 0;
+
+	r->unit = unit;
+	r->lun = 0;				/* ??? */
+	for(;;){
+		/*
+		 * Read-capacity is mandatory for DA, WORM, CD-ROM and
+		 * MO. It may return 'not ready' if type DA is not
+		 * spun up, type MO or type CD-ROM are not loaded or just
+		 * plain slow getting their act together after a reset.
+		 */
+		r->write = 0;
+		memset(r->cmd, 0, sizeof(r->cmd));
+		r->cmd[0] = 0x25;
+		r->cmd[1] = r->lun<<5;
+		r->clen = 10;
+		r->data = p;
+		r->dlen = 8;
+		r->flags = 0;
+
+		r->status = ~0;
+// cgascreenputs("F", 1);
+		switch(scsirio(r)){
+		default:
+			break;
+		case 0:
+			unit->sectors = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
+			/*
+			 * Read-capacity returns the LBA of the last sector,
+			 * therefore the number of sectors must be incremented.
+			 */
+			unit->sectors++;
+			unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7];
+			ok = 1;
+			break;
+		case 2:
+			continue;
+		}
+		break;
+	}
+
+	return ok;
+}
+
+int
+scsiexec(SDunit* unit, int write, uchar* cmd, int clen, void* data, int* dlen)
+{
+	static SDreq *r;
+	int status;
+
+	if((r = sdmalloc(r, sizeof(SDreq))) == nil)
+		return SDmalloc;
+
+	r->unit = unit;
+	r->lun = cmd[1]>>5;		/* ??? */
+	r->write = write;
+	memmove(r->cmd, cmd, clen);
+	r->clen = clen;
+	r->data = data;
+	if(dlen)
+		r->dlen = *dlen;
+	r->flags = 0;
+
+	r->status = ~0;
+
+	/*
+	 * Call the device-specific I/O routine.
+	 * There should be no calls to 'error()' below this
+	 * which percolate back up.
+	 */
+// cgascreenputs("D", 1);
+	switch(status = unit->dev->ifc->rio(r)){
+	case SDok:
+		if(dlen)
+			*dlen = r->rlen;
+		/*FALLTHROUGH*/
+	case SDcheck:
+		/*FALLTHROUGH*/
+	default:
+		/*
+		 * It's more complicated than this. There are conditions
+		 * which are 'ok' but for which the returned status code
+		 * is not 'SDok'.
+		 * Also, not all conditions require a reqsense, might
+		 * need to do a reqsense here and make it available to the
+		 * caller somehow.
+		 *
+		 * Mañana.
+		 */
+		break;
+	}
+
+	return status;
+}
+
+long
+scsibio(SDunit* unit, int lun, int write, void* data, long nb, Off bno)
+{
+	static SDreq *r;
+	long rlen;
+
+	if((r = sdmalloc(r, sizeof(SDreq))) == nil)
+		return SDmalloc;
+
+	r->unit = unit;
+	r->lun = lun;
+again:
+	r->write = write;
+	if(write == 0)
+		r->cmd[0] = 0x28;
+	else
+		r->cmd[0] = 0x2A;
+	r->cmd[1] = (lun<<5);
+	r->cmd[2] = bno>>24;
+	r->cmd[3] = bno>>16;
+	r->cmd[4] = bno>>8;
+	r->cmd[5] = bno;
+	r->cmd[6] = 0;
+	r->cmd[7] = nb>>8;
+	r->cmd[8] = nb;
+	r->cmd[9] = 0;
+	r->clen = 10;
+	r->data = data;
+	r->dlen = nb*unit->secsize;
+	r->flags = 0;
+
+	r->status = ~0;
+// cgascreenputs("E", 1);
+	switch(scsirio(r)){
+	default:
+		rlen = -1;
+		break;
+	case 0:
+		rlen = r->rlen;
+		break;
+	case 2:
+		rlen = -1;
+		if(!(r->flags & SDvalidsense))
+			break;
+		switch(r->sense[2] & 0x0F){
+		default:
+			break;
+		case 0x06:		/* check condition */
+			/*
+			 * Check for a removeable media change.
+			 * If so, mark it and zap the geometry info
+			 * to force an online request.
+			 */
+			if(r->sense[12] != 0x28 || r->sense[13] != 0)
+				break;
+			if(unit->inquiry[1] & 0x80){
+				unit->sectors = 0;
+			}
+			break;
+		case 0x02:		/* not ready */
+			/*
+			 * If unit is becoming ready,
+			 * rather than not not ready, try again.
+			 */
+			if(r->sense[12] == 0x04 && r->sense[13] == 0x01)
+				goto again;
+			break;
+		}
+		break;
+	}
+
+	return rlen;
+}
+
+SDev*
+scsiid(SDev* sdev, SDifc* ifc)
+{
+	static char idno[16] = "0123456789abcdef";
+	static char *p = idno;
+
+	while(sdev){
+		if(sdev->ifc == ifc){
+			sdev->idno = *p++;
+			if(p >= &idno[sizeof(idno)])
+				break;
+		}
+		sdev = sdev->next;
+	}
+
+	return nil;
+}

+ 3 - 3
sys/src/fs/pc/toy.c

@@ -23,7 +23,7 @@ enum {
 static Lock rtclock;
 
 void
-setrtc(ulong secs)
+setrtc(Timet secs)
 {
 	Rtc rtc;
 	uchar bcdclock[Nbcd];
@@ -93,11 +93,11 @@ _rtctime(void)
 	return rtc2sec(&rtc);
 }
 
-ulong
+Timet
 rtctime(void)
 {
 	int i;
-	ulong t, ot;
+	Timet t, ot;
 
 	ilock(&rtclock);
 

+ 82 - 107
sys/src/fs/port/9p1.c

@@ -113,7 +113,7 @@ authorize(Chan *cp, Fcall *in, Fcall *ou)
 
 	/*
 	 *  the id must be in a valid range.  the range is specified by a
-	 *  lower bount (idoffset) and a bit vector (idvec) where a
+	 *  lower bound (idoffset) and a bit vector (idvec) where a
 	 *  bit set to 1 means unusable
 	 */
 	lock(&aip->idlock);
@@ -196,7 +196,7 @@ f_attach(Chan *cp, Fcall *in, Fcall *ou)
 	File *f;
 	int u;
 	Filsys *fs;
-	long raddr;
+	Off raddr;
 
 	if(CHAT(cp)) {
 		print("c_attach %d\n", cp->chan);
@@ -246,11 +246,8 @@ f_attach(Chan *cp, Fcall *in, Fcall *ou)
 		ou->err = Ealloc;
 		goto out;
 	}
-	if(iaccess(f, d, DEXEC)) {
-		ou->err = Eaccess;
-		goto out;
-	}
-	if(f->uid == 0 && fs->dev->type == Devro) {
+	if (iaccess(f, d, DEXEC) ||
+	    f->uid == 0 && fs->dev->type == Devro) {
 		/*
 		 * 'none' not allowed on dump
 		 */
@@ -352,7 +349,7 @@ f_walk(Chan *cp, Fcall *in, Fcall *ou)
 	File *f;
 	Wpath *w;
 	int slot;
-	long addr, qpath;
+	Off addr, qpath;
 
 	if(CHAT(cp)) {
 		print("c_walk %d\n", cp->chan);
@@ -430,7 +427,7 @@ f_walk(Chan *cp, Fcall *in, Fcall *ou)
 			d1 = getdir(p1, slot);
 			if(!(d1->mode & DALLOC))
 				continue;
-			if(strncmp(in->name, d1->name, sizeof(in->name)))
+			if(strncmp(in->name, d1->name, sizeof(in->name)) != 0)
 				continue;
 			/*
 			 * update walk path
@@ -625,7 +622,7 @@ f_create(Chan *cp, Fcall *in, Fcall *ou)
 	Dentry *d, *d1;
 	File *f;
 	int slot, slot1, fmod, wok;
-	long addr, addr1, path;
+	Off addr, addr1, path;
 	Qid qid;
 	Tlock *t;
 	Wpath *w;
@@ -829,13 +826,14 @@ f_read(Chan *cp, Fcall *in, Fcall *ou)
 	File *f;
 	Dentry *d, *d1;
 	Tlock *t;
-	long addr, offset, tim;
+	Off addr, offset;
+	Timet tim;
 	int nread, count, n, o, slot;
 
 	if(CHAT(cp)) {
 		print("c_read %d\n", cp->chan);
 		print("	fid = %d\n", in->fid);
-		print("	offset = %ld\n", in->offset);
+		print("	offset = %lld\n", (Wideoff)in->offset);
 		print("	count = %ld\n", in->count);
 	}
 
@@ -886,7 +884,7 @@ f_read(Chan *cp, Fcall *in, Fcall *ou)
 	/* XXXX terrible hack to get at raw data XXXX */
 	if(rawreadok && strncmp(d->name, "--raw--", 7) == 0) {
 		Device *dev;
-		long boff, bsize;
+		Devsize boff, bsize;
 
 		dev = p->dev;
 		putbuf(p);
@@ -901,7 +899,7 @@ f_read(Chan *cp, Fcall *in, Fcall *ou)
 
 		if(offset+count >= 100000*RBUFSIZE)
 			count = 100000*RBUFSIZE - offset;
-		
+
 		if((offset+count)/RBUFSIZE >= bsize) {
 			/* will not overflow */
 			count = bsize*RBUFSIZE - offset;
@@ -910,11 +908,11 @@ f_read(Chan *cp, Fcall *in, Fcall *ou)
 		while(count > 0) {
 			addr = offset / RBUFSIZE;
 			addr += boff;
-			o = offset % RBUFSIZE;	
+			o = offset % RBUFSIZE;
 			n = RBUFSIZE - o;
 			if(n > count)
 				n = count;
-			
+
 			p1 = getbuf(dev, addr, Bread);
 			if(p1) {
 				memmove(ou->data+nread, p1->iobuf+o, n);
@@ -964,45 +962,45 @@ f_read(Chan *cp, Fcall *in, Fcall *ou)
 	goto out;
 
 dread:
-	if(p == 0) {
-		p = getbuf(f->fs->dev, f->addr, Bread);
-		d = getdir(p, f->slot);
-		if(!d || !(d->mode & DALLOC)) {
-			ou->err = Ealloc;
-			goto out;
-		}
-	}
-	p1 = dnodebuf1(p, d, addr, 0, f->uid);
-	p = 0;
-	if(!p1)
-		goto out;
-	if(checktag(p1, Tdir, QPNONE)) {
-		ou->err = Ephase;
-		putbuf(p1);
-		goto out;
-	}
-	n = DIRREC;
-	for(slot=0; slot<DIRPERBUF; slot++) {
-		d1 = getdir(p1, slot);
-		if(!(d1->mode & DALLOC))
-			continue;
-		if(offset >= n) {
-			offset -= n;
-			continue;
+	for (;;) {
+		if(p == 0) {
+			p = getbuf(f->fs->dev, f->addr, Bread);
+			d = getdir(p, f->slot);
+			if(!d || !(d->mode & DALLOC)) {
+				ou->err = Ealloc;
+				goto out;
+			}
 		}
-		if(count < n) {
+		p1 = dnodebuf1(p, d, addr, 0, f->uid);
+		p = 0;
+		if(!p1)
+			goto out;
+		if(checktag(p1, Tdir, QPNONE)) {
+			ou->err = Ephase;
 			putbuf(p1);
 			goto out;
 		}
-		if(convD2M9p1(d1, ou->data+nread) != n)
-			print("9p1: dirread convD2M1990\n");
-		nread += n;
-		count -= n;
+		n = DIRREC;
+		for(slot=0; slot<DIRPERBUF; slot++) {
+			d1 = getdir(p1, slot);
+			if(!(d1->mode & DALLOC))
+				continue;
+			if(offset >= n) {
+				offset -= n;
+				continue;
+			}
+			if(count < n) {
+				putbuf(p1);
+				goto out;
+			}
+			if(convD2M9p1(d1, ou->data+nread) != n)
+				print("9p1: dirread convD2M1990\n");
+			nread += n;
+			count -= n;
+		}
+		putbuf(p1);
+		addr++;
 	}
-	putbuf(p1);
-	addr++;
-	goto dread;
-
 out:
 	count = in->count - nread;
 	if(count > 0)
@@ -1024,13 +1022,14 @@ f_write(Chan *cp, Fcall *in, Fcall *ou)
 	Dentry *d;
 	File *f;
 	Tlock *t;
-	long offset, addr, tim, qpath;
+	Off offset, addr, qpath;
+	Timet tim;
 	int count, nwrite, o, n;
 
 	if(CHAT(cp)) {
 		print("c_write %d\n", cp->chan);
 		print("	fid = %d\n", in->fid);
-		print("	offset = %ld\n", in->offset);
+		print("	offset = %lld\n", (Wideoff)in->offset);
 		print("	count = %ld\n", in->count);
 	}
 
@@ -1131,7 +1130,7 @@ doremove(File *f, int wok)
 {
 	Iobuf *p, *p1;
 	Dentry *d, *d1;
-	long addr;
+	Off addr;
 	int slot, err;
 
 	p = 0;
@@ -1242,15 +1241,12 @@ f_clunk(Chan *cp, Fcall *in, Fcall *ou)
 	}
 
 	f = filep(cp, in->fid, 0);
-	if(!f) {
+	if(!f)
 		ou->err = Efid;
-		goto out;
-	}
-	doclunk(f, f->open & FREMOV, 0);
-
-out:
-	if(f)
+	else {
+		doclunk(f, f->open & FREMOV, 0);
 		qunlock(f);
+	}
 	ou->fid = in->fid;
 }
 
@@ -1265,15 +1261,12 @@ f_remove(Chan *cp, Fcall *in, Fcall *ou)
 	}
 
 	f = filep(cp, in->fid, 0);
-	if(!f) {
+	if(!f)
 		ou->err = Efid;
-		goto out;
-	}
-	ou->err = doclunk(f, 1, cp==cons.chan);
-
-out:
-	if(f)
+	else {
+		ou->err = doclunk(f, 1, cp==cons.chan);
 		qunlock(f);
+	}
 	ou->fid = in->fid;
 }
 
@@ -1324,7 +1317,7 @@ f_wstat(Chan *cp, Fcall *in, Fcall *ou)
 	Dentry *d, *d1, xd;
 	File *f;
 	int slot;
-	long addr;
+	Off addr;
 
 	if(CHAT(cp)) {
 		print("c_wstat %d\n", cp->chan);
@@ -1386,9 +1379,7 @@ f_wstat(Chan *cp, Fcall *in, Fcall *ou)
 	 * if chown,
 	 * must be god
 	 */
-	while(xd.uid != d->uid) {
-		if(wstatallow)			/* set to allow chown during boot */
-			break;
+	if(xd.uid != d->uid && !wstatallow) { /* set to allow chown during boot */
 		ou->err = Ewstatu;
 		goto out;
 	}
@@ -1399,14 +1390,10 @@ f_wstat(Chan *cp, Fcall *in, Fcall *ou)
 	 *	a) owner and in new group
 	 *	b) leader of both groups
 	 */
-	while(xd.gid != d->gid) {
-		if(wstatallow || writeallow)		/* set to allow chgrp during boot */
-			break;
-		if(d->uid == f->uid && ingroup(f->uid, xd.gid))
-			break;
-		if(leadgroup(f->uid, xd.gid))
-			if(leadgroup(f->uid, d->gid))
-				break;
+	if (xd.gid != d->gid &&
+	    (!wstatallow && !writeallow &&  /* set to allow chgrp during boot */
+	     (d->uid != f->uid || !ingroup(f->uid, xd.gid)) &&
+	     (!leadgroup(f->uid, xd.gid) || !leadgroup(f->uid, d->gid)))) {
 		ou->err = Ewstatg;
 		goto out;
 	}
@@ -1415,12 +1402,9 @@ f_wstat(Chan *cp, Fcall *in, Fcall *ou)
 	 * if rename,
 	 * must have write permission in parent
 	 */
-	while(strncmp(d->name, xd.name, sizeof(d->name)) != 0) {
-		if(checkname(xd.name) || !d1) {
-			ou->err = Ename;
-			goto out;
-		}
-		if(strcmp(xd.name, ".") == 0 || strcmp(xd.name, "..") == 0) {
+	if (strncmp(d->name, xd.name, sizeof(d->name)) != 0) {
+		if (checkname(xd.name) || !d1 ||
+		    strcmp(xd.name, ".") == 0 || strcmp(xd.name, "..") == 0) {
 			ou->err = Ename;
 			goto out;
 		}
@@ -1460,13 +1444,11 @@ f_wstat(Chan *cp, Fcall *in, Fcall *ou)
 			goto out;
 		}
 
-		if(wstatallow || writeallow) /* set to allow rename during boot */
-			break;
-		if(!d1 || iaccess(f, d1, DWRITE)) {
+		if (!wstatallow && !writeallow && /* set to allow rename during boot */
+		    (!d1 || iaccess(f, d1, DWRITE))) {
 			ou->err = Eaccess;
 			goto out;
 		}
-		break;
 	}
 
 	/*
@@ -1474,19 +1456,15 @@ f_wstat(Chan *cp, Fcall *in, Fcall *ou)
 	 *	a) owner
 	 *	b) leader of either group
 	 */
-	while(d->mtime != xd.mtime ||
-	     ((d->mode^xd.mode) & (DAPND|DLOCK|0777))) {
-		if(wstatallow)			/* set to allow chmod during boot */
-			break;
-		if(d->uid == f->uid)
-			break;
-		if(leadgroup(f->uid, xd.gid))
-			break;
-		if(leadgroup(f->uid, d->gid))
-			break;
-		ou->err = Ewstatu;
-		goto out;
-	}
+	if (d->mtime != xd.mtime ||
+	    ((d->mode^xd.mode) & (DAPND|DLOCK|0777)))
+		if (!wstatallow &&	/* set to allow chmod during boot */
+		    d->uid != f->uid &&
+		    !leadgroup(f->uid, xd.gid) &&
+		    !leadgroup(f->uid, d->gid)) {
+			ou->err = Ewstatu;
+			goto out;
+		}
 	d->mtime = xd.mtime;
 	d->uid = xd.uid;
 	d->gid = xd.gid;
@@ -1529,11 +1507,9 @@ f_clwalk(Chan *cp, Fcall *in, Fcall *ou)
 		f_clunk(cp, in, ou);	/* sets tag, fid */
 		ou->err = 0;
 		ou->fid = fid;
-		if(CHAT(cp)) 
+		if(CHAT(cp))
 			print("	error: %s\n", errstr9p[er]);
-		return;
-	}
-	if(er) {
+	} else if(er) {
 		/*
 		 * if any other error
 		 * return an error
@@ -1541,7 +1517,6 @@ f_clwalk(Chan *cp, Fcall *in, Fcall *ou)
 		ou->err = 0;
 		f_clunk(cp, in, ou);	/* sets tag, fid */
 		ou->err = er;
-		return;
 	}
 	/*
 	 * non error

+ 2 - 2
sys/src/fs/port/9p1.h

@@ -15,7 +15,7 @@ struct	Fcall
 	{
 		struct
 		{
-			short	uid;		/* T-Userstr */
+			short	uid;		/* T-Userstr [obs.] */
 			short	oldtag;		/* T-nFlush */
 			Qid9p1	qid;		/* R-Attach, R-Clwalk, R-Walk,
 						 * R-Open, R-Create */
@@ -44,7 +44,7 @@ struct	Fcall
 		};
 		struct
 		{
-			long	offset;		/* T-Read, T-Write */
+			Off	offset;		/* T-Read, T-Write */
 			long	count;		/* T-Read, T-Write, R-Read */
 			char*	data;		/* T-Write, R-Read */
 		};

+ 27 - 15
sys/src/fs/port/9p1lib.c

@@ -7,9 +7,15 @@ int kernel9p = 2;
 #include "9p1.h"
 
 #define	CHAR(x)		*p++ = f->x
-#define	SHORT(x)	{ ulong vvv = f->x; p[0] = vvv; p[1] = vvv>>8; p += 2; }
-#define	VLONG(q)	p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4
-#define	LONG(x)		{ ulong vvv = f->x; VLONG(vvv); }
+#define	SHORT(x)	{ ulong vvv = f->x; *p++ = vvv; *p++ = vvv>>8; }
+#define	LONGINT(q) {*p++ = (q); *p++ = (q)>>8; *p++ = (q)>>16; *p++ = (q)>>24;}
+#define	LONG(x)		{ ulong vvv = f->x; LONGINT(vvv); }
+#define	VLONG(x) { \
+	uvlong q = f->x; \
+	*p++ = (q)>> 0; *p++ = (q)>> 8; *p++ = (q)>>16; *p++ = (q)>>24; \
+	*p++ = (q)>>32; *p++ = (q)>>40; *p++ = (q)>>48; *p++ = (q)>>56; \
+	}
+
 #define	BYTES(x,n)	memmove(p, f->x, n); p += n
 #define	STRING(x,n)	strncpy((char*)p, f->x, n); p += n
 
@@ -86,13 +92,13 @@ convS2M9p1(Fcall *f, uchar *ap)
 
 	case Tread:
 		SHORT(fid);
-		LONG(offset); VLONG(0);
+		VLONG(offset);
 		SHORT(count);
 		break;
 
 	case Twrite:
 		SHORT(fid);
-		LONG(offset); VLONG(0);
+		VLONG(offset);
 		SHORT(count);
 		p++;
 		if((uchar*)p == (uchar*)f->data) {
@@ -222,7 +228,7 @@ convD2M9p1(Dentry *f, char *ap)
 	p += NAMELEN;
 
 	q = fakeqid9p1(f);
-	VLONG(q);
+	LONGINT(q);
 	LONG(qid.version);
 	{
 		q = f->mode & 0x0fff;
@@ -232,12 +238,12 @@ convD2M9p1(Dentry *f, char *ap)
 			q |= PAPND;
 		if(f->mode & DLOCK)
 			q |= PLOCK;
-		VLONG(q);
+		LONGINT(q);
 	}
 	LONG(atime);
 	LONG(mtime);
-	LONG(size); VLONG(0);
-	VLONG(0);
+	VLONG(size);
+	LONGINT(0);
 	return p - (uchar*)ap;
 }
 
@@ -260,14 +266,20 @@ convA2M9p1(Authenticator *f, char *ap, char *key)
 #undef	CHAR
 #undef	SHORT
 #undef	LONG
+#undef	LONGINT
 #undef	VLONG
 #undef	BYTES
 #undef	STRING
 
 #define	CHAR(x)		f->x = *p++
 #define	SHORT(x)	f->x = (p[0] | (p[1]<<8)); p += 2
-#define	VLONG(q)	q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4
-#define	LONG(x)		VLONG(f->x)
+#define	LONG(x)	f->x = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); p += 4
+#define	VLONG(x) { \
+	f->x =	    (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)) | \
+	    (uvlong)(p[4] | (p[5]<<8) | (p[6]<<16) | (p[7]<<24)) << 32; \
+	p += 8; \
+}
+
 #define	BYTES(x,n)	memmove(f->x, p, n); p += n
 #define	STRING(x,n)	memmove(f->x, p, n); p += n
 
@@ -352,13 +364,13 @@ convM2S9p1(uchar *ap, Fcall *f, int n)
 
 	case Tread:
 		SHORT(fid);
-		LONG(offset); p += 4;
+		VLONG(offset);
 		SHORT(count);
 		break;
 
 	case Twrite:
 		SHORT(fid);
-		LONG(offset); p += 4;
+		VLONG(offset);
 		SHORT(count);
 		p++;
 		f->data = (char*)p; p += f->count;
@@ -448,7 +460,7 @@ int
 convM2D9p1(char *ap, Dentry *f)
 {
 	uchar *p;
-	char str[28];
+	char str[NAMELEN];
 
 	p = (uchar*)ap;
 	BYTES(name, sizeof(f->name));
@@ -475,7 +487,7 @@ convM2D9p1(char *ap, Dentry *f)
 	}
 	LONG(atime);
 	LONG(mtime);
-	LONG(size); p += 4;
+	VLONG(size);
 	p += 4;
 	return p - (uchar*)ap;
 }

Some files were not shown because too many files changed in this diff