diff --git a/bin/ehcmodule_5.c b/bin/ehcmodule_5.c new file mode 100644 index 00000000..83210493 --- /dev/null +++ b/bin/ehcmodule_5.c @@ -0,0 +1,944 @@ +#define size_ehcmodule_5 26234 + +unsigned char ehcmodule_5[26234] __attribute__((aligned (32)))={ + 127, 69, 76, 70, 1, 2, 1, 97, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 40, 0, 0, 0, 1, 19, 112, 0, 0, 0, 0, 0, 52, 0, 0, 0, + 0, 0, 0, 6, 6, 0, 52, 0, 32, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, + 0, 0, 0, 160, 0, 240, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 212, 0, 0, 0, 160, 0, 0, 0, 160, 0, 0, 0, 52, 0, 0, 0, + 52, 0, 240, 0, 0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, 0, 0, 0, 212, 0, 240, 0, + 0, 0, 0, 64, 0, 0, 0, 0, 1, 0, 0, 1, 8, 19, 112, 0, 0, 19, 112, 0, 0, 0, 0, 90, 208, 0, 0, 90, 208, 0, 240, 0, 5, 0, + 0, 0, 4, 0, 0, 0, 1, 0, 0, 91, 216, 19, 112, 96, 0, 19, 112, 96, 0, 0, 0, 10, 162, 0, 2, 153, 40, 0, 240, 0, 6, 0, 0, + 0, 4, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 6, 0, 0, 0, 11, 0, 0, 0, 4, 0, 0, 0, 9, 19, 112, 0, 0, 0, 0, 0, 125, 0, 0, 0, + 120, 0, 0, 0, 126, 0, 0, 48, 0, 0, 0, 0, 127, 19, 114, 247, 40, 227, 160, 0, 0, 227, 160, 16, 0, 229, 159, 49, 0, 225, + 47, 255, 19, 229, 159, 192, 252, 229, 156, 192, 0, 225, 160, 0, 0, 231, 156, 193, 11, 225, 160, 0, 0, 225, 47, 255, + 28, 225, 160, 0, 0, 225, 160, 0, 0, 227, 160, 176, 63, 234, 255, 255, 245, 225, 160, 0, 0, 225, 160, 0, 0, 227, 160, + 176, 64, 234, 255, 255, 241, 227, 160, 0, 0, 238, 7, 15, 21, 225, 47, 255, 30, 225, 160, 0, 0, 225, 160, 0, 0, 225, + 160, 0, 0, 227, 24, 0, 16, 10, 0, 0, 18, 227, 200, 128, 16, 227, 160, 32, 16, 229, 135, 32, 0, 225, 160, 0, 0, 225, + 160, 32, 13, 225, 160, 0, 0, 229, 159, 208, 144, 225, 160, 0, 0, 233, 45, 95, 254, 225, 160, 0, 0, 235, 0, 0, 15, 232, + 189, 95, 254, 225, 160, 0, 0, 225, 160, 208, 2, 227, 16, 0, 1, 10, 0, 0, 2, 225, 160, 0, 0, 227, 160, 0, 4, 235, 0, + 0, 5, 227, 24, 0, 1, 10, 0, 0, 1, 229, 159, 240, 88, 225, 160, 0, 0, 229, 159, 240, 84, 225, 160, 0, 0, 229, 159, 240, + 80, 225, 160, 0, 0, 229, 159, 32, 76, 225, 47, 255, 18, 225, 160, 0, 0, 238, 19, 15, 16, 225, 47, 255, 30, 225, 160, + 0, 0, 225, 160, 0, 0, 238, 3, 15, 16, 225, 47, 255, 30, 225, 160, 0, 0, 225, 160, 0, 0, 69, 72, 67, 95, 67, 70, 71, + 0, 18, 52, 0, 1, 0, 0, 0, 0, 19, 112, 16, 157, 19, 114, 195, 128, 19, 114, 249, 40, 255, 255, 30, 128, 255, 255, 30, + 156, 255, 255, 29, 68, 19, 112, 13, 61, 225, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 0, 40, + 2, 208, 16, 40, 2, 216, 4, 40, 0, 208, 7, 40, 1, 209, 19, 224, 7, 40, 16, 208, 12, 40, 17, 209, 14, 224, 11, 75, 8, + 104, 24, 224, 11, 72, 7, 224, 9, 75, 7, 34, 1, 96, 26, 32, 0, 224, 4, 72, 6, 224, 2, 72, 6, 224, 0, 72, 6, 188, 2, 71, + 8, 19, 112, 96, 48, 19, 112, 73, 29, 19, 112, 106, 200, 19, 112, 66, 25, 19, 112, 67, 97, 255, 255, 253, 102, 181, 112, + 28, 12, 28, 5, 240, 5, 252, 105, 28, 6, 32, 1, 66, 64, 240, 5, 252, 88, 14, 43, 115, 35, 12, 43, 115, 99, 10, 43, 115, + 163, 124, 34, 124, 99, 6, 18, 4, 27, 67, 19, 124, 162, 115, 229, 2, 18, 67, 19, 124, 226, 6, 45, 67, 19, 28, 32, 96, + 29, 48, 12, 33, 4, 240, 5, 252, 88, 124, 35, 124, 96, 6, 27, 4, 0, 67, 24, 124, 163, 33, 4, 2, 27, 67, 24, 124, 227, + 67, 24, 240, 5, 252, 75, 28, 48, 240, 5, 252, 48, 32, 0, 188, 112, 188, 2, 71, 8, 181, 8, 75, 12, 28, 1, 34, 32, 104, + 24, 240, 5, 252, 81, 40, 0, 209, 13, 72, 9, 240, 5, 252, 40, 240, 4, 252, 99, 32, 200, 240, 0, 255, 33, 240, 4, 252, + 104, 32, 200, 240, 0, 255, 28, 231, 244, 188, 8, 188, 2, 71, 8, 19, 112, 96, 48, 19, 112, 96, 84, 181, 8, 75, 4, 28, + 1, 104, 24, 240, 5, 251, 252, 188, 8, 188, 1, 71, 0, 70, 192, 19, 112, 96, 48, 181, 240, 176, 139, 144, 4, 145, 5, 28, + 8, 28, 17, 28, 23, 146, 7, 240, 5, 252, 8, 76, 129, 104, 35, 43, 0, 209, 0, 224, 241, 168, 9, 240, 3, 254, 8, 75, 126, + 96, 24, 40, 0, 208, 4, 35, 128, 154, 9, 1, 27, 66, 154, 208, 4, 35, 1, 74, 122, 66, 91, 96, 19, 224, 224, 78, 121, 72, + 122, 104, 51, 24, 26, 42, 0, 208, 104, 43, 0, 219, 102, 75, 116, 104, 26, 28, 81, 209, 98, 28, 53, 75, 112, 104, 48, + 104, 28, 33, 31, 34, 16, 52, 31, 24, 18, 67, 140, 146, 2, 33, 16, 28, 34, 240, 3, 255, 166, 40, 0, 209, 0, 224, 195, + 120, 35, 43, 67, 209, 11, 120, 99, 43, 73, 209, 8, 120, 163, 43, 83, 209, 5, 120, 227, 43, 79, 209, 2, 75, 103, 96, + 51, 224, 8, 104, 43, 43, 0, 208, 2, 35, 0, 96, 43, 231, 218, 35, 1, 66, 91, 96, 43, 121, 98, 121, 163, 2, 18, 4, 27, + 24, 211, 121, 34, 77, 95, 24, 155, 121, 226, 28, 40, 6, 18, 24, 155, 10, 219, 147, 3, 154, 3, 75, 91, 33, 0, 96, 26, + 34, 128, 1, 18, 240, 5, 250, 143, 75, 84, 104, 26, 75, 85, 66, 154, 209, 30, 32, 7, 35, 0, 70, 132, 70, 102, 64, 30, + 209, 4, 16, 218, 152, 2, 73, 82, 0, 146, 80, 80, 24, 226, 122, 18, 42, 0, 208, 10, 16, 218, 92, 168, 33, 1, 64, 177, + 67, 8, 84, 168, 154, 2, 28, 16, 154, 3, 24, 128, 144, 2, 34, 128, 51, 1, 1, 210, 66, 147, 209, 227, 75, 63, 34, 31, + 104, 27, 153, 5, 51, 31, 67, 147, 34, 128, 2, 18, 147, 3, 145, 2, 78, 60, 146, 6, 224, 97, 155, 4, 32, 15, 10, 93, 104, + 51, 67, 133, 66, 157, 208, 63, 73, 56, 75, 57, 104, 10, 96, 53, 66, 154, 209, 33, 75, 57, 28, 40, 104, 28, 28, 33, 240, + 5, 251, 33, 75, 55, 8, 193, 0, 138, 88, 210, 35, 0, 147, 1, 35, 7, 64, 24, 70, 140, 35, 0, 144, 0, 224, 9, 72, 47, 70, + 97, 92, 64, 28, 1, 65, 25, 32, 1, 66, 8, 208, 0, 25, 18, 51, 1, 153, 0, 66, 139, 211, 242, 60, 1, 64, 44, 25, 21, 75, + 34, 104, 28, 27, 100, 44, 16, 220, 10, 44, 16, 208, 9, 34, 128, 152, 3, 33, 0, 2, 18, 240, 5, 250, 34, 44, 0, 221, 8, + 224, 0, 36, 16, 28, 40, 28, 33, 154, 3, 240, 3, 254, 255, 40, 0, 208, 29, 154, 4, 152, 6, 4, 209, 12, 73, 26, 67, 28, + 60, 66, 187, 216, 0, 28, 28, 154, 3, 152, 2, 24, 137, 28, 34, 240, 5, 249, 198, 152, 2, 28, 33, 240, 5, 251, 28, 155, + 2, 152, 4, 25, 27, 27, 63, 16, 164, 25, 0, 147, 2, 144, 4, 47, 0, 209, 155, 224, 5, 32, 128, 2, 0, 176, 11, 188, 240, + 188, 2, 71, 8, 152, 5, 153, 7, 240, 5, 251, 39, 32, 0, 231, 245, 70, 192, 19, 112, 107, 88, 19, 112, 106, 208, 19, 112, + 96, 24, 19, 112, 96, 12, 128, 0, 0, 1, 127, 255, 255, 255, 19, 112, 123, 128, 19, 112, 106, 212, 19, 112, 131, 128, + 181, 56, 28, 12, 240, 5, 250, 214, 30, 5, 209, 38, 44, 0, 208, 36, 104, 34, 42, 0, 208, 33, 120, 17, 120, 83, 6, 9, + 4, 27, 67, 11, 120, 145, 2, 9, 67, 11, 120, 209, 67, 11, 43, 6, 209, 21, 123, 17, 123, 83, 6, 9, 4, 27, 67, 11, 123, + 145, 2, 9, 67, 11, 123, 209, 67, 11, 43, 122, 208, 2, 43, 136, 209, 7, 224, 2, 72, 5, 33, 21, 224, 1, 72, 4, 33, 20, + 240, 4, 250, 238, 28, 40, 188, 56, 188, 2, 71, 8, 70, 192, 19, 112, 1, 153, 181, 240, 176, 145, 75, 206, 33, 128, 104, + 24, 240, 5, 250, 97, 33, 32, 240, 5, 250, 144, 144, 8, 240, 0, 251, 131, 240, 5, 250, 211, 33, 120, 240, 5, 250, 140, + 153, 8, 72, 199, 240, 5, 250, 120, 72, 198, 154, 8, 28, 1, 35, 0, 240, 5, 250, 72, 73, 196, 144, 9, 32, 205, 240, 4, + 250, 109, 33, 0, 36, 1, 145, 7, 145, 11, 145, 6, 39, 0, 152, 8, 169, 15, 34, 0, 240, 5, 250, 122, 144, 14, 155, 14, + 43, 0, 209, 246, 44, 0, 208, 2, 152, 9, 240, 5, 250, 67, 74, 185, 158, 15, 35, 0, 96, 19, 46, 0, 209, 87, 75, 183, 104, + 27, 43, 0, 208, 5, 75, 182, 104, 27, 43, 0, 209, 1, 74, 181, 96, 19, 36, 0, 47, 0, 208, 222, 75, 178, 104, 27, 43, 0, + 208, 218, 75, 175, 104, 27, 43, 0, 209, 214, 75, 175, 104, 27, 43, 0, 208, 9, 240, 3, 253, 88, 40, 0, 208, 5, 240, 3, + 253, 84, 40, 0, 208, 1, 240, 3, 253, 80, 75, 168, 104, 27, 147, 4, 43, 0, 209, 36, 77, 167, 75, 167, 104, 42, 58, 1, + 66, 154, 216, 30, 78, 159, 76, 165, 35, 1, 96, 51, 104, 32, 33, 1, 74, 164, 240, 3, 254, 26, 155, 4, 96, 51, 40, 0, + 208, 9, 35, 128, 104, 42, 0, 155, 66, 154, 209, 4, 104, 35, 33, 128, 2, 9, 24, 91, 96, 35, 75, 154, 74, 156, 104, 25, + 104, 18, 66, 145, 211, 1, 34, 0, 96, 26, 75, 145, 36, 0, 104, 27, 43, 0, 209, 153, 152, 9, 73, 138, 240, 5, 249, 219, + 36, 1, 231, 147, 120, 51, 120, 112, 6, 27, 4, 0, 67, 24, 120, 179, 2, 27, 67, 24, 120, 243, 67, 24, 56, 1, 40, 6, 217, + 0, 226, 152, 240, 4, 255, 69, 0, 10, 0, 73, 2, 151, 2, 151, 2, 151, 0, 7, 0, 92, 38, 1, 37, 1, 226, 150, 123, 51, 123, + 116, 6, 27, 4, 36, 67, 28, 123, 179, 73, 119, 2, 27, 67, 28, 123, 243, 67, 28, 28, 32, 240, 5, 249, 53, 40, 0, 209, + 11, 125, 51, 125, 117, 6, 27, 4, 45, 67, 29, 125, 179, 2, 27, 67, 29, 125, 243, 38, 1, 67, 29, 226, 122, 28, 32, 73, + 120, 240, 5, 249, 35, 40, 0, 208, 0, 226, 108, 125, 51, 125, 117, 6, 27, 4, 45, 67, 29, 125, 179, 34, 1, 2, 27, 67, + 29, 125, 243, 39, 0, 67, 29, 75, 103, 38, 1, 96, 26, 75, 103, 96, 24, 72, 110, 240, 0, 251, 48, 75, 109, 34, 4, 104, + 27, 104, 155, 96, 154, 240, 1, 248, 39, 226, 87, 122, 50, 122, 115, 6, 18, 4, 27, 67, 19, 122, 178, 2, 18, 67, 19, 122, + 242, 67, 19, 154, 6, 66, 154, 209, 0, 226, 70, 240, 1, 248, 46, 38, 1, 37, 0, 226, 68, 126, 51, 126, 116, 6, 27, 4, + 36, 67, 28, 126, 179, 124, 50, 2, 27, 67, 28, 126, 243, 6, 18, 67, 28, 124, 115, 4, 27, 67, 19, 124, 178, 2, 18, 67, + 19, 124, 242, 67, 26, 146, 4, 125, 51, 125, 117, 6, 27, 4, 45, 67, 29, 125, 179, 2, 27, 67, 29, 125, 243, 67, 29, 155, + 6, 43, 0, 209, 16, 123, 51, 43, 0, 209, 13, 122, 50, 122, 115, 122, 176, 6, 18, 4, 27, 67, 19, 2, 0, 67, 3, 122, 240, + 67, 24, 240, 1, 248, 10, 28, 6, 224, 0, 38, 0, 153, 4, 28, 32, 25, 73, 145, 10, 0, 201, 240, 5, 249, 164, 34, 0, 28, + 37, 146, 5, 224, 7, 104, 40, 104, 105, 240, 5, 249, 156, 155, 5, 53, 8, 51, 1, 147, 5, 153, 5, 154, 10, 66, 145, 219, + 243, 73, 48, 104, 11, 147, 5, 43, 0, 208, 0, 225, 213, 154, 15, 123, 16, 123, 83, 6, 0, 4, 27, 67, 3, 123, 144, 2, 0, + 67, 3, 123, 208, 67, 3, 72, 51, 66, 131, 209, 0, 225, 38, 66, 131, 216, 31, 43, 6, 216, 11, 43, 5, 211, 0, 225, 180, + 43, 1, 209, 0, 224, 159, 43, 0, 208, 119, 43, 2, 208, 0, 225, 70, 225, 171, 43, 27, 216, 6, 43, 26, 211, 0, 224, 172, + 43, 12, 208, 0, 225, 61, 224, 156, 74, 37, 66, 147, 209, 0, 224, 167, 50, 1, 66, 147, 208, 0, 225, 52, 224, 246, 72, + 34, 66, 131, 209, 0, 224, 226, 66, 131, 216, 68, 74, 32, 66, 147, 209, 0, 224, 182, 66, 147, 216, 8, 58, 12, 66, 147, + 209, 0, 225, 17, 50, 2, 66, 147, 208, 0, 225, 31, 225, 20, 74, 25, 66, 147, 209, 0, 225, 22, 50, 1, 66, 147, 208, 0, + 225, 22, 224, 169, 70, 192, 19, 112, 96, 48, 19, 112, 96, 109, 0, 152, 150, 128, 19, 112, 1, 65, 19, 112, 106, 240, + 19, 112, 106, 196, 19, 112, 106, 200, 19, 112, 96, 20, 19, 112, 107, 84, 19, 114, 199, 32, 0, 0, 15, 254, 19, 112, 106, + 192, 19, 112, 107, 128, 19, 114, 199, 36, 19, 112, 96, 121, 19, 112, 21, 69, 19, 112, 96, 60, 85, 77, 83, 3, 85, 77, + 83, 1, 85, 77, 83, 130, 85, 77, 83, 16, 85, 77, 83, 128, 73, 196, 66, 139, 209, 0, 225, 63, 66, 139, 216, 7, 73, 194, + 66, 139, 208, 119, 73, 194, 66, 139, 208, 0, 224, 219, 224, 220, 74, 192, 66, 147, 209, 0, 225, 36, 50, 1, 66, 147, + 208, 0, 224, 210, 225, 20, 46, 0, 209, 0, 225, 55, 105, 162, 105, 35, 136, 21, 106, 34, 136, 27, 136, 16, 104, 34, 2, + 25, 10, 27, 120, 18, 67, 11, 2, 41, 10, 45, 67, 13, 146, 5, 4, 45, 104, 162, 12, 45, 120, 18, 149, 0, 2, 5, 10, 0, 67, + 40, 4, 0, 12, 0, 144, 1, 107, 32, 4, 27, 144, 2, 12, 27, 28, 48, 153, 5, 240, 1, 251, 44, 225, 3, 46, 0, 209, 0, 225, + 17, 104, 35, 28, 48, 120, 25, 104, 163, 136, 26, 105, 35, 240, 1, 251, 12, 224, 247, 46, 0, 208, 0, 225, 5, 104, 35, + 105, 34, 120, 24, 104, 163, 120, 25, 105, 163, 240, 0, 255, 46, 224, 235, 38, 0, 37, 1, 225, 4, 154, 11, 42, 0, 209, + 1, 240, 0, 254, 203, 240, 3, 249, 119, 39, 0, 28, 5, 40, 0, 219, 3, 75, 151, 34, 1, 96, 26, 39, 1, 155, 15, 38, 1, 122, + 25, 122, 90, 6, 9, 4, 18, 67, 10, 122, 153, 122, 219, 2, 9, 67, 10, 67, 19, 147, 6, 150, 11, 224, 230, 75, 141, 153, + 5, 38, 1, 96, 25, 37, 0, 39, 0, 224, 223, 104, 35, 104, 26, 75, 138, 224, 97, 104, 35, 37, 0, 120, 222, 75, 136, 120, + 27, 66, 179, 208, 16, 75, 135, 104, 27, 43, 0, 208, 12, 240, 3, 249, 40, 240, 0, 254, 206, 240, 0, 254, 180, 75, 129, + 112, 30, 240, 0, 254, 146, 240, 3, 249, 62, 28, 5, 75, 126, 112, 30, 224, 165, 35, 1, 96, 11, 154, 5, 75, 121, 72, 124, + 96, 26, 240, 0, 249, 164, 75, 123, 34, 4, 104, 27, 39, 0, 104, 155, 96, 154, 240, 0, 254, 154, 77, 120, 28, 40, 240, + 3, 250, 74, 75, 119, 96, 24, 104, 35, 43, 0, 208, 1, 104, 42, 96, 26, 75, 116, 104, 29, 224, 135, 104, 35, 105, 34, + 104, 24, 104, 163, 38, 1, 104, 25, 240, 3, 251, 254, 75, 111, 28, 5, 120, 26, 42, 0, 209, 0, 224, 148, 70, 105, 34, + 23, 92, 81, 112, 25, 104, 35, 40, 0, 221, 2, 104, 25, 72, 105, 224, 1, 104, 25, 72, 105, 240, 1, 248, 24, 224, 133, + 104, 35, 105, 34, 104, 24, 104, 163, 104, 25, 240, 3, 252, 134, 224, 97, 75, 100, 104, 26, 66, 81, 65, 74, 96, 26, 224, + 105, 104, 35, 104, 26, 75, 85, 96, 26, 38, 1, 231, 106, 122, 17, 122, 83, 6, 9, 4, 27, 67, 11, 122, 145, 122, 210, 2, + 9, 67, 11, 67, 26, 104, 37, 35, 0, 146, 6, 147, 13, 120, 42, 42, 95, 209, 26, 120, 106, 42, 68, 209, 23, 120, 170, 42, + 86, 209, 20, 120, 234, 42, 68, 209, 17, 77, 82, 74, 69, 96, 43, 104, 227, 33, 1, 96, 17, 43, 4, 209, 6, 104, 161, 168, + 13, 34, 4, 240, 4, 254, 134, 155, 13, 96, 43, 240, 3, 255, 12, 224, 17, 104, 227, 43, 4, 209, 4, 104, 161, 168, 13, + 34, 4, 240, 4, 254, 121, 28, 40, 153, 13, 240, 3, 255, 67, 144, 7, 40, 0, 208, 46, 75, 53, 34, 1, 96, 26, 38, 1, 37, + 0, 224, 43, 240, 3, 251, 66, 38, 1, 28, 5, 40, 0, 209, 42, 35, 1, 74, 60, 66, 91, 96, 19, 224, 37, 75, 45, 34, 1, 96, + 26, 104, 35, 105, 33, 104, 24, 104, 163, 104, 26, 247, 255, 251, 159, 28, 5, 38, 1, 224, 24, 154, 7, 42, 0, 208, 18, + 104, 35, 105, 34, 104, 25, 104, 163, 152, 7, 104, 27, 240, 4, 251, 17, 38, 1, 37, 0, 224, 10, 38, 1, 37, 6, 224, 6, + 37, 1, 38, 1, 66, 109, 39, 1, 224, 2, 38, 1, 37, 1, 66, 109, 153, 4, 0, 203, 24, 228, 224, 7, 104, 32, 104, 97, 240, + 4, 255, 141, 154, 4, 52, 8, 50, 1, 146, 4, 155, 4, 153, 10, 66, 139, 219, 243, 224, 9, 38, 1, 37, 1, 224, 1, 38, 1, + 37, 6, 66, 109, 224, 2, 37, 0, 38, 1, 149, 6, 74, 12, 36, 0, 104, 19, 43, 0, 208, 4, 152, 9, 73, 23, 240, 4, 255, 31, + 36, 1, 46, 0, 209, 0, 228, 213, 152, 15, 28, 41, 240, 4, 255, 117, 228, 208, 87, 70, 83, 2, 85, 77, 83, 131, 87, 70, + 83, 1, 87, 70, 83, 3, 19, 112, 96, 20, 19, 112, 106, 196, 19, 112, 1, 12, 19, 112, 107, 56, 19, 112, 21, 69, 19, 112, + 96, 60, 19, 114, 199, 32, 19, 114, 199, 36, 19, 112, 96, 16, 19, 112, 96, 137, 19, 112, 96, 164, 19, 112, 106, 204, + 19, 112, 96, 12, 19, 112, 96, 24, 0, 152, 150, 128, 74, 3, 35, 128, 104, 17, 2, 27, 67, 11, 96, 19, 71, 112, 70, 192, + 13, 4, 0, 204, 75, 2, 74, 3, 104, 25, 64, 10, 96, 26, 71, 112, 13, 4, 0, 204, 255, 255, 127, 255, 181, 16, 247, 255, + 255, 243, 32, 128, 240, 3, 254, 16, 33, 32, 240, 4, 255, 1, 76, 9, 96, 32, 32, 4, 240, 4, 255, 12, 104, 33, 34, 0, 32, + 4, 240, 4, 255, 47, 247, 255, 255, 215, 32, 4, 240, 4, 254, 216, 188, 16, 188, 1, 71, 0, 70, 192, 19, 112, 96, 32, 181, + 16, 28, 4, 75, 15, 28, 8, 0, 137, 24, 9, 104, 26, 0, 73, 35, 1, 240, 4, 254, 171, 75, 12, 33, 16, 96, 24, 72, 11, 240, + 3, 255, 18, 33, 16, 72, 10, 240, 3, 255, 25, 75, 10, 34, 55, 96, 28, 75, 9, 32, 4, 104, 27, 104, 155, 96, 154, 240, + 4, 254, 179, 188, 16, 188, 1, 71, 0, 19, 112, 96, 32, 19, 112, 96, 28, 13, 128, 0, 56, 13, 128, 0, 60, 19, 112, 106, + 224, 19, 112, 96, 60, 181, 112, 76, 21, 75, 21, 38, 2, 66, 118, 28, 33, 104, 24, 34, 0, 96, 38, 240, 4, 254, 191, 75, + 18, 77, 18, 104, 27, 104, 154, 35, 0, 96, 147, 74, 17, 104, 40, 96, 19, 240, 4, 254, 134, 104, 40, 240, 4, 254, 205, + 35, 1, 66, 91, 96, 43, 104, 35, 43, 0, 209, 3, 75, 11, 104, 27, 96, 35, 224, 0, 96, 38, 32, 4, 240, 4, 254, 125, 75, + 2, 104, 24, 188, 112, 188, 2, 71, 8, 19, 112, 106, 216, 19, 112, 96, 32, 19, 112, 96, 60, 19, 112, 96, 28, 19, 112, + 106, 224, 19, 112, 106, 220, 75, 2, 34, 0, 96, 24, 75, 2, 96, 26, 71, 112, 19, 112, 106, 228, 19, 112, 106, 224, 181, + 248, 76, 34, 38, 16, 104, 35, 77, 33, 67, 179, 96, 35, 104, 43, 73, 32, 104, 154, 121, 19, 121, 87, 6, 27, 4, 63, 67, + 31, 121, 147, 2, 27, 67, 31, 121, 211, 67, 31, 104, 11, 43, 0, 208, 18, 28, 56, 240, 0, 248, 56, 40, 0, 220, 20, 74, + 22, 35, 0, 96, 19, 75, 22, 96, 24, 32, 4, 240, 4, 254, 66, 104, 43, 34, 55, 104, 155, 64, 23, 96, 95, 224, 22, 75, 17, + 104, 27, 43, 0, 208, 8, 28, 56, 240, 0, 248, 33, 104, 43, 34, 55, 104, 155, 64, 23, 96, 95, 224, 2, 35, 55, 64, 31, + 96, 87, 104, 35, 67, 51, 96, 35, 75, 9, 104, 26, 67, 22, 96, 30, 32, 0, 188, 248, 188, 2, 71, 8, 70, 192, 13, 128, 0, + 60, 19, 112, 96, 60, 19, 112, 106, 224, 19, 112, 106, 220, 19, 112, 106, 228, 13, 128, 0, 56, 71, 24, 70, 192, 181, + 112, 28, 12, 28, 5, 240, 4, 254, 67, 28, 6, 32, 1, 66, 64, 240, 4, 254, 50, 104, 34, 75, 17, 66, 154, 209, 25, 75, 16, + 73, 17, 34, 1, 67, 19, 96, 75, 28, 32, 34, 8, 240, 4, 252, 219, 28, 32, 27, 100, 8, 164, 33, 8, 60, 2, 240, 4, 254, + 54, 2, 36, 35, 234, 10, 36, 6, 27, 67, 35, 96, 43, 28, 40, 33, 4, 240, 4, 254, 44, 28, 48, 240, 4, 254, 17, 188, 112, + 188, 1, 71, 0, 230, 0, 1, 112, 19, 112, 4, 153, 19, 112, 96, 36, 181, 56, 28, 4, 240, 4, 253, 202, 44, 38, 208, 72, + 44, 38, 216, 5, 44, 36, 208, 9, 44, 37, 208, 0, 224, 169, 224, 14, 44, 57, 208, 112, 44, 60, 208, 0, 224, 163, 224, + 110, 73, 83, 75, 84, 72, 84, 96, 75, 34, 8, 240, 4, 252, 163, 72, 82, 224, 150, 72, 82, 73, 82, 247, 255, 255, 171, + 76, 77, 75, 81, 77, 82, 96, 99, 34, 8, 28, 40, 28, 33, 240, 4, 252, 148, 28, 40, 33, 8, 240, 4, 253, 242, 75, 77, 77, + 78, 96, 99, 34, 8, 28, 40, 28, 33, 240, 4, 252, 136, 28, 40, 33, 8, 240, 4, 253, 230, 75, 73, 77, 74, 96, 99, 34, 8, + 28, 33, 28, 40, 240, 4, 252, 124, 28, 40, 33, 8, 240, 4, 253, 218, 75, 59, 72, 69, 96, 99, 28, 33, 34, 8, 240, 4, 252, + 113, 72, 66, 224, 100, 72, 66, 73, 66, 247, 255, 255, 121, 76, 52, 75, 65, 77, 57, 96, 99, 34, 8, 28, 40, 28, 33, 240, + 4, 252, 98, 28, 40, 33, 8, 240, 4, 253, 192, 75, 60, 77, 53, 96, 99, 34, 8, 28, 40, 28, 33, 240, 4, 252, 86, 28, 40, + 33, 8, 240, 4, 253, 180, 75, 55, 77, 49, 96, 99, 34, 8, 28, 33, 28, 40, 240, 4, 252, 74, 28, 40, 33, 8, 240, 4, 253, + 168, 75, 34, 72, 50, 96, 99, 28, 33, 34, 8, 240, 4, 252, 63, 72, 47, 224, 50, 72, 47, 224, 0, 72, 47, 73, 47, 247, 255, + 255, 69, 76, 26, 75, 46, 77, 31, 96, 99, 34, 8, 28, 40, 28, 33, 240, 4, 252, 46, 28, 40, 33, 8, 240, 4, 253, 140, 75, + 41, 77, 27, 96, 99, 34, 8, 28, 40, 28, 33, 240, 4, 252, 34, 28, 40, 33, 8, 240, 4, 253, 128, 75, 36, 77, 23, 96, 99, + 34, 8, 28, 33, 28, 40, 240, 4, 252, 22, 28, 40, 33, 8, 240, 4, 253, 116, 75, 8, 72, 31, 96, 99, 28, 33, 34, 8, 240, + 4, 252, 11, 72, 28, 33, 8, 240, 4, 253, 105, 32, 0, 188, 56, 188, 2, 71, 8, 70, 192, 19, 112, 96, 36, 19, 112, 0, 96, + 255, 255, 30, 120, 32, 32, 93, 232, 32, 32, 64, 140, 255, 255, 31, 112, 19, 112, 0, 188, 255, 255, 31, 140, 19, 112, + 0, 196, 255, 255, 30, 52, 19, 112, 0, 204, 255, 255, 31, 104, 32, 32, 91, 20, 32, 32, 62, 108, 255, 255, 30, 176, 255, + 255, 30, 204, 255, 255, 29, 116, 255, 255, 30, 168, 32, 32, 94, 132, 32, 32, 93, 148, 32, 32, 63, 96, 255, 255, 33, + 48, 255, 255, 33, 76, 255, 255, 31, 244, 255, 255, 33, 40, 181, 56, 77, 11, 35, 0, 104, 42, 73, 10, 240, 4, 252, 201, + 76, 10, 73, 10, 34, 0, 96, 32, 104, 40, 240, 4, 253, 4, 104, 32, 240, 4, 252, 211, 104, 32, 240, 4, 253, 26, 188, 56, + 188, 1, 71, 0, 70, 192, 19, 112, 96, 44, 0, 152, 150, 128, 19, 112, 96, 52, 19, 112, 106, 232, 181, 8, 1, 67, 26, 27, + 0, 155, 24, 24, 0, 192, 247, 255, 255, 216, 188, 8, 188, 1, 71, 0, 0, 0, 181, 0, 74, 5, 2, 192, 104, 19, 104, 17, 26, + 201, 213, 0, 104, 19, 66, 129, 217, 249, 188, 1, 71, 0, 13, 128, 0, 16, 181, 8, 75, 24, 120, 26, 30, 83, 65, 154, 75, + 23, 96, 26, 240, 3, 252, 204, 75, 22, 33, 4, 96, 24, 28, 24, 240, 4, 252, 225, 240, 3, 252, 206, 34, 0, 28, 1, 72, 18, + 240, 3, 253, 3, 33, 160, 2, 73, 72, 16, 240, 4, 252, 201, 75, 16, 33, 128, 96, 24, 240, 4, 252, 122, 33, 32, 240, 4, + 252, 169, 75, 13, 96, 24, 240, 3, 251, 37, 40, 0, 219, 3, 247, 255, 250, 7, 32, 0, 224, 1, 32, 1, 66, 64, 188, 8, 188, + 2, 71, 8, 19, 112, 1, 12, 19, 112, 107, 80, 19, 114, 195, 128, 19, 112, 14, 77, 19, 113, 131, 128, 19, 112, 96, 48, + 19, 112, 96, 44, 181, 240, 7, 65, 213, 27, 34, 128, 78, 14, 77, 15, 76, 15, 35, 0, 1, 146, 104, 49, 0, 152, 104, 137, + 104, 47, 49, 68, 24, 9, 104, 8, 66, 187, 209, 5, 7, 193, 212, 7, 73, 9, 32, 2, 96, 8, 224, 3, 64, 32, 40, 3, 209, 0, + 96, 10, 51, 1, 43, 4, 209, 233, 188, 240, 188, 1, 71, 0, 19, 112, 96, 60, 19, 112, 107, 80, 0, 0, 32, 3, 19, 112, 107, + 84, 181, 240, 176, 131, 147, 1, 155, 8, 76, 14, 0, 91, 104, 38, 28, 13, 28, 23, 147, 0, 32, 10, 247, 255, 255, 79, 104, + 43, 154, 1, 64, 59, 66, 147, 208, 9, 104, 35, 27, 155, 213, 0, 104, 38, 154, 0, 66, 147, 211, 240, 32, 2, 66, 64, 224, + 0, 32, 0, 176, 3, 188, 240, 188, 2, 71, 8, 70, 192, 13, 128, 0, 16, 181, 56, 28, 4, 240, 3, 251, 27, 33, 0, 28, 5, 34, + 96, 28, 32, 240, 4, 251, 64, 35, 64, 96, 163, 35, 128, 4, 91, 99, 101, 96, 35, 96, 99, 188, 56, 188, 1, 71, 0, 181, + 16, 75, 12, 36, 0, 104, 27, 108, 26, 42, 7, 220, 13, 73, 10, 104, 12, 0, 228, 24, 164, 0, 97, 25, 12, 106, 25, 1, 100, + 25, 12, 50, 1, 100, 26, 28, 32, 247, 255, 255, 215, 28, 32, 188, 16, 188, 2, 71, 8, 70, 192, 19, 112, 96, 60, 19, 112, + 107, 64, 181, 240, 28, 4, 176, 133, 28, 8, 28, 14, 147, 3, 28, 23, 240, 4, 250, 147, 35, 0, 98, 35, 5, 53, 35, 128, + 13, 45, 1, 91, 27, 93, 96, 224, 66, 175, 210, 1, 28, 61, 224, 49, 32, 128, 1, 64, 24, 54, 11, 54, 3, 54, 150, 2, 33, + 1, 28, 38, 54, 16, 145, 1, 224, 20, 152, 2, 240, 4, 250, 121, 34, 0, 198, 1, 97, 50, 155, 2, 33, 128, 28, 24, 35, 128, + 1, 91, 1, 73, 24, 192, 24, 109, 144, 2, 66, 189, 211, 0, 28, 61, 154, 1, 50, 1, 146, 1, 66, 189, 210, 8, 152, 1, 33, + 0, 15, 195, 34, 4, 66, 130, 65, 75, 6, 27, 43, 0, 209, 223, 66, 189, 208, 4, 28, 40, 153, 10, 240, 4, 251, 194, 26, + 109, 155, 3, 4, 40, 67, 24, 240, 4, 250, 80, 35, 128, 4, 91, 96, 160, 100, 37, 96, 35, 96, 99, 176, 5, 28, 40, 188, + 240, 188, 2, 71, 8, 0, 0, 181, 240, 176, 139, 28, 5, 247, 255, 255, 133, 28, 6, 32, 0, 46, 0, 209, 0, 224, 145, 99, + 245, 105, 42, 126, 107, 126, 47, 97, 104, 146, 3, 147, 7, 47, 0, 209, 25, 35, 8, 147, 0, 35, 160, 104, 105, 34, 8, 0, + 155, 28, 48, 247, 255, 255, 140, 247, 255, 255, 108, 28, 4, 28, 56, 44, 0, 208, 121, 107, 96, 99, 229, 240, 4, 250, + 30, 154, 3, 96, 48, 99, 180, 42, 0, 208, 4, 79, 58, 224, 3, 28, 52, 39, 128, 224, 0, 79, 56, 104, 235, 154, 7, 147, + 4, 42, 0, 208, 2, 35, 128, 0, 91, 67, 31, 105, 235, 150, 9, 5, 91, 13, 91, 147, 5, 154, 5, 35, 128, 4, 91, 58, 1, 147, + 6, 146, 8, 155, 5, 154, 3, 147, 0, 28, 32, 28, 59, 153, 4, 247, 255, 255, 92, 154, 3, 155, 4, 26, 18, 146, 3, 154, 7, + 24, 27, 147, 4, 42, 0, 208, 1, 155, 6, 96, 99, 154, 8, 155, 5, 24, 128, 66, 3, 209, 2, 34, 128, 6, 18, 24, 191, 155, + 3, 43, 0, 221, 11, 247, 255, 255, 38, 30, 6, 208, 52, 107, 112, 99, 245, 240, 4, 249, 218, 99, 166, 96, 32, 28, 52, + 231, 213, 35, 128, 4, 91, 96, 99, 105, 43, 158, 9, 148, 3, 43, 0, 208, 29, 126, 42, 146, 4, 42, 0, 209, 25, 247, 255, + 255, 14, 28, 4, 32, 0, 44, 0, 208, 27, 107, 96, 99, 229, 240, 4, 249, 192, 155, 3, 34, 128, 96, 24, 99, 156, 35, 128, + 0, 91, 6, 18, 64, 123, 67, 19, 154, 4, 28, 32, 146, 0, 33, 0, 34, 0, 247, 255, 255, 20, 104, 162, 35, 128, 4, 27, 67, + 19, 96, 163, 28, 48, 224, 0, 32, 0, 176, 11, 188, 240, 188, 2, 71, 8, 128, 0, 0, 128, 128, 0, 1, 128, 181, 56, 76, 19, + 37, 32, 104, 35, 104, 155, 120, 25, 120, 90, 6, 9, 4, 18, 67, 10, 120, 153, 120, 219, 2, 9, 67, 10, 67, 19, 224, 18, + 104, 34, 67, 171, 104, 146, 32, 10, 96, 19, 247, 255, 253, 243, 104, 35, 104, 155, 120, 25, 120, 90, 6, 9, 4, 18, 67, + 10, 120, 153, 120, 219, 2, 9, 67, 10, 67, 19, 66, 29, 209, 234, 188, 56, 188, 1, 71, 0, 19, 112, 96, 60, 181, 56, 76, + 19, 37, 32, 104, 35, 104, 155, 120, 25, 120, 90, 6, 9, 4, 18, 67, 10, 120, 153, 120, 219, 2, 9, 67, 10, 67, 19, 224, + 18, 104, 34, 67, 43, 104, 146, 32, 10, 96, 19, 247, 255, 253, 201, 104, 35, 104, 155, 120, 25, 120, 90, 6, 9, 4, 18, + 67, 10, 120, 153, 120, 219, 2, 9, 67, 10, 67, 19, 66, 29, 208, 234, 188, 56, 188, 1, 71, 0, 19, 112, 96, 60, 181, 248, + 28, 12, 40, 0, 209, 33, 77, 28, 76, 29, 79, 29, 38, 0, 104, 43, 104, 155, 126, 25, 126, 90, 6, 9, 4, 18, 67, 10, 126, + 153, 126, 219, 2, 9, 67, 10, 67, 19, 105, 34, 108, 82, 66, 147, 211, 6, 105, 98, 108, 82, 66, 147, 216, 2, 247, 255, + 255, 142, 224, 25, 32, 10, 54, 1, 247, 255, 253, 147, 66, 190, 209, 226, 224, 18, 40, 1, 209, 16, 224, 6, 32, 10, 54, + 1, 247, 255, 253, 137, 66, 190, 209, 3, 224, 8, 77, 6, 79, 8, 38, 0, 104, 43, 104, 155, 105, 154, 108, 99, 66, 154, + 208, 239, 32, 0, 188, 248, 188, 2, 71, 8, 70, 192, 19, 112, 96, 60, 19, 114, 195, 160, 0, 0, 19, 136, 181, 0, 7, 67, + 213, 21, 75, 12, 104, 27, 104, 154, 75, 11, 108, 81, 64, 11, 43, 3, 209, 2, 35, 128, 1, 155, 100, 83, 75, 6, 104, 27, + 104, 154, 75, 6, 108, 145, 64, 11, 43, 3, 209, 2, 35, 128, 1, 155, 100, 147, 188, 1, 71, 0, 70, 192, 19, 112, 96, 60, + 0, 0, 32, 3, 181, 16, 75, 19, 28, 4, 104, 27, 52, 16, 104, 155, 0, 164, 25, 28, 34, 128, 104, 99, 1, 146, 66, 19, 209, + 0, 64, 83, 34, 46, 67, 147, 96, 99, 32, 5, 247, 255, 253, 106, 34, 128, 104, 99, 1, 146, 66, 19, 208, 0, 64, 83, 34, + 46, 67, 147, 96, 99, 32, 5, 247, 255, 253, 94, 75, 4, 32, 5, 96, 99, 247, 255, 253, 89, 188, 16, 188, 1, 71, 0, 19, + 112, 96, 60, 0, 0, 24, 1, 181, 240, 176, 131, 75, 36, 28, 5, 104, 27, 53, 16, 104, 155, 0, 173, 25, 93, 104, 107, 74, + 33, 29, 44, 64, 26, 38, 4, 42, 1, 208, 4, 4, 154, 213, 51, 247, 255, 255, 192, 224, 48, 39, 136, 1, 127, 34, 4, 67, + 147, 67, 59, 96, 35, 32, 60, 247, 255, 253, 40, 104, 35, 74, 24, 32, 50, 64, 19, 96, 35, 247, 255, 253, 33, 75, 22, + 34, 128, 147, 0, 28, 32, 28, 33, 0, 82, 35, 0, 247, 255, 253, 156, 40, 0, 208, 2, 104, 107, 72, 17, 224, 20, 104, 35, + 34, 4, 66, 26, 209, 16, 62, 1, 46, 0, 209, 220, 34, 128, 1, 146, 67, 19, 33, 42, 67, 139, 96, 107, 32, 10, 96, 106, + 247, 255, 253, 2, 72, 8, 224, 1, 32, 1, 66, 64, 176, 3, 188, 240, 188, 2, 71, 8, 19, 112, 96, 60, 0, 0, 32, 1, 255, + 255, 254, 213, 0, 0, 19, 136, 255, 255, 248, 48, 255, 255, 251, 161, 181, 16, 75, 20, 28, 4, 104, 27, 52, 16, 104, 155, + 0, 164, 25, 28, 34, 128, 104, 99, 1, 146, 66, 19, 209, 0, 64, 83, 34, 46, 67, 147, 96, 99, 32, 5, 247, 255, 252, 230, + 34, 128, 104, 99, 1, 146, 66, 19, 208, 0, 64, 83, 34, 46, 67, 147, 96, 99, 32, 5, 247, 255, 252, 218, 75, 5, 32, 60, + 96, 99, 247, 255, 252, 213, 32, 1, 188, 16, 188, 2, 71, 8, 70, 192, 19, 112, 96, 60, 0, 0, 24, 1, 32, 0, 71, 112, 75, + 5, 48, 16, 104, 27, 0, 128, 104, 155, 24, 24, 35, 128, 1, 155, 96, 67, 32, 0, 71, 112, 70, 192, 19, 112, 96, 60, 74, + 5, 32, 0, 104, 19, 104, 153, 35, 128, 1, 155, 100, 75, 104, 18, 104, 146, 100, 147, 71, 112, 70, 192, 19, 112, 96, 60, + 181, 0, 75, 10, 104, 27, 104, 154, 35, 128, 108, 81, 1, 155, 66, 25, 209, 0, 100, 83, 75, 5, 104, 27, 104, 154, 35, + 128, 108, 145, 1, 155, 66, 25, 209, 0, 100, 147, 32, 0, 188, 2, 71, 8, 19, 112, 96, 60, 75, 6, 33, 0, 104, 26, 35, 1, + 66, 91, 28, 16, 103, 17, 103, 147, 48, 152, 50, 160, 96, 1, 96, 19, 71, 112, 70, 192, 19, 112, 96, 60, 75, 6, 120, 27, + 30, 90, 65, 147, 74, 5, 0, 152, 96, 19, 74, 5, 24, 195, 0, 219, 104, 16, 51, 88, 24, 192, 71, 112, 19, 112, 1, 12, 19, + 112, 107, 80, 19, 112, 96, 60, 181, 16, 73, 19, 120, 9, 30, 72, 65, 129, 72, 18, 0, 140, 96, 1, 72, 17, 24, 97, 104, + 0, 0, 201, 24, 64, 111, 4, 33, 0, 44, 0, 208, 16, 128, 25, 128, 89, 28, 1, 49, 96, 136, 8, 4, 0, 10, 4, 14, 0, 67, 32, + 128, 152, 136, 73, 4, 9, 10, 8, 14, 9, 67, 1, 128, 217, 33, 1, 32, 0, 112, 17, 188, 16, 188, 2, 71, 8, 70, 192, 19, + 112, 1, 12, 19, 112, 107, 80, 19, 112, 96, 60, 181, 0, 28, 3, 224, 8, 120, 90, 58, 4, 6, 18, 14, 18, 42, 1, 217, 4, + 120, 26, 26, 137, 24, 155, 41, 0, 209, 244, 26, 24, 188, 2, 71, 8, 181, 240, 176, 133, 124, 130, 124, 195, 6, 18, 4, + 27, 67, 19, 125, 2, 28, 5, 2, 18, 67, 19, 125, 66, 67, 26, 208, 103, 34, 0, 146, 3, 146, 1, 224, 83, 6, 36, 4, 9, 2, + 18, 67, 12, 67, 20, 158, 3, 67, 35, 25, 156, 122, 98, 122, 163, 6, 18, 4, 27, 67, 19, 122, 226, 2, 18, 67, 19, 123, + 34, 67, 26, 208, 58, 39, 0, 151, 2, 28, 46, 224, 36, 6, 0, 4, 9, 2, 18, 67, 8, 67, 16, 67, 3, 25, 221, 123, 170, 123, + 235, 6, 18, 4, 27, 67, 19, 124, 42, 124, 104, 2, 18, 67, 19, 67, 24, 208, 1, 240, 2, 255, 250, 122, 106, 122, 171, 6, + 18, 4, 27, 67, 19, 122, 234, 123, 40, 2, 18, 67, 19, 67, 24, 208, 1, 240, 2, 255, 237, 154, 2, 55, 18, 50, 1, 146, 2, + 121, 35, 157, 2, 70, 156, 122, 96, 122, 161, 122, 226, 123, 35, 69, 101, 219, 209, 28, 53, 6, 6, 4, 8, 67, 48, 2, 18, + 67, 16, 67, 24, 240, 2, 255, 215, 158, 1, 154, 3, 54, 1, 50, 13, 150, 1, 146, 3, 124, 104, 158, 1, 124, 172, 124, 233, + 125, 42, 125, 107, 66, 134, 219, 163, 6, 36, 4, 8, 67, 32, 2, 18, 67, 16, 67, 24, 240, 2, 255, 193, 176, 5, 188, 240, + 188, 1, 71, 0, 0, 0, 181, 248, 30, 5, 209, 4, 75, 28, 34, 48, 112, 26, 112, 93, 224, 47, 76, 26, 28, 46, 28, 48, 33, + 10, 240, 4, 248, 128, 28, 48, 28, 15, 33, 10, 240, 4, 248, 119, 23, 251, 24, 255, 64, 95, 55, 48, 75, 20, 112, 39, 60, + 1, 28, 6, 66, 156, 209, 236, 75, 15, 34, 0, 118, 154, 45, 0, 218, 2, 34, 45, 112, 26, 34, 1, 35, 16, 72, 10, 224, 0, + 51, 1, 92, 193, 41, 48, 208, 251, 66, 72, 65, 65, 26, 91, 73, 6, 224, 2, 84, 136, 51, 1, 50, 1, 92, 200, 40, 0, 209, + 249, 84, 136, 188, 248, 188, 1, 71, 0, 70, 192, 19, 114, 197, 96, 19, 114, 197, 121, 19, 114, 197, 111, 181, 248, 30, + 4, 209, 4, 75, 24, 34, 48, 112, 26, 112, 92, 224, 40, 77, 22, 28, 47, 63, 10, 28, 32, 33, 10, 240, 4, 248, 23, 28, 32, + 28, 14, 33, 10, 240, 4, 248, 30, 54, 48, 112, 46, 61, 1, 28, 4, 66, 189, 209, 240, 74, 12, 35, 0, 118, 147, 28, 17, + 35, 16, 224, 0, 51, 1, 92, 202, 42, 48, 208, 251, 66, 81, 65, 74, 26, 155, 73, 6, 34, 0, 224, 2, 84, 136, 51, 1, 50, + 1, 92, 200, 40, 0, 209, 249, 84, 136, 188, 248, 188, 1, 71, 0, 19, 114, 197, 96, 19, 114, 197, 121, 181, 16, 40, 0, + 209, 4, 75, 24, 34, 48, 112, 26, 112, 88, 224, 40, 75, 22, 33, 15, 28, 28, 60, 8, 28, 10, 64, 2, 9, 0, 42, 9, 221, 0, + 50, 7, 50, 48, 112, 26, 59, 1, 66, 163, 209, 244, 74, 14, 35, 0, 118, 19, 35, 48, 112, 19, 35, 120, 112, 83, 35, 16, + 224, 0, 51, 1, 92, 209, 41, 48, 208, 251, 66, 74, 65, 74, 26, 155, 73, 6, 34, 2, 224, 2, 84, 136, 51, 1, 50, 1, 92, + 200, 40, 0, 209, 249, 84, 136, 188, 16, 188, 1, 71, 0, 19, 114, 197, 96, 19, 114, 197, 119, 180, 15, 181, 240, 176, + 133, 171, 10, 203, 64, 34, 128, 1, 146, 169, 3, 128, 10, 147, 2, 36, 0, 77, 46, 79, 46, 224, 81, 28, 114, 146, 1, 43, + 37, 208, 12, 70, 106, 115, 19, 168, 3, 240, 3, 255, 41, 25, 4, 158, 1, 66, 172, 220, 68, 168, 3, 240, 3, 255, 222, 224, + 64, 120, 115, 43, 115, 208, 46, 43, 115, 216, 4, 43, 100, 208, 7, 43, 105, 209, 53, 224, 4, 43, 117, 208, 9, 43, 120, + 209, 48, 224, 20, 155, 2, 29, 26, 146, 2, 104, 24, 247, 255, 255, 26, 224, 19, 155, 2, 29, 26, 146, 2, 104, 24, 247, + 255, 255, 85, 28, 56, 240, 3, 255, 2, 25, 4, 28, 56, 66, 172, 220, 27, 224, 24, 155, 2, 29, 26, 146, 2, 104, 24, 247, + 255, 255, 127, 72, 16, 240, 3, 254, 244, 25, 4, 66, 172, 220, 14, 72, 13, 224, 10, 155, 2, 29, 26, 146, 2, 104, 30, + 28, 48, 240, 3, 254, 232, 25, 4, 66, 172, 220, 2, 28, 48, 240, 3, 255, 158, 158, 1, 54, 1, 120, 51, 43, 0, 209, 170, + 176, 5, 188, 240, 188, 8, 176, 4, 71, 24, 0, 0, 13, 171, 19, 114, 197, 96, 181, 240, 28, 12, 176, 133, 28, 6, 33, 0, + 32, 0, 247, 255, 252, 176, 126, 35, 43, 0, 209, 6, 74, 218, 33, 8, 96, 19, 104, 32, 240, 2, 254, 69, 96, 96, 105, 33, + 41, 0, 208, 9, 126, 99, 104, 160, 43, 0, 208, 2, 240, 2, 254, 59, 224, 1, 240, 2, 254, 64, 96, 224, 126, 35, 43, 0, + 209, 3, 75, 207, 104, 27, 105, 219, 224, 6, 126, 99, 43, 0, 208, 1, 75, 205, 224, 0, 75, 205, 104, 27, 147, 2, 77, 204, + 75, 205, 153, 2, 28, 40, 96, 25, 34, 96, 240, 2, 255, 104, 33, 0, 34, 48, 152, 2, 240, 3, 254, 69, 75, 195, 34, 0, 104, + 27, 28, 32, 100, 26, 75, 197, 34, 1, 104, 25, 64, 74, 96, 26, 247, 255, 251, 121, 75, 195, 100, 168, 96, 24, 126, 34, + 35, 15, 146, 1, 64, 26, 146, 3, 155, 3, 28, 7, 34, 128, 105, 176, 2, 29, 1, 146, 155, 1, 67, 2, 105, 225, 67, 42, 43, + 0, 209, 1, 72, 186, 224, 5, 5, 72, 35, 128, 9, 64, 5, 219, 67, 3, 28, 24, 67, 16, 240, 3, 253, 158, 77, 176, 35, 64, + 96, 171, 75, 180, 33, 255, 104, 27, 34, 255, 108, 91, 2, 9, 4, 18, 64, 25, 64, 26, 2, 9, 10, 18, 67, 10, 14, 25, 67, + 10, 33, 224, 64, 11, 33, 2, 67, 11, 6, 27, 67, 26, 96, 104, 96, 42, 107, 120, 240, 3, 253, 130, 35, 128, 153, 1, 4, + 91, 97, 40, 97, 107, 41, 0, 208, 16, 9, 203, 153, 3, 106, 114, 1, 27, 24, 91, 64, 218, 28, 19, 7, 218, 213, 3, 105, + 170, 35, 128, 67, 19, 224, 2, 105, 171, 34, 128, 67, 147, 97, 171, 77, 150, 75, 156, 105, 170, 28, 40, 64, 19, 97, 171, + 33, 96, 240, 2, 253, 195, 108, 173, 224, 4, 28, 40, 33, 96, 240, 2, 253, 189, 107, 173, 45, 0, 209, 248, 152, 2, 73, + 141, 34, 96, 240, 2, 254, 224, 75, 146, 120, 27, 43, 0, 208, 4, 75, 145, 72, 145, 104, 25, 247, 255, 254, 218, 75, 144, + 34, 1, 96, 26, 77, 130, 35, 0, 147, 1, 153, 1, 74, 139, 49, 1, 145, 1, 72, 140, 104, 17, 247, 254, 255, 192, 104, 43, + 33, 32, 105, 152, 240, 2, 253, 173, 104, 42, 73, 124, 39, 2, 108, 75, 105, 145, 34, 255, 2, 18, 64, 26, 2, 16, 34, 255, + 4, 18, 64, 26, 10, 18, 67, 2, 14, 24, 67, 2, 32, 224, 64, 3, 67, 59, 6, 24, 28, 19, 67, 3, 96, 11, 104, 43, 33, 32, + 105, 152, 240, 2, 253, 125, 247, 255, 251, 163, 247, 254, 255, 201, 154, 1, 42, 4, 208, 6, 28, 3, 51, 9, 209, 3, 72, + 118, 247, 255, 254, 158, 231, 200, 75, 114, 34, 0, 96, 26, 75, 109, 28, 7, 120, 27, 43, 0, 208, 3, 72, 113, 28, 57, + 247, 255, 254, 145, 47, 0, 209, 3, 75, 93, 104, 27, 43, 0, 208, 15, 75, 92, 104, 27, 104, 154, 75, 107, 50, 68, 104, + 27, 0, 155, 24, 211, 104, 27, 34, 5, 64, 19, 43, 5, 208, 2, 75, 84, 34, 1, 96, 26, 66, 121, 65, 121, 145, 1, 29, 122, + 208, 1, 41, 0, 208, 65, 247, 255, 251, 66, 77, 79, 33, 32, 104, 43, 105, 152, 240, 3, 254, 144, 104, 43, 33, 32, 105, + 152, 240, 2, 253, 39, 75, 82, 104, 42, 104, 27, 105, 145, 108, 91, 34, 255, 2, 18, 64, 26, 2, 16, 34, 255, 4, 18, 64, + 26, 10, 18, 67, 2, 14, 24, 67, 2, 32, 224, 64, 3, 32, 2, 67, 3, 6, 24, 28, 19, 67, 3, 96, 11, 104, 43, 33, 32, 105, + 152, 240, 3, 254, 78, 247, 255, 251, 66, 126, 34, 42, 0, 208, 98, 9, 211, 32, 15, 64, 16, 1, 27, 24, 27, 106, 113, 34, + 1, 64, 154, 67, 145, 28, 10, 73, 70, 104, 9, 6, 9, 15, 201, 64, 153, 28, 11, 67, 19, 98, 115, 224, 79, 247, 255, 251, + 0, 75, 49, 108, 157, 224, 4, 107, 104, 33, 96, 240, 2, 253, 15, 107, 173, 45, 0, 209, 248, 77, 44, 34, 32, 28, 40, 153, + 2, 240, 2, 254, 42, 126, 34, 42, 0, 208, 16, 9, 211, 32, 15, 64, 16, 1, 27, 24, 27, 106, 113, 34, 1, 64, 154, 67, 145, + 28, 10, 105, 169, 6, 9, 15, 201, 64, 153, 28, 11, 67, 19, 98, 115, 77, 28, 33, 32, 104, 43, 38, 2, 105, 152, 240, 3, + 254, 41, 104, 43, 33, 32, 105, 152, 240, 2, 252, 192, 75, 30, 104, 42, 104, 27, 105, 145, 108, 91, 34, 255, 2, 18, 64, + 26, 2, 16, 34, 255, 4, 18, 64, 26, 10, 18, 67, 2, 14, 24, 67, 2, 32, 224, 64, 3, 67, 51, 6, 24, 28, 19, 67, 3, 96, 11, + 104, 43, 33, 32, 105, 152, 240, 3, 253, 232, 247, 255, 250, 220, 73, 10, 32, 1, 247, 255, 251, 2, 105, 33, 41, 0, 208, + 48, 126, 99, 104, 224, 43, 0, 208, 42, 240, 2, 252, 175, 224, 41, 19, 112, 107, 84, 19, 112, 96, 60, 19, 112, 106, 244, + 19, 112, 107, 72, 19, 114, 197, 0, 19, 112, 106, 252, 19, 112, 107, 64, 19, 112, 107, 20, 64, 64, 64, 0, 19, 112, 107, + 68, 1, 0, 0, 128, 19, 112, 106, 248, 19, 112, 96, 56, 19, 112, 96, 194, 19, 112, 107, 16, 19, 112, 41, 229, 19, 112, + 98, 156, 19, 112, 96, 240, 19, 112, 107, 80, 19, 112, 107, 4, 240, 2, 252, 138, 126, 35, 43, 0, 209, 3, 104, 96, 33, + 8, 240, 2, 252, 125, 155, 1, 43, 0, 208, 0, 105, 103, 176, 5, 28, 56, 188, 240, 188, 2, 71, 8, 181, 48, 176, 137, 70, + 108, 118, 33, 9, 201, 118, 97, 33, 128, 0, 137, 37, 0, 145, 7, 70, 105, 149, 0, 146, 4, 147, 2, 247, 255, 253, 246, + 176, 9, 188, 48, 188, 2, 71, 8, 181, 240, 176, 139, 28, 6, 70, 104, 48, 70, 136, 4, 72, 43, 70, 156, 104, 0, 70, 107, + 51, 66, 48, 248, 104, 0, 136, 27, 112, 66, 4, 27, 70, 98, 147, 1, 2, 19, 10, 18, 67, 19, 4, 26, 12, 19, 14, 18, 112, + 1, 159, 18, 112, 130, 112, 195, 155, 1, 4, 36, 10, 26, 14, 27, 67, 19, 4, 27, 12, 37, 12, 26, 14, 27, 113, 3, 14, 36, + 2, 43, 67, 35, 4, 27, 113, 66, 14, 26, 12, 27, 113, 195, 172, 2, 35, 0, 113, 130, 118, 35, 35, 64, 97, 227, 75, 21, + 9, 201, 144, 2, 118, 97, 97, 37, 66, 159, 217, 22, 28, 40, 240, 2, 252, 52, 28, 42, 28, 57, 96, 160, 240, 3, 251, 237, + 28, 33, 28, 48, 247, 255, 253, 171, 104, 164, 28, 6, 28, 33, 28, 56, 28, 42, 240, 3, 251, 226, 28, 32, 240, 2, 252, + 63, 224, 5, 28, 48, 96, 167, 28, 33, 247, 255, 253, 155, 28, 6, 176, 11, 28, 48, 188, 240, 188, 2, 71, 8, 70, 192, 19, + 112, 96, 60, 19, 136, 0, 0, 181, 0, 176, 133, 145, 0, 35, 0, 33, 2, 34, 1, 147, 1, 147, 2, 247, 255, 255, 148, 176, + 5, 188, 2, 71, 8, 181, 0, 176, 133, 28, 19, 34, 0, 145, 0, 146, 1, 146, 2, 33, 1, 34, 11, 247, 255, 255, 134, 176, 5, + 188, 2, 71, 8, 181, 0, 176, 133, 34, 0, 146, 0, 146, 1, 146, 2, 28, 11, 34, 9, 33, 0, 247, 255, 255, 120, 176, 5, 188, + 2, 71, 8, 181, 112, 28, 4, 176, 132, 32, 1, 28, 14, 240, 2, 251, 224, 30, 5, 208, 18, 35, 0, 147, 0, 35, 1, 147, 1, + 28, 32, 33, 128, 34, 8, 35, 0, 149, 2, 247, 255, 255, 97, 30, 4, 219, 1, 120, 43, 112, 51, 28, 40, 240, 2, 251, 234, + 224, 1, 36, 4, 66, 100, 176, 4, 28, 32, 188, 112, 188, 2, 71, 8, 181, 16, 176, 132, 70, 108, 2, 18, 52, 27, 120, 36, + 67, 19, 34, 0, 146, 0, 145, 2, 34, 6, 33, 128, 148, 1, 247, 255, 255, 67, 176, 4, 188, 16, 188, 2, 71, 8, 0, 0, 181, + 240, 176, 137, 75, 65, 28, 2, 104, 30, 0, 129, 104, 179, 24, 9, 50, 16, 0, 201, 0, 146, 24, 154, 28, 11, 51, 88, 28, + 7, 24, 240, 144, 5, 24, 113, 32, 0, 103, 8, 104, 81, 29, 21, 74, 56, 64, 10, 42, 1, 209, 99, 34, 4, 146, 4, 147, 7, + 75, 54, 32, 10, 96, 43, 247, 254, 255, 156, 75, 52, 32, 100, 96, 43, 247, 254, 255, 151, 75, 51, 34, 128, 96, 43, 35, + 250, 0, 219, 147, 0, 0, 82, 35, 0, 28, 40, 28, 41, 247, 255, 248, 15, 104, 42, 75, 45, 28, 4, 64, 19, 43, 5, 209, 50, + 35, 192, 1, 27, 64, 26, 35, 128, 0, 219, 66, 154, 208, 43, 40, 0, 209, 43, 32, 100, 247, 254, 255, 121, 75, 38, 74, + 38, 104, 24, 96, 26, 154, 7, 35, 22, 147, 1, 24, 179, 147, 2, 35, 128, 144, 6, 33, 128, 152, 5, 34, 6, 0, 91, 148, 0, + 247, 255, 254, 231, 30, 4, 219, 12, 28, 123, 32, 0, 4, 27, 144, 0, 144, 1, 144, 2, 12, 27, 152, 5, 33, 0, 34, 5, 247, + 255, 254, 217, 28, 4, 75, 21, 154, 6, 96, 26, 44, 0, 219, 2, 224, 8, 36, 1, 66, 100, 155, 4, 59, 1, 147, 4, 43, 0, 209, + 169, 44, 0, 219, 10, 0, 187, 25, 219, 0, 219, 24, 246, 55, 1, 35, 0, 103, 243, 103, 55, 224, 1, 36, 1, 66, 100, 176, + 9, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 96, 60, 0, 0, 32, 1, 0, 0, 24, 3, 0, 0, 25, 3, 0, 0, 16, 1, 0, + 0, 32, 5, 19, 112, 96, 56, 0, 6, 26, 128, 181, 248, 76, 25, 28, 6, 104, 35, 104, 157, 35, 1, 96, 171, 247, 255, 255, + 97, 30, 7, 218, 24, 104, 35, 33, 55, 104, 155, 28, 52, 104, 90, 32, 10, 64, 10, 96, 90, 52, 16, 247, 254, 255, 18, 0, + 164, 75, 15, 25, 44, 96, 99, 32, 50, 247, 254, 255, 11, 75, 13, 32, 100, 96, 99, 247, 254, 255, 6, 75, 11, 96, 99, 75, + 7, 32, 55, 104, 26, 104, 146, 104, 81, 64, 1, 96, 81, 104, 27, 34, 4, 104, 155, 28, 56, 96, 154, 188, 248, 188, 2, 71, + 8, 70, 192, 19, 112, 96, 60, 0, 0, 24, 3, 0, 0, 25, 3, 0, 0, 16, 1, 181, 240, 176, 137, 75, 126, 34, 0, 104, 31, 0, + 131, 24, 27, 0, 219, 28, 30, 147, 5, 54, 88, 24, 251, 28, 4, 25, 190, 103, 26, 32, 50, 247, 254, 254, 218, 37, 3, 150, + 6, 72, 118, 247, 255, 251, 219, 32, 50, 247, 254, 254, 210, 35, 22, 147, 1, 155, 6, 34, 0, 147, 2, 35, 128, 146, 0, + 28, 48, 33, 128, 34, 6, 0, 91, 247, 255, 254, 69, 40, 0, 218, 12, 72, 109, 247, 255, 251, 198, 32, 50, 247, 254, 254, + 189, 61, 1, 32, 100, 247, 254, 254, 185, 45, 0, 209, 222, 224, 2, 154, 5, 24, 187, 224, 38, 28, 32, 247, 255, 249, 106, + 75, 100, 66, 152, 209, 0, 224, 184, 32, 100, 247, 254, 254, 169, 72, 98, 247, 255, 251, 172, 35, 22, 147, 1, 155, 6, + 34, 0, 147, 2, 35, 128, 146, 0, 28, 48, 33, 128, 34, 6, 0, 91, 247, 255, 254, 25, 40, 0, 218, 6, 72, 90, 53, 1, 247, + 255, 251, 153, 45, 3, 209, 221, 224, 12, 0, 163, 25, 27, 0, 219, 24, 251, 110, 26, 75, 85, 66, 154, 209, 46, 28, 32, + 247, 255, 249, 199, 76, 83, 224, 144, 28, 32, 247, 255, 249, 14, 32, 100, 247, 254, 254, 125, 28, 32, 247, 255, 249, + 52, 74, 73, 66, 144, 209, 0, 224, 130, 32, 100, 247, 254, 254, 115, 72, 75, 247, 255, 251, 118, 35, 0, 154, 6, 147, + 0, 35, 22, 147, 1, 35, 128, 146, 2, 28, 48, 33, 128, 34, 6, 0, 91, 247, 255, 253, 227, 40, 0, 218, 7, 72, 63, 61, 1, + 247, 255, 251, 99, 45, 0, 209, 215, 76, 64, 224, 102, 72, 64, 247, 255, 251, 92, 28, 99, 147, 5, 4, 27, 12, 27, 147, + 7, 0, 163, 25, 28, 0, 228, 37, 0, 25, 63, 32, 50, 247, 254, 254, 73, 72, 57, 28, 41, 247, 255, 251, 75, 35, 0, 147, + 0, 147, 1, 147, 2, 28, 48, 33, 0, 34, 5, 155, 7, 247, 255, 253, 187, 30, 4, 218, 7, 76, 50, 28, 41, 72, 50, 27, 100, + 247, 255, 251, 57, 53, 1, 224, 3, 72, 48, 28, 41, 247, 255, 251, 51, 155, 5, 34, 0, 103, 250, 103, 59, 44, 0, 218, 47, + 33, 0, 28, 48, 247, 255, 254, 5, 32, 50, 247, 254, 254, 32, 72, 40, 247, 255, 251, 35, 35, 22, 147, 1, 155, 6, 34, 0, + 147, 2, 35, 128, 146, 0, 28, 48, 33, 128, 34, 6, 0, 91, 247, 255, 253, 144, 30, 4, 218, 6, 72, 32, 247, 255, 251, 17, + 34, 0, 103, 58, 76, 31, 224, 2, 72, 31, 247, 255, 251, 10, 53, 1, 44, 0, 218, 7, 15, 233, 34, 0, 35, 4, 66, 171, 65, + 74, 6, 18, 42, 0, 209, 172, 44, 0, 219, 4, 72, 24, 247, 255, 250, 250, 224, 0, 76, 6, 176, 9, 28, 32, 188, 240, 188, + 2, 71, 8, 70, 192, 19, 112, 96, 60, 19, 112, 97, 75, 19, 112, 97, 0, 255, 255, 251, 161, 19, 112, 97, 30, 19, 112, 97, + 69, 149, 11, 32, 119, 255, 255, 251, 160, 19, 112, 97, 106, 255, 255, 247, 103, 19, 112, 97, 155, 19, 112, 97, 181, + 255, 255, 224, 192, 19, 112, 97, 212, 19, 112, 97, 243, 19, 112, 98, 55, 19, 112, 98, 14, 255, 255, 247, 62, 19, 112, + 98, 52, 19, 112, 98, 87, 181, 16, 75, 10, 34, 1, 104, 27, 28, 4, 104, 155, 96, 154, 247, 255, 248, 117, 40, 0, 219, + 2, 28, 32, 247, 255, 254, 202, 75, 3, 34, 4, 104, 27, 104, 155, 96, 154, 188, 16, 188, 2, 71, 8, 19, 112, 96, 60, 181, + 240, 176, 141, 144, 6, 32, 22, 28, 13, 240, 2, 249, 151, 30, 7, 209, 0, 224, 214, 32, 10, 247, 254, 253, 159, 35, 18, + 147, 0, 152, 6, 28, 57, 34, 1, 35, 0, 247, 255, 253, 197, 40, 0, 218, 54, 72, 180, 247, 255, 250, 152, 76, 179, 104, + 32, 247, 255, 255, 202, 28, 6, 32, 20, 247, 254, 253, 138, 75, 176, 104, 32, 104, 27, 0, 130, 104, 155, 51, 68, 24, + 155, 104, 26, 46, 0, 219, 4, 75, 172, 64, 26, 75, 172, 66, 154, 208, 13, 247, 255, 254, 81, 32, 20, 247, 254, 253, 118, + 75, 166, 104, 27, 104, 154, 75, 164, 50, 68, 104, 27, 0, 155, 24, 211, 104, 27, 32, 30, 247, 254, 253, 106, 35, 18, + 147, 0, 152, 6, 28, 57, 34, 1, 35, 0, 247, 255, 253, 144, 30, 4, 218, 1, 72, 158, 224, 165, 28, 57, 34, 18, 28, 40, + 240, 3, 249, 9, 28, 56, 240, 2, 249, 102, 120, 170, 120, 235, 2, 18, 67, 26, 4, 18, 10, 19, 14, 18, 67, 19, 4, 27, 12, + 26, 112, 234, 14, 27, 122, 42, 112, 171, 122, 107, 2, 18, 67, 26, 4, 18, 10, 19, 14, 18, 67, 19, 4, 27, 12, 26, 114, + 106, 14, 27, 122, 170, 114, 43, 122, 235, 2, 18, 67, 26, 4, 18, 10, 19, 14, 18, 67, 19, 4, 27, 12, 26, 114, 234, 14, + 27, 123, 42, 114, 171, 123, 107, 2, 18, 67, 26, 4, 18, 10, 19, 14, 18, 67, 19, 4, 27, 12, 26, 14, 27, 115, 43, 124, + 107, 115, 106, 0, 88, 24, 192, 0, 128, 24, 192, 240, 2, 249, 13, 14, 3, 116, 171, 12, 3, 116, 235, 10, 3, 117, 43, 117, + 104, 40, 0, 209, 1, 72, 120, 224, 204, 124, 107, 33, 0, 0, 90, 24, 210, 0, 146, 24, 210, 240, 3, 248, 250, 33, 0, 145, + 10, 145, 5, 28, 46, 225, 51, 32, 9, 240, 2, 248, 242, 30, 4, 208, 49, 154, 5, 28, 33, 6, 19, 14, 27, 147, 3, 35, 9, + 147, 0, 34, 2, 155, 3, 152, 6, 247, 255, 253, 32, 124, 179, 124, 245, 6, 27, 4, 45, 67, 29, 125, 51, 28, 33, 2, 27, + 67, 29, 125, 115, 34, 9, 67, 29, 155, 10, 24, 237, 28, 40, 240, 3, 248, 145, 28, 32, 240, 2, 248, 238, 120, 170, 120, + 235, 2, 18, 67, 26, 4, 18, 10, 19, 14, 18, 67, 19, 4, 27, 12, 28, 14, 27, 112, 171, 112, 236, 28, 32, 240, 2, 248, 192, + 30, 7, 209, 6, 28, 53, 72, 86, 36, 4, 247, 255, 249, 205, 66, 100, 225, 3, 120, 235, 152, 6, 147, 0, 28, 57, 34, 2, + 155, 3, 247, 255, 252, 235, 40, 0, 218, 5, 28, 4, 72, 78, 28, 53, 247, 255, 249, 188, 224, 238, 120, 41, 145, 2, 121, + 43, 0, 216, 24, 192, 0, 64, 240, 2, 248, 159, 14, 3, 114, 107, 12, 3, 114, 171, 10, 3, 114, 235, 115, 40, 40, 0, 209, + 2, 28, 53, 72, 68, 224, 93, 155, 2, 154, 2, 26, 228, 25, 210, 146, 3, 148, 4, 121, 43, 33, 0, 0, 218, 24, 210, 0, 82, + 240, 3, 248, 134, 33, 0, 145, 11, 145, 8, 224, 177, 122, 107, 122, 172, 6, 27, 4, 36, 67, 28, 122, 235, 154, 11, 2, + 27, 67, 28, 123, 43, 153, 3, 67, 28, 24, 164, 28, 32, 34, 9, 240, 3, 248, 47, 120, 35, 147, 2, 121, 35, 0, 216, 26, + 192, 240, 2, 248, 106, 14, 3, 115, 163, 12, 3, 115, 227, 10, 3, 116, 35, 116, 96, 40, 0, 209, 2, 28, 53, 72, 42, 224, + 40, 153, 3, 155, 4, 28, 10, 153, 2, 24, 82, 153, 2, 146, 3, 26, 91, 147, 4, 121, 35, 33, 0, 0, 218, 26, 210, 240, 3, + 248, 79, 152, 3, 153, 4, 247, 255, 248, 33, 35, 11, 144, 2, 70, 106, 92, 154, 115, 98, 155, 2, 43, 0, 208, 31, 28, 24, + 240, 2, 248, 64, 14, 3, 114, 99, 12, 3, 114, 163, 10, 3, 114, 227, 115, 32, 40, 0, 209, 6, 72, 23, 28, 53, 36, 4, 247, + 255, 249, 70, 66, 100, 224, 119, 153, 3, 154, 2, 240, 2, 255, 234, 153, 3, 155, 4, 28, 10, 153, 2, 24, 82, 153, 2, 146, + 3, 26, 91, 147, 4, 34, 0, 146, 7, 146, 9, 224, 68, 70, 192, 19, 112, 98, 121, 19, 112, 107, 80, 19, 112, 96, 60, 0, + 0, 49, 5, 0, 0, 16, 5, 19, 112, 98, 163, 19, 112, 98, 198, 19, 112, 98, 96, 19, 112, 98, 236, 19, 112, 99, 22, 19, 112, + 99, 56, 19, 112, 99, 89, 123, 162, 123, 227, 6, 18, 4, 27, 67, 19, 124, 34, 153, 7, 2, 18, 67, 19, 124, 98, 67, 19, + 24, 201, 145, 2, 28, 8, 34, 7, 153, 3, 240, 2, 255, 178, 154, 2, 153, 3, 120, 19, 24, 201, 145, 3, 153, 2, 121, 18, + 121, 75, 2, 18, 67, 26, 4, 18, 10, 17, 14, 19, 67, 11, 4, 27, 153, 2, 12, 26, 14, 27, 113, 11, 113, 74, 154, 9, 155, + 7, 50, 1, 51, 7, 146, 9, 147, 7, 121, 35, 153, 9, 66, 153, 211, 207, 154, 8, 155, 11, 50, 1, 51, 18, 146, 8, 147, 11, + 121, 43, 153, 8, 66, 153, 210, 0, 231, 72, 28, 56, 240, 1, 255, 230, 154, 5, 155, 10, 50, 1, 51, 13, 146, 5, 147, 10, + 39, 0, 124, 115, 153, 5, 66, 153, 210, 0, 230, 198, 28, 53, 36, 0, 47, 0, 208, 7, 28, 56, 240, 1, 255, 211, 44, 0, 208, + 2, 28, 40, 247, 254, 255, 151, 176, 13, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 181, 248, 28, 4, 38, 3, 39, 64, 224, + 79, 104, 160, 240, 2, 255, 38, 10, 3, 64, 51, 28, 5, 43, 2, 208, 7, 107, 227, 108, 34, 105, 89, 24, 138, 0, 65, 12, + 73, 26, 82, 97, 90, 108, 35, 43, 0, 208, 59, 66, 47, 208, 57, 75, 36, 120, 27, 43, 0, 208, 2, 72, 35, 247, 255, 248, + 157, 6, 235, 213, 6, 75, 32, 120, 27, 43, 0, 208, 2, 72, 32, 247, 255, 248, 148, 7, 107, 213, 6, 75, 27, 120, 27, 43, + 0, 208, 2, 72, 28, 247, 255, 248, 139, 6, 171, 213, 6, 75, 23, 120, 27, 43, 0, 208, 2, 72, 25, 247, 255, 248, 130, 7, + 43, 213, 6, 75, 18, 120, 27, 43, 0, 208, 2, 72, 21, 247, 255, 248, 121, 75, 15, 120, 27, 43, 0, 208, 2, 72, 19, 247, + 255, 248, 114, 75, 11, 120, 27, 43, 0, 208, 8, 72, 16, 247, 255, 248, 107, 224, 4, 107, 164, 44, 0, 209, 173, 32, 0, + 224, 3, 107, 227, 32, 1, 66, 64, 97, 88, 75, 11, 34, 0, 104, 27, 100, 26, 188, 248, 188, 2, 71, 8, 19, 112, 106, 248, + 19, 112, 99, 118, 19, 112, 99, 131, 19, 112, 99, 139, 19, 112, 99, 160, 19, 112, 99, 179, 19, 112, 99, 190, 19, 112, + 105, 116, 19, 112, 96, 60, 181, 240, 176, 131, 28, 5, 35, 9, 7, 193, 213, 60, 75, 58, 120, 27, 43, 0, 208, 2, 72, 57, + 247, 255, 248, 59, 75, 57, 104, 28, 44, 0, 208, 9, 107, 96, 33, 96, 240, 3, 248, 79, 107, 96, 33, 96, 240, 1, 254, 239, + 107, 164, 231, 243, 76, 51, 104, 35, 43, 0, 208, 24, 108, 88, 33, 32, 240, 3, 248, 65, 104, 35, 33, 32, 108, 88, 240, + 1, 254, 224, 104, 35, 34, 128, 4, 82, 97, 26, 74, 43, 108, 88, 104, 18, 33, 32, 107, 82, 97, 90, 240, 3, 248, 32, 104, + 35, 105, 154, 75, 39, 96, 26, 75, 35, 104, 24, 247, 255, 255, 78, 35, 0, 40, 0, 208, 4, 74, 36, 104, 18, 100, 19, 35, + 5, 66, 91, 7, 106, 213, 40, 75, 26, 120, 27, 43, 0, 208, 2, 72, 31, 247, 254, 255, 251, 33, 128, 1, 137, 79, 28, 78, + 29, 35, 1, 34, 0, 145, 1, 104, 56, 104, 49, 104, 128, 0, 148, 48, 68, 25, 0, 70, 140, 104, 4, 69, 98, 209, 7, 32, 1, + 66, 32, 209, 10, 75, 22, 96, 24, 35, 1, 66, 91, 224, 5, 73, 20, 64, 12, 44, 3, 209, 1, 153, 1, 96, 1, 50, 1, 42, 4, + 209, 228, 43, 0, 208, 4, 34, 18, 66, 42, 208, 1, 35, 6, 66, 91, 176, 3, 28, 24, 188, 240, 188, 2, 71, 8, 70, 192, 19, + 112, 106, 248, 19, 112, 99, 207, 19, 112, 107, 20, 19, 112, 106, 252, 19, 112, 107, 8, 19, 112, 107, 4, 19, 112, 96, + 60, 19, 112, 99, 240, 19, 112, 107, 80, 19, 112, 107, 84, 0, 0, 32, 3, 181, 240, 176, 131, 147, 1, 70, 107, 51, 39, + 120, 30, 28, 5, 30, 115, 6, 27, 14, 27, 145, 0, 28, 23, 43, 15, 216, 61, 28, 4, 52, 160, 104, 32, 40, 0, 208, 56, 33, + 0, 34, 31, 240, 2, 254, 139, 104, 35, 74, 29, 96, 26, 28, 43, 51, 152, 104, 24, 240, 2, 254, 7, 104, 35, 96, 88, 28, + 56, 240, 2, 254, 2, 104, 35, 70, 105, 96, 152, 29, 202, 120, 17, 104, 35, 70, 106, 115, 25, 28, 209, 104, 35, 120, 10, + 115, 90, 104, 34, 35, 6, 46, 6, 217, 0, 35, 10, 28, 44, 115, 147, 52, 160, 104, 32, 153, 8, 28, 50, 48, 15, 240, 2, + 254, 34, 28, 43, 51, 144, 104, 24, 123, 105, 104, 35, 34, 31, 247, 255, 249, 214, 40, 31, 208, 6, 40, 0, 219, 5, 72, + 5, 224, 3, 32, 3, 66, 64, 224, 0, 32, 0, 176, 3, 188, 240, 188, 2, 71, 8, 85, 83, 66, 67, 255, 255, 216, 238, 181, 240, + 28, 4, 176, 131, 52, 160, 28, 5, 145, 0, 28, 23, 33, 255, 34, 13, 104, 32, 240, 2, 254, 62, 28, 43, 51, 144, 104, 24, + 123, 41, 104, 35, 34, 13, 247, 255, 249, 176, 40, 13, 208, 1, 40, 0, 218, 40, 40, 0, 219, 43, 28, 43, 51, 160, 104, + 28, 104, 32, 240, 2, 253, 174, 144, 1, 104, 96, 240, 2, 253, 170, 28, 6, 104, 160, 240, 2, 253, 166, 74, 16, 153, 1, + 123, 35, 66, 145, 209, 21, 47, 0, 208, 0, 96, 56, 154, 0, 42, 0, 208, 0, 112, 19, 53, 152, 104, 43, 66, 158, 209, 12, + 28, 179, 4, 27, 12, 54, 12, 27, 4, 54, 67, 30, 96, 46, 32, 0, 224, 4, 72, 5, 224, 2, 72, 5, 224, 0, 72, 5, 176, 3, 188, + 240, 188, 2, 71, 8, 83, 66, 83, 85, 255, 255, 216, 237, 255, 255, 216, 236, 255, 255, 216, 235, 181, 240, 176, 135, + 74, 75, 75, 76, 120, 18, 28, 6, 104, 31, 42, 0, 208, 1, 73, 74, 224, 0, 73, 74, 96, 25, 28, 51, 51, 144, 104, 24, 75, + 72, 104, 27, 104, 153, 105, 195, 49, 68, 0, 155, 24, 203, 104, 25, 35, 1, 66, 11, 209, 6, 74, 68, 36, 1, 96, 19, 75, + 62, 66, 100, 96, 31, 224, 113, 75, 60, 42, 0, 208, 1, 74, 60, 224, 0, 74, 60, 96, 26, 75, 62, 120, 27, 43, 0, 208, 17, + 136, 243, 34, 255, 147, 0, 33, 33, 35, 0, 147, 1, 147, 2, 247, 255, 249, 80, 28, 4, 28, 33, 72, 55, 247, 254, 254, 209, + 32, 60, 247, 254, 249, 200, 224, 6, 33, 0, 247, 255, 249, 166, 32, 5, 247, 254, 249, 193, 36, 0, 75, 41, 120, 26, 75, + 41, 42, 0, 208, 1, 74, 41, 224, 0, 74, 41, 28, 53, 96, 26, 53, 144, 44, 0, 218, 10, 32, 50, 247, 254, 249, 176, 104, + 40, 33, 0, 247, 255, 249, 142, 32, 5, 247, 254, 249, 169, 224, 48, 123, 49, 104, 40, 247, 255, 249, 134, 28, 4, 32, + 5, 247, 254, 249, 160, 72, 33, 28, 33, 247, 254, 254, 162, 44, 0, 219, 34, 123, 113, 104, 40, 247, 255, 249, 120, 28, + 4, 32, 5, 247, 254, 249, 146, 72, 27, 28, 33, 247, 254, 254, 148, 44, 0, 219, 20, 32, 10, 247, 254, 249, 137, 70, 105, + 49, 23, 104, 40, 247, 255, 249, 143, 28, 4, 28, 33, 72, 20, 247, 254, 254, 133, 44, 0, 219, 5, 72, 19, 247, 254, 254, + 128, 75, 7, 96, 31, 224, 4, 75, 6, 34, 1, 96, 31, 75, 8, 96, 26, 176, 7, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 19, + 112, 1, 15, 19, 112, 96, 56, 0, 15, 66, 64, 0, 3, 13, 64, 19, 112, 96, 60, 19, 112, 107, 84, 19, 112, 1, 13, 19, 112, + 100, 17, 19, 112, 100, 50, 19, 112, 100, 89, 19, 112, 100, 129, 19, 112, 100, 176, 181, 240, 176, 141, 146, 6, 70, 106, + 50, 79, 120, 18, 28, 31, 70, 107, 51, 95, 146, 7, 70, 106, 120, 27, 50, 83, 120, 18, 6, 27, 22, 27, 146, 8, 70, 106, + 50, 47, 147, 4, 35, 0, 112, 19, 74, 147, 77, 148, 28, 6, 145, 5, 147, 10, 96, 19, 147, 3, 36, 0, 28, 163, 43, 1, 216, + 0, 225, 6, 44, 0, 208, 33, 28, 48, 33, 0, 247, 255, 255, 38, 30, 4, 219, 1, 32, 5, 224, 0, 32, 60, 247, 254, 249, 37, + 75, 137, 104, 27, 104, 154, 75, 136, 50, 68, 104, 27, 0, 155, 24, 211, 104, 27, 44, 0, 218, 9, 154, 3, 50, 1, 146, 3, + 42, 2, 221, 0, 224, 232, 32, 10, 247, 254, 249, 17, 224, 219, 35, 0, 147, 3, 155, 4, 154, 8, 59, 1, 6, 27, 22, 27, 147, + 4, 42, 0, 208, 59, 104, 43, 74, 123, 147, 2, 120, 19, 43, 0, 208, 1, 75, 121, 224, 0, 75, 121, 96, 43, 154, 7, 155, + 18, 146, 1, 147, 0, 28, 58, 35, 0, 28, 48, 153, 5, 247, 255, 254, 66, 75, 110, 154, 2, 28, 4, 96, 26, 28, 131, 43, 1, + 216, 0, 224, 190, 40, 0, 218, 0, 224, 178, 155, 6, 34, 144, 25, 146, 28, 60, 147, 2, 146, 9, 224, 18, 155, 9, 123, 113, + 104, 24, 28, 34, 155, 2, 247, 255, 248, 69, 28, 131, 43, 1, 216, 0, 224, 159, 40, 0, 219, 69, 66, 160, 209, 67, 154, + 2, 26, 36, 24, 18, 146, 2, 44, 0, 209, 234, 224, 62, 104, 43, 74, 93, 147, 2, 120, 19, 43, 0, 208, 1, 75, 91, 224, 0, + 75, 91, 96, 43, 154, 7, 155, 18, 146, 1, 147, 0, 28, 58, 35, 128, 28, 48, 153, 5, 247, 255, 254, 6, 75, 80, 154, 2, + 28, 4, 96, 26, 40, 0, 218, 3, 72, 83, 28, 33, 247, 254, 253, 178, 28, 163, 43, 1, 217, 125, 44, 0, 218, 22, 224, 113, + 28, 51, 51, 144, 104, 24, 123, 49, 28, 58, 155, 6, 247, 255, 248, 10, 30, 4, 218, 3, 72, 74, 28, 33, 247, 254, 253, + 158, 28, 163, 43, 1, 217, 96, 44, 0, 219, 5, 66, 188, 208, 5, 224, 2, 47, 0, 209, 231, 224, 1, 76, 68, 224, 86, 75, + 59, 74, 61, 104, 27, 147, 2, 120, 19, 43, 0, 208, 1, 75, 59, 224, 0, 75, 59, 74, 54, 70, 105, 96, 19, 28, 48, 49, 47, + 170, 10, 247, 255, 254, 33, 30, 4, 218, 34, 72, 58, 28, 33, 247, 254, 253, 121, 28, 99, 209, 3, 75, 46, 154, 2, 96, + 26, 224, 65, 28, 52, 52, 144, 104, 32, 123, 49, 247, 255, 248, 73, 123, 51, 32, 15, 104, 34, 64, 24, 9, 219, 1, 27, + 106, 81, 24, 27, 32, 1, 64, 152, 67, 129, 98, 81, 70, 105, 28, 48, 49, 47, 170, 10, 247, 255, 253, 253, 28, 4, 75, 31, + 154, 2, 96, 26, 28, 163, 43, 1, 217, 34, 155, 10, 43, 0, 208, 7, 75, 37, 104, 27, 43, 0, 208, 3, 70, 107, 34, 1, 51, + 47, 112, 26, 44, 0, 219, 12, 75, 32, 36, 0, 104, 27, 43, 0, 208, 7, 70, 107, 51, 47, 120, 27, 43, 0, 208, 2, 76, 28, + 224, 0, 28, 4, 155, 4, 43, 0, 221, 2, 44, 0, 218, 0, 230, 247, 34, 0, 44, 0, 218, 0, 34, 1, 75, 9, 96, 26, 154, 21, + 42, 0, 208, 3, 70, 107, 51, 47, 120, 27, 112, 19, 155, 22, 43, 0, 208, 2, 155, 10, 154, 22, 96, 19, 176, 13, 28, 32, + 188, 240, 188, 2, 71, 8, 19, 112, 107, 84, 19, 112, 96, 56, 19, 112, 96, 60, 19, 112, 107, 80, 19, 112, 1, 15, 0, 15, + 66, 64, 0, 3, 13, 64, 19, 112, 100, 186, 19, 112, 100, 205, 255, 255, 216, 233, 19, 112, 100, 229, 19, 112, 107, 28, + 255, 255, 216, 234, 181, 240, 28, 3, 176, 143, 51, 160, 145, 7, 104, 30, 33, 128, 70, 111, 1, 9, 55, 55, 37, 0, 172, + 9, 24, 118, 144, 6, 112, 61, 28, 32, 33, 0, 34, 16, 240, 2, 251, 218, 46, 0, 208, 87, 35, 6, 147, 1, 35, 1, 147, 2, + 35, 10, 147, 5, 153, 7, 35, 0, 152, 6, 34, 0, 149, 4, 148, 0, 151, 3, 247, 255, 254, 131, 28, 5, 28, 41, 72, 40, 247, + 254, 252, 216, 28, 107, 208, 69, 45, 0, 219, 2, 120, 59, 43, 0, 208, 64, 153, 7, 171, 9, 34, 3, 112, 26, 1, 74, 112, + 90, 34, 18, 113, 26, 34, 0, 113, 90, 28, 48, 33, 0, 34, 18, 240, 2, 251, 174, 75, 28, 53, 2, 209, 1, 74, 28, 224, 0, + 74, 28, 96, 26, 171, 9, 147, 0, 35, 6, 147, 1, 35, 0, 147, 2, 147, 3, 147, 4, 35, 10, 147, 5, 153, 7, 152, 6, 28, 50, + 35, 18, 247, 255, 254, 81, 28, 5, 28, 41, 72, 19, 247, 254, 252, 166, 45, 0, 219, 19, 120, 179, 33, 15, 70, 108, 64, + 25, 52, 55, 72, 15, 112, 33, 247, 254, 252, 155, 120, 35, 59, 2, 6, 27, 14, 27, 43, 2, 217, 3, 224, 3, 37, 4, 66, 109, + 224, 0, 77, 9, 176, 15, 28, 40, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 100, 244, 19, 112, 96, 56, 0, 38, 37, 160, + 0, 152, 150, 128, 19, 112, 101, 21, 19, 112, 101, 52, 255, 255, 216, 239, 181, 240, 176, 147, 70, 106, 35, 0, 50, 71, + 112, 19, 74, 197, 28, 4, 32, 16, 112, 16, 72, 196, 28, 34, 50, 152, 96, 16, 174, 12, 58, 8, 96, 17, 28, 8, 130, 115, + 28, 49, 130, 179, 247, 255, 249, 179, 28, 5, 28, 41, 72, 190, 247, 254, 252, 96, 45, 0, 218, 0, 225, 195, 75, 188, 104, + 27, 43, 0, 208, 37, 75, 187, 121, 50, 121, 153, 66, 145, 209, 23, 121, 217, 121, 114, 66, 145, 209, 19, 137, 25, 137, + 50, 66, 145, 209, 15, 137, 89, 137, 114, 66, 145, 209, 11, 123, 25, 123, 178, 66, 145, 209, 7, 123, 89, 123, 242, 66, + 145, 209, 3, 123, 154, 124, 51, 66, 154, 208, 8, 168, 12, 247, 254, 251, 12, 37, 1, 72, 171, 247, 254, 252, 53, 66, + 109, 225, 167, 170, 12, 121, 17, 75, 167, 38, 0, 113, 153, 121, 81, 113, 217, 137, 17, 129, 25, 137, 81, 129, 89, 123, + 145, 115, 25, 123, 209, 115, 89, 124, 18, 115, 154, 74, 161, 35, 128, 66, 91, 96, 19, 35, 0, 147, 10, 147, 5, 224, 221, + 138, 111, 138, 171, 154, 10, 4, 63, 67, 31, 24, 191, 122, 58, 72, 155, 0, 82, 153, 5, 247, 254, 252, 14, 34, 156, 35, + 0, 25, 18, 147, 9, 147, 6, 146, 11, 224, 191, 122, 123, 122, 189, 6, 27, 4, 45, 67, 29, 122, 251, 2, 27, 67, 29, 123, + 59, 67, 29, 155, 9, 24, 237, 121, 107, 43, 8, 209, 115, 121, 235, 43, 80, 209, 112, 121, 43, 43, 1, 217, 109, 154, 11, + 35, 1, 112, 19, 121, 169, 72, 137, 34, 1, 247, 254, 251, 234, 35, 0, 115, 102, 115, 38, 147, 8, 147, 7, 224, 49, 123, + 170, 123, 235, 6, 18, 4, 27, 67, 19, 124, 42, 2, 18, 67, 19, 124, 106, 67, 19, 154, 8, 24, 155, 120, 218, 42, 2, 209, + 28, 120, 154, 6, 17, 41, 0, 218, 11, 123, 33, 41, 0, 209, 8, 42, 0, 208, 6, 115, 34, 121, 25, 121, 91, 2, 9, 67, 25, + 72, 119, 224, 10, 123, 97, 41, 0, 209, 9, 42, 0, 208, 7, 115, 98, 121, 25, 121, 91, 2, 9, 72, 114, 67, 25, 247, 254, + 251, 184, 155, 7, 154, 8, 51, 1, 50, 7, 147, 7, 146, 8, 121, 43, 154, 7, 66, 154, 211, 201, 123, 33, 41, 0, 208, 93, + 123, 98, 42, 0, 208, 90, 72, 105, 247, 254, 251, 165, 75, 97, 123, 34, 168, 12, 116, 26, 123, 98, 116, 90, 121, 122, + 112, 34, 120, 170, 96, 98, 115, 218, 120, 235, 96, 163, 247, 254, 250, 105, 75, 91, 74, 97, 72, 97, 96, 26, 120, 33, + 104, 162, 247, 254, 251, 142, 28, 35, 51, 144, 70, 105, 104, 24, 49, 71, 247, 254, 254, 140, 40, 0, 218, 89, 224, 85, + 123, 170, 123, 235, 6, 18, 4, 27, 67, 19, 124, 42, 124, 104, 2, 18, 67, 19, 67, 24, 208, 1, 240, 1, 250, 129, 122, 106, + 122, 171, 6, 18, 4, 27, 67, 19, 122, 234, 123, 40, 2, 18, 67, 19, 115, 174, 115, 238, 116, 46, 116, 110, 67, 24, 208, + 1, 240, 1, 250, 112, 121, 107, 114, 110, 114, 174, 114, 238, 115, 46, 43, 9, 209, 4, 74, 72, 75, 63, 168, 12, 96, 26, + 224, 39, 43, 8, 209, 10, 121, 235, 43, 80, 209, 7, 121, 43, 43, 1, 217, 4, 121, 171, 74, 66, 26, 211, 74, 56, 96, 19, + 155, 6, 154, 9, 51, 1, 50, 18, 147, 6, 146, 9, 121, 59, 154, 6, 66, 154, 210, 0, 231, 58, 155, 5, 154, 10, 51, 1, 50, + 13, 147, 5, 146, 10, 173, 12, 124, 107, 154, 5, 66, 154, 210, 0, 231, 27, 72, 53, 247, 254, 251, 51, 28, 40, 247, 254, + 250, 3, 77, 49, 224, 148, 72, 50, 247, 254, 251, 43, 70, 107, 51, 71, 120, 25, 120, 34, 72, 48, 247, 254, 251, 36, 75, + 34, 74, 47, 96, 26, 28, 35, 51, 144, 104, 24, 120, 33, 247, 254, 254, 18, 40, 0, 218, 2, 72, 43, 247, 254, 251, 22, + 74, 42, 75, 27, 121, 225, 96, 26, 28, 35, 51, 144, 104, 24, 122, 226, 247, 254, 253, 245, 40, 0, 218, 2, 72, 37, 247, + 254, 251, 7, 72, 37, 70, 109, 247, 254, 251, 3, 53, 70, 35, 0, 112, 43, 28, 35, 51, 144, 104, 24, 136, 227, 33, 161, + 147, 0, 35, 1, 147, 1, 34, 254, 35, 0, 149, 2, 247, 254, 253, 109, 40, 0, 218, 56, 72, 27, 247, 254, 250, 238, 35, 8, + 115, 163, 224, 58, 70, 192, 19, 112, 96, 64, 44, 13, 224, 1, 19, 112, 101, 86, 19, 112, 107, 56, 19, 112, 107, 36, 19, + 112, 101, 128, 19, 112, 107, 0, 19, 112, 101, 166, 19, 112, 101, 205, 19, 112, 102, 4, 19, 112, 102, 18, 19, 112, 102, + 33, 255, 255, 251, 79, 19, 112, 102, 102, 255, 255, 177, 224, 255, 255, 216, 240, 19, 112, 102, 53, 19, 112, 102, 148, + 19, 112, 102, 189, 255, 255, 251, 78, 19, 112, 102, 222, 255, 255, 251, 77, 19, 112, 102, 252, 19, 112, 103, 43, 19, + 112, 103, 102, 120, 41, 72, 24, 49, 1, 6, 9, 14, 9, 115, 161, 247, 254, 250, 176, 28, 37, 53, 160, 104, 43, 43, 0, 209, + 7, 32, 129, 1, 64, 240, 1, 249, 147, 35, 31, 48, 31, 67, 152, 96, 40, 52, 160, 104, 35, 37, 0, 43, 0, 209, 4, 74, 12, + 75, 13, 37, 4, 96, 26, 66, 109, 74, 12, 75, 12, 96, 26, 45, 0, 208, 5, 75, 8, 72, 11, 104, 25, 247, 254, 250, 143, 224, + 2, 72, 9, 247, 254, 250, 139, 176, 19, 28, 40, 188, 240, 188, 2, 71, 8, 19, 112, 103, 141, 255, 255, 251, 75, 19, 112, + 107, 0, 0, 15, 66, 64, 19, 112, 96, 56, 19, 112, 103, 164, 19, 112, 103, 198, 181, 8, 33, 0, 247, 255, 251, 104, 188, + 8, 188, 2, 71, 8, 0, 0, 181, 240, 176, 139, 171, 8, 34, 18, 112, 26, 1, 74, 112, 90, 34, 0, 112, 154, 112, 218, 113, + 90, 28, 15, 28, 2, 33, 36, 50, 160, 113, 25, 104, 20, 34, 128, 1, 18, 24, 164, 144, 7, 44, 0, 208, 42, 38, 0, 33, 0, + 34, 36, 28, 32, 240, 2, 249, 63, 171, 8, 34, 6, 147, 0, 35, 10, 37, 0, 146, 1, 147, 5, 152, 7, 28, 57, 28, 34, 35, 36, + 149, 2, 149, 3, 149, 4, 247, 255, 251, 233, 40, 0, 219, 7, 120, 35, 34, 31, 64, 19, 43, 5, 208, 6, 43, 7, 209, 8, 224, + 3, 54, 1, 46, 2, 208, 9, 231, 220, 75, 6, 34, 1, 96, 26, 224, 4, 75, 4, 96, 29, 224, 1, 32, 4, 66, 64, 176, 11, 188, + 240, 188, 2, 71, 8, 19, 112, 107, 12, 181, 112, 176, 136, 28, 22, 28, 29, 34, 37, 171, 7, 112, 26, 1, 74, 112, 90, 28, + 2, 50, 160, 104, 20, 34, 128, 1, 18, 24, 164, 44, 0, 208, 42, 147, 0, 35, 2, 147, 1, 35, 0, 147, 2, 147, 3, 147, 4, + 35, 10, 147, 5, 28, 34, 35, 8, 247, 255, 251, 174, 40, 0, 219, 29, 168, 6, 28, 33, 34, 4, 240, 2, 248, 171, 45, 0, 208, + 1, 155, 6, 96, 43, 29, 33, 168, 6, 34, 4, 240, 2, 248, 162, 46, 0, 208, 1, 155, 6, 96, 51, 35, 128, 154, 6, 1, 27, 32, + 0, 66, 154, 209, 5, 75, 4, 34, 1, 96, 26, 224, 1, 32, 4, 66, 64, 176, 8, 188, 112, 188, 2, 71, 8, 19, 112, 107, 12, + 181, 240, 176, 131, 123, 131, 28, 15, 28, 5, 66, 187, 217, 72, 78, 40, 73, 40, 72, 41, 96, 49, 33, 0, 247, 254, 249, + 212, 75, 39, 28, 40, 96, 51, 28, 57, 247, 255, 252, 204, 30, 4, 219, 53, 74, 33, 28, 57, 96, 50, 28, 40, 247, 255, 255, + 86, 28, 4, 28, 33, 72, 32, 247, 254, 249, 193, 44, 0, 219, 40, 28, 43, 51, 160, 104, 26, 35, 128, 1, 27, 92, 211, 33, + 31, 64, 25, 72, 27, 247, 254, 249, 180, 28, 59, 29, 62, 51, 20, 0, 182, 0, 155, 25, 170, 147, 1, 28, 57, 24, 235, 28, + 40, 247, 255, 255, 130, 153, 1, 28, 4, 89, 75, 89, 114, 72, 18, 28, 33, 247, 254, 249, 160, 89, 114, 75, 17, 66, 154, + 217, 3, 154, 1, 89, 83, 43, 9, 216, 1, 36, 33, 66, 100, 74, 6, 75, 5, 96, 26, 224, 1, 36, 3, 66, 100, 176, 3, 28, 32, + 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 96, 56, 0, 15, 66, 64, 19, 112, 103, 231, 1, 49, 45, 0, 19, 112, 104, 2, + 19, 112, 104, 22, 19, 112, 104, 43, 0, 0, 1, 255, 181, 240, 176, 141, 175, 9, 37, 40, 112, 61, 1, 77, 112, 125, 14, + 21, 112, 189, 70, 110, 12, 21, 36, 0, 112, 253, 113, 122, 10, 21, 54, 47, 10, 26, 112, 52, 113, 61, 113, 188, 113, 250, + 114, 59, 114, 124, 123, 130, 66, 138, 217, 28, 29, 10, 0, 146, 88, 18, 146, 7, 42, 0, 208, 22, 77, 15, 34, 1, 96, 42, + 154, 7, 151, 0, 67, 83, 34, 10, 146, 1, 34, 6, 146, 5, 154, 18, 148, 2, 150, 3, 148, 4, 247, 255, 250, 240, 96, 44, + 40, 0, 221, 7, 120, 51, 43, 0, 209, 3, 224, 3, 32, 3, 66, 64, 224, 0, 72, 3, 176, 13, 188, 240, 188, 2, 71, 8, 70, 192, + 19, 112, 107, 28, 255, 255, 216, 234, 181, 240, 176, 141, 147, 7, 1, 75, 175, 9, 37, 42, 70, 156, 112, 61, 35, 8, 70, + 101, 67, 43, 112, 123, 14, 21, 155, 7, 112, 189, 70, 110, 12, 21, 36, 0, 112, 253, 113, 122, 10, 21, 54, 47, 10, 26, + 112, 52, 113, 61, 113, 188, 113, 250, 114, 59, 114, 124, 123, 130, 66, 138, 217, 32, 29, 10, 0, 146, 88, 18, 70, 148, + 69, 164, 208, 26, 77, 17, 35, 1, 96, 43, 155, 7, 151, 0, 70, 98, 67, 90, 70, 148, 34, 1, 146, 2, 34, 6, 146, 5, 39, + 10, 154, 18, 70, 99, 151, 1, 150, 3, 148, 4, 247, 255, 250, 162, 96, 44, 40, 0, 221, 7, 120, 51, 43, 0, 209, 3, 224, + 3, 32, 3, 66, 64, 224, 0, 72, 3, 176, 13, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 107, 28, 255, 255, 216, 234, 181, + 240, 176, 135, 28, 28, 147, 2, 28, 11, 51, 4, 0, 155, 145, 3, 88, 25, 28, 6, 28, 11, 67, 99, 32, 128, 2, 64, 28, 23, + 66, 131, 217, 2, 240, 2, 248, 151, 144, 2, 155, 3, 32, 1, 51, 4, 0, 155, 66, 64, 147, 5, 224, 28, 154, 2, 28, 37, 66, + 148, 217, 0, 28, 21, 4, 43, 12, 27, 147, 4, 155, 12, 28, 48, 147, 0, 153, 3, 28, 58, 155, 4, 247, 255, 255, 67, 40, + 0, 219, 12, 154, 4, 25, 127, 26, 164, 154, 5, 4, 36, 89, 147, 12, 36, 67, 93, 155, 12, 25, 91, 147, 12, 44, 0, 209, + 224, 176, 7, 188, 240, 188, 2, 71, 8, 181, 240, 176, 135, 28, 28, 147, 2, 28, 11, 51, 4, 0, 155, 145, 3, 88, 25, 28, + 6, 28, 11, 67, 99, 32, 128, 2, 64, 28, 23, 66, 131, 217, 2, 240, 2, 248, 89, 144, 2, 155, 3, 32, 1, 51, 4, 0, 155, 66, + 64, 147, 5, 224, 28, 154, 2, 28, 37, 66, 148, 217, 0, 28, 21, 4, 43, 12, 27, 147, 4, 155, 12, 28, 48, 147, 0, 153, 3, + 28, 58, 155, 4, 247, 255, 255, 73, 40, 0, 219, 12, 154, 4, 25, 127, 26, 164, 154, 5, 4, 36, 89, 147, 12, 36, 67, 93, + 155, 12, 25, 91, 147, 12, 44, 0, 209, 224, 176, 7, 188, 240, 188, 2, 71, 8, 181, 240, 176, 139, 74, 135, 35, 0, 128, + 19, 74, 135, 36, 0, 128, 19, 75, 134, 74, 135, 112, 28, 75, 135, 79, 135, 112, 28, 35, 120, 144, 8, 66, 91, 96, 19, + 28, 56, 153, 8, 247, 255, 251, 218, 40, 0, 218, 0, 224, 241, 34, 1, 75, 129, 115, 186, 104, 25, 41, 0, 208, 74, 121, + 91, 43, 0, 209, 3, 75, 126, 120, 27, 66, 26, 208, 29, 76, 122, 35, 0, 115, 163, 77, 124, 75, 124, 38, 1, 96, 43, 28, + 35, 51, 144, 104, 24, 136, 227, 33, 161, 147, 0, 28, 35, 51, 14, 147, 2, 34, 254, 35, 0, 150, 1, 247, 254, 250, 171, + 75, 117, 96, 43, 40, 0, 218, 1, 115, 166, 224, 188, 123, 163, 51, 1, 115, 163, 75, 108, 72, 113, 121, 29, 28, 41, 247, + 254, 248, 34, 78, 104, 28, 41, 28, 48, 247, 255, 254, 59, 28, 4, 28, 33, 72, 108, 247, 254, 248, 24, 28, 163, 43, 1, + 216, 9, 28, 48, 247, 255, 253, 154, 74, 94, 35, 121, 66, 91, 96, 19, 34, 0, 75, 91, 224, 61, 44, 0, 218, 0, 224, 153, + 75, 88, 34, 1, 112, 26, 75, 89, 112, 29, 224, 139, 38, 1, 113, 92, 37, 0, 28, 60, 150, 6, 55, 144, 28, 40, 30, 67, 65, + 152, 144, 9, 40, 0, 209, 5, 75, 84, 120, 27, 7, 217, 213, 1, 46, 0, 209, 41, 72, 87, 28, 41, 247, 253, 255, 236, 6, + 43, 14, 27, 28, 25, 72, 76, 147, 5, 247, 255, 254, 3, 144, 7, 153, 7, 72, 80, 247, 253, 255, 224, 155, 7, 51, 2, 43, + 1, 216, 16, 154, 9, 42, 0, 208, 13, 74, 73, 75, 70, 72, 67, 96, 26, 247, 255, 253, 91, 74, 63, 35, 121, 66, 91, 96, + 19, 75, 60, 34, 0, 112, 26, 224, 105, 155, 7, 43, 0, 218, 59, 46, 0, 208, 52, 78, 61, 75, 62, 73, 66, 96, 51, 136, 227, + 32, 0, 96, 8, 115, 160, 104, 56, 147, 0, 28, 35, 34, 1, 51, 14, 146, 1, 147, 2, 33, 161, 35, 0, 34, 254, 247, 254, 250, + 44, 75, 53, 144, 5, 96, 51, 40, 0, 218, 5, 73, 55, 35, 1, 32, 0, 115, 163, 96, 8, 224, 5, 123, 163, 34, 1, 51, 1, 115, + 163, 75, 42, 113, 90, 75, 40, 72, 49, 123, 155, 153, 5, 28, 26, 147, 6, 247, 253, 255, 153, 75, 38, 38, 0, 120, 27, + 7, 216, 213, 3, 153, 5, 15, 206, 224, 0, 53, 1, 154, 6, 66, 149, 219, 144, 224, 29, 33, 23, 70, 104, 74, 29, 92, 8, + 35, 1, 96, 19, 113, 16, 154, 8, 152, 8, 137, 17, 74, 19, 128, 17, 137, 65, 74, 19, 128, 17, 74, 19, 70, 105, 112, 19, + 34, 23, 75, 19, 92, 81, 112, 25, 74, 23, 75, 21, 32, 0, 96, 26, 75, 14, 34, 0, 96, 26, 224, 14, 74, 12, 35, 122, 66, + 91, 96, 19, 72, 12, 247, 255, 252, 239, 75, 8, 34, 0, 112, 26, 72, 20, 247, 253, 255, 97, 32, 3, 66, 64, 176, 11, 188, + 240, 188, 2, 71, 8, 70, 192, 19, 112, 107, 24, 19, 112, 107, 32, 19, 112, 107, 60, 19, 112, 107, 0, 19, 112, 96, 64, + 19, 114, 197, 128, 19, 112, 107, 36, 19, 112, 1, 14, 19, 112, 96, 56, 0, 152, 150, 128, 0, 15, 66, 64, 19, 112, 104, + 98, 19, 112, 104, 128, 19, 112, 104, 103, 19, 112, 107, 84, 19, 112, 104, 157, 19, 112, 104, 198, 181, 8, 74, 10, 104, + 19, 43, 0, 208, 13, 75, 9, 33, 16, 112, 25, 35, 0, 73, 8, 96, 19, 74, 8, 112, 11, 96, 19, 72, 7, 33, 0, 34, 20, 240, + 1, 254, 17, 188, 8, 188, 1, 71, 0, 19, 112, 107, 56, 19, 112, 96, 64, 19, 112, 107, 60, 19, 112, 107, 84, 19, 112, 107, + 36, 181, 240, 176, 135, 75, 120, 36, 0, 104, 29, 45, 0, 208, 0, 224, 229, 75, 118, 32, 0, 104, 27, 104, 155, 96, 157, + 247, 253, 248, 99, 74, 116, 35, 100, 66, 91, 96, 19, 74, 115, 35, 1, 96, 19, 74, 114, 72, 115, 112, 21, 74, 115, 112, + 19, 247, 253, 254, 251, 75, 114, 120, 27, 43, 1, 208, 4, 43, 2, 208, 6, 34, 0, 146, 4, 224, 6, 35, 1, 147, 4, 36, 1, + 224, 2, 34, 1, 146, 4, 36, 0, 0, 167, 224, 176, 75, 99, 74, 105, 104, 29, 0, 163, 25, 27, 0, 219, 24, 235, 103, 92, + 111, 27, 96, 20, 43, 0, 209, 54, 104, 171, 34, 1, 51, 68, 25, 219, 104, 27, 66, 26, 209, 2, 28, 32, 247, 253, 252, 223, + 78, 88, 34, 1, 104, 51, 104, 155, 51, 68, 25, 219, 104, 27, 66, 26, 208, 35, 28, 32, 247, 254, 251, 253, 35, 140, 0, + 219, 24, 192, 40, 1, 216, 4, 35, 100, 74, 80, 66, 91, 96, 19, 224, 130, 28, 32, 247, 254, 250, 140, 144, 3, 32, 20, + 247, 253, 249, 176, 104, 51, 104, 155, 51, 68, 25, 219, 104, 26, 155, 3, 43, 0, 219, 4, 75, 77, 64, 26, 75, 77, 66, + 154, 208, 2, 28, 32, 247, 254, 251, 220, 0, 160, 25, 0, 0, 192, 24, 43, 111, 27, 43, 0, 208, 100, 75, 64, 34, 1, 48, + 88, 96, 26, 24, 40, 247, 255, 254, 44, 144, 3, 40, 0, 209, 87, 75, 56, 34, 1, 96, 26, 79, 65, 74, 57, 96, 16, 28, 56, + 247, 252, 255, 230, 77, 52, 34, 4, 104, 43, 72, 62, 104, 155, 96, 154, 247, 253, 254, 132, 32, 100, 247, 253, 249, 123, + 32, 128, 1, 0, 240, 0, 253, 105, 78, 57, 75, 57, 34, 1, 96, 51, 75, 57, 144, 4, 112, 26, 104, 43, 154, 3, 104, 155, + 32, 0, 96, 154, 247, 252, 255, 201, 72, 53, 247, 253, 254, 108, 75, 52, 34, 0, 120, 25, 155, 4, 72, 51, 147, 0, 35, + 1, 247, 255, 253, 123, 144, 5, 28, 56, 247, 252, 255, 185, 104, 43, 34, 4, 104, 155, 96, 154, 70, 107, 34, 15, 92, 210, + 75, 40, 152, 4, 112, 26, 75, 43, 96, 51, 240, 0, 253, 91, 155, 5, 43, 0, 218, 7, 72, 40, 247, 253, 254, 75, 28, 32, + 247, 253, 252, 132, 76, 38, 224, 27, 72, 38, 247, 253, 254, 67, 75, 38, 104, 27, 43, 0, 208, 20, 52, 2, 224, 18, 28, + 32, 247, 253, 252, 118, 52, 1, 55, 4, 154, 4, 66, 148, 220, 0, 231, 74, 72, 18, 247, 252, 255, 139, 74, 7, 104, 19, + 34, 4, 104, 155, 96, 154, 75, 5, 104, 28, 176, 7, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 107, 56, 19, 112, + 96, 60, 19, 112, 107, 0, 19, 112, 107, 84, 19, 112, 107, 60, 19, 112, 104, 227, 19, 112, 1, 15, 19, 112, 1, 12, 19, + 112, 107, 80, 0, 0, 57, 5, 0, 0, 16, 5, 19, 112, 17, 29, 19, 112, 105, 118, 19, 112, 96, 56, 0, 152, 150, 128, 19, 112, + 106, 248, 19, 112, 105, 167, 19, 112, 96, 64, 19, 114, 197, 128, 0, 15, 66, 64, 19, 112, 105, 140, 255, 255, 251, 162, + 19, 112, 105, 164, 19, 112, 107, 12, 181, 0, 30, 3, 208, 1, 34, 0, 96, 26, 74, 12, 32, 0, 120, 18, 42, 1, 209, 16, 74, + 10, 120, 18, 42, 16, 208, 12, 43, 0, 208, 4, 50, 4, 73, 8, 0, 146, 88, 82, 96, 26, 75, 5, 120, 26, 75, 5, 50, 20, 0, + 146, 88, 208, 188, 2, 71, 8, 70, 192, 19, 112, 107, 60, 19, 112, 96, 64, 19, 114, 197, 128, 181, 240, 176, 139, 75, + 78, 104, 27, 43, 0, 209, 0, 224, 139, 75, 77, 34, 16, 112, 26, 75, 76, 120, 26, 75, 76, 42, 0, 208, 1, 74, 76, 224, + 0, 74, 76, 78, 76, 96, 26, 172, 4, 35, 0, 28, 53, 130, 99, 130, 163, 53, 144, 28, 33, 104, 40, 247, 254, 251, 0, 75, + 71, 121, 34, 121, 153, 66, 145, 209, 23, 121, 217, 121, 98, 66, 145, 209, 19, 137, 25, 137, 34, 66, 145, 209, 15, 137, + 89, 137, 98, 66, 145, 209, 11, 123, 25, 123, 162, 66, 145, 209, 7, 123, 89, 123, 226, 66, 145, 209, 3, 123, 154, 124, + 35, 66, 154, 208, 6, 168, 4, 247, 253, 252, 101, 72, 56, 247, 253, 253, 143, 224, 80, 28, 32, 247, 253, 252, 94, 74, + 54, 75, 47, 104, 40, 96, 26, 120, 49, 247, 254, 248, 123, 40, 0, 219, 70, 104, 178, 42, 0, 208, 7, 6, 18, 104, 40, 121, + 241, 14, 18, 247, 254, 248, 98, 40, 0, 219, 61, 76, 41, 35, 0, 115, 163, 77, 36, 75, 42, 39, 1, 96, 43, 28, 35, 51, + 144, 104, 24, 136, 227, 33, 161, 147, 0, 28, 35, 51, 14, 147, 2, 34, 254, 35, 0, 151, 1, 247, 253, 255, 220, 75, 28, + 96, 43, 40, 0, 218, 6, 115, 167, 28, 32, 33, 0, 247, 254, 254, 77, 72, 30, 224, 34, 123, 163, 78, 25, 51, 1, 115, 163, + 75, 28, 28, 32, 96, 43, 121, 49, 247, 255, 248, 75, 75, 18, 96, 43, 40, 0, 219, 17, 121, 49, 28, 32, 247, 255, 250, + 213, 40, 0, 219, 13, 121, 50, 75, 9, 112, 26, 75, 19, 112, 31, 224, 8, 72, 19, 224, 6, 72, 19, 224, 4, 72, 19, 224, + 2, 72, 19, 224, 0, 72, 19, 176, 11, 188, 240, 188, 2, 71, 8, 19, 112, 107, 56, 19, 112, 96, 64, 19, 112, 1, 15, 19, + 112, 96, 56, 0, 15, 66, 64, 0, 3, 13, 64, 19, 114, 197, 128, 19, 112, 107, 36, 19, 112, 101, 128, 0, 152, 150, 128, + 255, 255, 252, 20, 1, 49, 45, 0, 19, 112, 107, 60, 255, 255, 252, 15, 255, 255, 252, 23, 255, 255, 252, 22, 255, 255, + 252, 19, 255, 255, 252, 18, 181, 240, 176, 131, 75, 57, 76, 58, 120, 26, 38, 1, 30, 83, 65, 154, 75, 56, 96, 34, 104, + 27, 43, 0, 208, 99, 75, 55, 0, 146, 104, 27, 104, 155, 28, 25, 49, 68, 24, 138, 104, 18, 66, 22, 208, 89, 34, 0, 32, + 0, 96, 154, 247, 252, 254, 74, 104, 32, 247, 254, 248, 191, 30, 7, 218, 3, 104, 32, 247, 254, 248, 186, 28, 7, 32, 60, + 247, 252, 255, 222, 77, 41, 72, 42, 104, 43, 28, 57, 104, 154, 75, 37, 50, 68, 104, 27, 38, 1, 0, 155, 24, 211, 104, + 27, 28, 26, 147, 1, 247, 253, 252, 212, 76, 32, 35, 1, 96, 35, 47, 0, 219, 48, 155, 1, 74, 32, 64, 26, 75, 32, 66, 154, + 209, 42, 74, 32, 35, 0, 112, 19, 34, 0, 96, 34, 247, 255, 254, 242, 74, 29, 75, 30, 96, 26, 40, 0, 219, 12, 74, 26, + 35, 0, 112, 22, 96, 35, 72, 27, 247, 252, 254, 16, 104, 43, 34, 4, 104, 155, 38, 0, 96, 154, 224, 17, 28, 57, 72, 23, + 96, 38, 247, 253, 252, 171, 75, 17, 34, 0, 112, 26, 32, 100, 247, 252, 255, 159, 72, 17, 247, 252, 253, 252, 104, 43, + 34, 4, 104, 155, 96, 154, 32, 100, 247, 252, 255, 149, 176, 3, 28, 48, 188, 240, 188, 2, 71, 8, 19, 112, 1, 12, 19, + 112, 107, 80, 19, 112, 107, 84, 19, 112, 96, 60, 19, 112, 105, 185, 0, 0, 49, 5, 0, 0, 16, 5, 19, 112, 107, 60, 0, 15, + 66, 64, 19, 112, 96, 56, 19, 112, 17, 29, 19, 112, 105, 213, 181, 240, 176, 131, 75, 30, 34, 0, 104, 27, 146, 1, 43, + 0, 208, 50, 247, 255, 255, 104, 75, 27, 104, 27, 43, 0, 209, 44, 75, 26, 120, 27, 43, 0, 208, 40, 77, 25, 79, 26, 76, + 26, 78, 27, 96, 47, 121, 49, 28, 32, 247, 255, 249, 243, 40, 0, 219, 29, 96, 47, 121, 49, 28, 32, 28, 11, 29, 10, 51, + 20, 0, 146, 0, 155, 25, 18, 25, 27, 247, 255, 250, 47, 40, 0, 219, 15, 75, 17, 104, 26, 42, 0, 208, 7, 121, 49, 35, + 128, 49, 4, 0, 137, 89, 9, 1, 27, 66, 153, 209, 3, 28, 19, 30, 90, 65, 147, 147, 1, 152, 1, 176, 3, 188, 240, 188, 2, + 71, 8, 19, 112, 107, 56, 19, 112, 107, 84, 19, 112, 107, 60, 19, 112, 96, 56, 0, 15, 66, 64, 19, 114, 197, 128, 19, + 112, 107, 36, 19, 112, 107, 12, 181, 240, 176, 135, 75, 66, 144, 3, 104, 27, 146, 4, 43, 0, 209, 7, 75, 64, 104, 27, + 43, 0, 208, 3, 0, 66, 75, 63, 8, 82, 96, 26, 75, 62, 104, 27, 43, 0, 208, 6, 75, 61, 104, 26, 42, 1, 221, 2, 34, 1, + 96, 26, 224, 101, 4, 9, 12, 9, 79, 56, 38, 4, 36, 0, 145, 5, 247, 255, 254, 255, 30, 67, 65, 152, 104, 59, 66, 64, 64, + 4, 43, 0, 208, 3, 74, 50, 104, 19, 43, 3, 208, 79, 44, 0, 219, 3, 75, 48, 120, 27, 43, 1, 208, 4, 75, 45, 34, 1, 36, + 1, 96, 26, 66, 100, 75, 43, 104, 27, 43, 0, 209, 61, 75, 38, 104, 26, 75, 42, 42, 0, 208, 1, 74, 41, 224, 0, 74, 41, + 96, 26, 28, 99, 208, 24, 32, 0, 247, 252, 253, 61, 77, 39, 34, 0, 104, 43, 72, 38, 104, 155, 96, 154, 154, 4, 75, 37, + 120, 25, 155, 5, 146, 0, 154, 3, 247, 255, 250, 237, 28, 4, 72, 34, 247, 252, 253, 43, 104, 43, 34, 4, 104, 155, 96, + 154, 74, 32, 75, 24, 96, 26, 44, 0, 218, 2, 75, 20, 34, 1, 96, 26, 75, 18, 104, 27, 43, 0, 208, 3, 75, 17, 104, 27, + 43, 0, 209, 15, 75, 15, 104, 27, 43, 0, 209, 5, 75, 14, 120, 27, 43, 1, 209, 1, 44, 0, 218, 7, 62, 1, 46, 0, 209, 162, + 67, 228, 15, 224, 224, 2, 32, 0, 224, 0, 32, 1, 176, 7, 188, 240, 188, 2, 71, 8, 19, 112, 106, 240, 19, 112, 107, 12, + 19, 112, 106, 192, 19, 112, 106, 196, 19, 112, 107, 84, 19, 112, 107, 60, 19, 112, 96, 56, 0, 152, 150, 128, 0, 45, + 198, 192, 19, 112, 96, 60, 19, 114, 197, 128, 19, 112, 96, 64, 19, 112, 17, 29, 0, 15, 66, 64, 181, 240, 176, 133, 75, + 39, 28, 7, 104, 27, 28, 22, 32, 0, 43, 0, 209, 67, 4, 9, 12, 9, 36, 0, 145, 3, 247, 255, 254, 111, 40, 0, 208, 2, 44, + 0, 219, 5, 224, 0, 36, 0, 74, 31, 120, 19, 43, 1, 208, 4, 75, 30, 34, 1, 36, 1, 96, 26, 66, 100, 75, 27, 104, 27, 43, + 0, 209, 234, 73, 26, 74, 27, 96, 17, 28, 98, 208, 22, 77, 26, 32, 0, 104, 42, 104, 146, 96, 147, 247, 252, 252, 182, + 75, 23, 28, 58, 120, 25, 72, 23, 155, 3, 150, 0, 247, 255, 250, 170, 28, 4, 72, 21, 247, 252, 252, 170, 104, 43, 34, + 4, 104, 155, 96, 154, 74, 18, 75, 13, 96, 26, 44, 0, 218, 2, 75, 9, 34, 1, 96, 26, 75, 7, 104, 27, 43, 0, 209, 194, + 44, 0, 219, 192, 32, 1, 176, 5, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 107, 12, 19, 112, 107, 60, 19, 112, 107, + 84, 0, 45, 198, 192, 19, 112, 96, 56, 19, 112, 96, 60, 19, 112, 96, 64, 19, 114, 197, 128, 19, 112, 17, 29, 0, 15, 66, + 64, 181, 240, 176, 137, 74, 95, 104, 19, 43, 0, 209, 6, 75, 94, 104, 27, 105, 153, 96, 17, 106, 26, 75, 93, 96, 26, + 75, 92, 77, 90, 104, 28, 38, 0, 28, 55, 104, 43, 55, 8, 0, 191, 80, 252, 28, 32, 33, 0, 34, 96, 240, 1, 249, 245, 104, + 43, 33, 96, 88, 248, 240, 0, 249, 214, 52, 127, 33, 31, 54, 1, 67, 140, 46, 8, 209, 234, 37, 8, 38, 31, 28, 32, 33, + 0, 34, 96, 240, 1, 249, 227, 61, 1, 28, 32, 33, 96, 52, 127, 240, 0, 249, 195, 67, 180, 45, 0, 209, 241, 75, 72, 34, + 255, 96, 28, 75, 68, 39, 128, 104, 29, 35, 255, 2, 18, 4, 27, 76, 69, 38, 0, 5, 255, 146, 2, 147, 3, 33, 0, 96, 37, + 34, 96, 28, 40, 240, 1, 249, 198, 28, 40, 240, 0, 249, 152, 28, 50, 100, 104, 104, 35, 30, 81, 65, 138, 5, 210, 96, + 90, 104, 35, 34, 0, 96, 154, 104, 35, 33, 31, 97, 159, 53, 127, 67, 141, 104, 34, 28, 40, 146, 5, 240, 0, 249, 132, + 144, 6, 28, 40, 240, 0, 249, 128, 144, 1, 28, 40, 240, 0, 249, 124, 144, 7, 28, 40, 240, 0, 249, 120, 35, 255, 2, 27, + 153, 2, 147, 4, 34, 255, 155, 1, 4, 18, 64, 11, 146, 1, 153, 3, 154, 7, 2, 27, 64, 10, 10, 18, 67, 19, 14, 0, 154, 6, + 67, 3, 32, 224, 64, 16, 33, 2, 67, 8, 154, 5, 6, 0, 67, 3, 96, 19, 104, 34, 35, 128, 4, 91, 97, 19, 104, 34, 33, 96, + 97, 83, 204, 1, 54, 1, 240, 0, 249, 100, 46, 6, 209, 175, 76, 22, 77, 26, 104, 32, 105, 110, 240, 0, 249, 75, 144, 2, + 104, 32, 240, 0, 249, 71, 28, 7, 104, 32, 240, 0, 249, 67, 144, 3, 104, 32, 240, 0, 249, 63, 155, 4, 153, 1, 64, 31, + 155, 3, 2, 63, 64, 11, 10, 27, 67, 31, 154, 2, 14, 3, 28, 56, 67, 24, 35, 224, 64, 19, 33, 2, 67, 11, 6, 27, 67, 24, + 96, 48, 105, 104, 33, 96, 240, 0, 249, 57, 176, 9, 188, 240, 188, 1, 71, 0, 70, 192, 19, 112, 107, 76, 19, 112, 96, + 60, 19, 112, 106, 236, 19, 112, 107, 8, 19, 114, 195, 160, 181, 248, 75, 16, 39, 128, 104, 28, 38, 0, 4, 127, 28, 32, + 247, 252, 253, 241, 46, 3, 208, 15, 28, 37, 35, 31, 53, 127, 67, 157, 107, 104, 240, 1, 248, 182, 96, 103, 96, 32, 33, + 96, 28, 32, 240, 0, 249, 18, 54, 1, 28, 44, 231, 234, 28, 32, 33, 96, 240, 0, 249, 11, 188, 248, 188, 1, 71, 0, 19, + 112, 107, 8, 181, 240, 176, 131, 247, 255, 255, 12, 247, 255, 255, 214, 76, 65, 75, 66, 104, 34, 104, 24, 77, 65, 97, + 144, 104, 89, 78, 65, 97, 209, 104, 154, 79, 64, 96, 42, 104, 218, 105, 27, 96, 50, 96, 59, 33, 96, 240, 0, 249, 1, + 104, 35, 33, 0, 105, 154, 100, 211, 105, 154, 100, 145, 105, 155, 28, 24, 147, 1, 240, 0, 248, 209, 155, 1, 104, 34, + 100, 88, 104, 59, 105, 145, 108, 91, 34, 255, 2, 18, 64, 26, 2, 16, 34, 255, 4, 18, 64, 26, 10, 18, 67, 2, 14, 24, 67, + 2, 32, 224, 64, 3, 39, 2, 67, 59, 6, 24, 28, 19, 67, 3, 96, 11, 104, 35, 34, 128, 105, 153, 4, 18, 96, 74, 105, 154, + 33, 0, 96, 145, 105, 153, 34, 128, 5, 210, 97, 138, 105, 153, 34, 128, 4, 82, 97, 10, 105, 153, 97, 74, 105, 152, 33, + 96, 240, 0, 248, 179, 104, 35, 33, 96, 105, 216, 240, 0, 248, 194, 104, 35, 33, 0, 105, 218, 100, 211, 105, 218, 100, + 145, 105, 223, 28, 56, 240, 0, 248, 147, 33, 96, 100, 120, 104, 40, 240, 0, 248, 179, 104, 47, 104, 35, 28, 56, 100, + 251, 35, 0, 100, 187, 240, 0, 248, 134, 33, 96, 100, 120, 104, 40, 240, 0, 248, 146, 104, 48, 33, 96, 240, 0, 248, 162, + 104, 53, 104, 35, 33, 0, 100, 235, 100, 169, 28, 40, 240, 0, 248, 117, 33, 96, 100, 104, 104, 48, 240, 0, 248, 129, + 176, 3, 188, 240, 188, 1, 71, 0, 70, 192, 19, 112, 96, 60, 19, 114, 195, 160, 19, 112, 106, 244, 19, 112, 107, 72, 19, + 112, 107, 68, 181, 248, 76, 35, 75, 35, 96, 35, 240, 0, 248, 74, 40, 0, 219, 58, 240, 0, 249, 117, 74, 32, 104, 131, + 96, 32, 96, 26, 104, 32, 35, 1, 104, 130, 104, 17, 66, 11, 209, 252, 33, 252, 240, 0, 248, 91, 38, 128, 39, 128, 76, + 23, 37, 0, 4, 118, 1, 127, 104, 35, 33, 4, 108, 219, 81, 94, 104, 35, 108, 219, 89, 88, 53, 4, 240, 0, 248, 75, 66, + 189, 209, 243, 247, 255, 255, 65, 104, 35, 34, 2, 51, 84, 112, 26, 247, 252, 255, 185, 104, 35, 32, 0, 104, 154, 105, + 155, 108, 91, 97, 147, 104, 35, 34, 4, 104, 155, 96, 154, 104, 35, 74, 8, 104, 155, 96, 26, 104, 35, 104, 155, 104, + 27, 224, 1, 32, 1, 66, 64, 188, 248, 188, 2, 71, 8, 19, 112, 96, 60, 19, 114, 198, 36, 0, 1, 0, 32, 0, 1, 0, 33, 181, + 8, 33, 224, 72, 4, 2, 9, 240, 1, 249, 66, 75, 3, 96, 24, 23, 192, 188, 8, 188, 2, 71, 8, 19, 112, 163, 128, 19, 112, + 96, 68, 71, 112, 71, 112, 181, 16, 28, 4, 240, 1, 249, 62, 28, 32, 188, 16, 188, 2, 71, 8, 181, 16, 28, 4, 240, 1, 249, + 54, 28, 32, 188, 16, 188, 2, 71, 8, 181, 16, 28, 4, 240, 1, 249, 46, 28, 32, 188, 16, 188, 2, 71, 8, 181, 8, 240, 1, + 249, 71, 188, 8, 188, 1, 71, 0, 181, 8, 240, 1, 249, 65, 188, 8, 188, 1, 71, 0, 181, 8, 240, 1, 249, 59, 188, 8, 188, + 1, 71, 0, 181, 8, 75, 12, 28, 1, 34, 32, 104, 24, 240, 1, 249, 45, 40, 0, 209, 13, 72, 9, 240, 1, 249, 4, 240, 0, 249, + 63, 32, 200, 247, 252, 251, 253, 240, 0, 249, 68, 32, 200, 247, 252, 251, 248, 231, 244, 188, 8, 188, 2, 71, 8, 19, + 112, 96, 68, 19, 112, 105, 237, 181, 8, 75, 4, 28, 1, 104, 24, 240, 1, 248, 216, 188, 8, 188, 1, 71, 0, 70, 192, 19, + 112, 96, 68, 181, 248, 76, 13, 28, 15, 104, 33, 28, 30, 28, 24, 28, 21, 67, 81, 240, 1, 248, 228, 28, 56, 28, 41, 28, + 50, 247, 255, 252, 173, 35, 1, 40, 0, 208, 5, 104, 33, 28, 48, 67, 105, 240, 1, 248, 247, 35, 0, 28, 24, 188, 248, 188, + 2, 71, 8, 19, 114, 199, 32, 181, 16, 76, 11, 104, 32, 40, 0, 208, 3, 240, 0, 252, 39, 35, 0, 96, 35, 76, 8, 104, 35, + 43, 0, 209, 3, 72, 7, 247, 251, 252, 132, 96, 32, 74, 6, 35, 1, 66, 91, 96, 19, 188, 16, 188, 1, 71, 0, 19, 112, 107, + 96, 19, 112, 107, 88, 0, 0, 128, 32, 19, 112, 96, 24, 181, 56, 75, 14, 104, 24, 40, 0, 208, 1, 247, 251, 252, 139, 76, + 12, 75, 10, 104, 32, 37, 0, 96, 29, 40, 0, 208, 2, 240, 0, 251, 254, 96, 37, 75, 8, 104, 24, 40, 0, 208, 1, 240, 0, + 251, 91, 75, 5, 34, 0, 96, 26, 188, 56, 188, 1, 71, 0, 70, 192, 19, 112, 107, 88, 19, 112, 107, 96, 19, 112, 107, 92, + 181, 240, 176, 135, 75, 37, 28, 7, 104, 24, 145, 5, 40, 0, 208, 1, 247, 251, 252, 100, 75, 33, 77, 34, 34, 0, 96, 26, + 104, 43, 43, 0, 209, 23, 247, 255, 249, 93, 78, 31, 36, 0, 28, 48, 247, 255, 250, 124, 75, 29, 96, 24, 40, 0, 208, 43, + 154, 5, 104, 51, 33, 0, 144, 0, 146, 1, 72, 26, 34, 0, 148, 2, 240, 0, 249, 238, 96, 40, 40, 0, 208, 30, 77, 23, 104, + 44, 44, 0, 208, 11, 72, 22, 28, 57, 34, 6, 240, 0, 254, 210, 40, 0, 208, 19, 28, 32, 240, 0, 251, 179, 35, 0, 96, 43, + 75, 11, 28, 57, 104, 24, 240, 0, 251, 54, 75, 12, 28, 4, 96, 24, 40, 0, 208, 4, 72, 11, 28, 57, 34, 6, 240, 0, 254, + 234, 176, 7, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 107, 88, 19, 112, 107, 92, 19, 114, 199, 32, 19, 114, + 199, 36, 19, 112, 72, 161, 19, 112, 107, 96, 19, 112, 96, 72, 181, 8, 28, 3, 28, 10, 32, 0, 28, 25, 35, 0, 240, 1, 248, + 56, 188, 8, 188, 1, 71, 0, 181, 8, 32, 1, 33, 0, 34, 0, 35, 0, 240, 1, 248, 46, 188, 8, 188, 2, 71, 8, 181, 8, 32, 17, + 33, 0, 34, 0, 35, 0, 240, 1, 248, 36, 188, 8, 188, 2, 71, 8, 181, 8, 32, 18, 33, 0, 34, 0, 35, 0, 240, 1, 248, 26, 188, + 8, 188, 2, 71, 8, 181, 56, 28, 5, 28, 12, 30, 19, 221, 4, 32, 2, 28, 41, 28, 34, 240, 1, 248, 13, 188, 56, 188, 1, 71, + 0, 181, 56, 28, 5, 28, 12, 30, 19, 221, 4, 32, 9, 28, 41, 28, 34, 240, 1, 248, 0, 188, 56, 188, 1, 71, 0, 181, 8, 28, + 3, 28, 10, 32, 5, 28, 25, 35, 0, 240, 0, 255, 245, 188, 8, 188, 1, 71, 0, 181, 8, 28, 3, 28, 10, 32, 6, 28, 25, 35, + 0, 240, 0, 255, 234, 188, 8, 188, 1, 71, 0, 181, 56, 28, 5, 28, 12, 28, 19, 28, 41, 28, 34, 32, 16, 240, 0, 255, 222, + 188, 56, 188, 2, 71, 8, 181, 8, 32, 128, 33, 0, 34, 0, 35, 0, 240, 0, 255, 212, 188, 8, 188, 1, 71, 0, 181, 8, 32, 129, + 33, 0, 34, 0, 35, 0, 240, 0, 255, 202, 188, 8, 188, 1, 71, 0, 181, 0, 35, 0, 224, 3, 51, 1, 6, 27, 14, 27, 8, 64, 40, + 0, 209, 249, 30, 88, 6, 0, 14, 0, 188, 2, 71, 8, 0, 0, 181, 240, 176, 133, 144, 1, 32, 84, 145, 3, 146, 2, 28, 30, 159, + 11, 247, 251, 251, 98, 28, 4, 30, 48, 209, 1, 32, 128, 0, 128, 247, 251, 251, 91, 28, 5, 32, 128, 2, 0, 97, 32, 247, + 255, 255, 218, 75, 125, 117, 32, 97, 227, 154, 10, 75, 124, 96, 37, 96, 30, 11, 211, 67, 115, 97, 163, 155, 12, 98, + 231, 43, 0, 208, 50, 33, 0, 28, 50, 28, 40, 240, 0, 254, 102, 35, 87, 112, 43, 35, 66, 112, 107, 35, 70, 112, 171, 35, + 83, 112, 235, 28, 48, 247, 255, 255, 188, 114, 40, 159, 10, 73, 111, 14, 59, 113, 43, 12, 59, 113, 107, 10, 59, 113, + 171, 113, 239, 105, 162, 35, 6, 66, 138, 217, 16, 73, 106, 35, 7, 66, 138, 217, 12, 73, 105, 35, 8, 66, 138, 217, 8, + 73, 104, 35, 9, 66, 138, 217, 4, 75, 103, 66, 147, 65, 155, 66, 91, 51, 10, 125, 34, 24, 155, 114, 107, 224, 6, 28, + 57, 152, 2, 34, 1, 28, 43, 159, 1, 240, 0, 248, 207, 120, 43, 120, 106, 6, 27, 4, 18, 67, 26, 120, 171, 2, 27, 67, 26, + 120, 235, 67, 26, 75, 91, 66, 154, 208, 4, 72, 90, 240, 0, 255, 49, 72, 90, 224, 40, 75, 90, 104, 27, 66, 95, 65, 95, + 46, 0, 208, 12, 47, 0, 208, 10, 28, 48, 247, 255, 255, 117, 122, 43, 66, 131, 208, 4, 72, 81, 240, 0, 255, 30, 72, 82, + 224, 21, 154, 10, 42, 0, 208, 31, 47, 0, 208, 29, 121, 42, 121, 107, 6, 18, 4, 27, 67, 19, 121, 170, 159, 10, 2, 18, + 67, 19, 121, 234, 67, 19, 66, 187, 208, 16, 72, 69, 240, 0, 255, 7, 72, 72, 240, 0, 255, 4, 72, 71, 240, 0, 255, 1, + 28, 32, 247, 251, 250, 232, 28, 40, 247, 251, 250, 229, 36, 0, 224, 100, 122, 46, 39, 1, 28, 58, 64, 178, 96, 98, 122, + 43, 28, 22, 147, 0, 114, 35, 121, 42, 121, 107, 6, 18, 4, 27, 67, 19, 121, 170, 121, 232, 2, 18, 67, 19, 67, 24, 105, + 33, 96, 224, 240, 0, 254, 168, 67, 112, 97, 160, 122, 107, 125, 34, 64, 159, 98, 99, 26, 155, 105, 226, 64, 216, 64, + 218, 4, 18, 12, 18, 4, 51, 12, 27, 133, 98, 50, 128, 4, 1, 133, 32, 0, 82, 30, 88, 24, 130, 66, 91, 64, 26, 155, 3, + 28, 37, 53, 72, 99, 99, 12, 201, 155, 0, 98, 39, 128, 42, 26, 127, 154, 1, 64, 223, 99, 34, 100, 39, 154, 2, 159, 12, + 99, 162, 47, 0, 209, 1, 100, 103, 224, 11, 30, 112, 24, 65, 66, 112, 64, 8, 247, 251, 250, 125, 141, 34, 100, 96, 8, + 210, 33, 255, 240, 0, 253, 155, 28, 35, 51, 72, 136, 25, 108, 32, 122, 35, 56, 1, 65, 25, 240, 0, 254, 104, 4, 2, 104, + 96, 12, 18, 28, 3, 59, 12, 135, 162, 66, 154, 217, 0, 135, 163, 247, 251, 250, 99, 35, 0, 100, 224, 101, 35, 176, 5, + 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 0, 4, 96, 144, 19, 112, 107, 108, 0, 63, 255, 255, 0, 127, 255, 255, 0, 255, + 255, 255, 1, 255, 255, 255, 3, 255, 255, 255, 87, 66, 70, 83, 19, 112, 106, 12, 19, 112, 106, 24, 19, 112, 107, 104, + 19, 112, 106, 34, 19, 112, 106, 63, 19, 112, 105, 116, 71, 56, 70, 192, 181, 240, 176, 165, 144, 9, 28, 24, 146, 10, + 147, 11, 145, 15, 247, 251, 250, 51, 35, 1, 74, 140, 77, 141, 66, 91, 96, 19, 104, 43, 28, 4, 43, 0, 209, 3, 152, 11, + 247, 251, 250, 39, 96, 40, 75, 136, 152, 11, 33, 0, 96, 24, 34, 1, 152, 10, 28, 35, 157, 9, 240, 0, 249, 24, 40, 0, + 208, 0, 224, 246, 35, 255, 0, 91, 92, 227, 43, 85, 209, 28, 51, 171, 51, 255, 92, 227, 43, 170, 209, 23, 28, 224, 73, + 125, 34, 4, 240, 0, 253, 138, 40, 0, 208, 16, 77, 123, 28, 32, 48, 54, 28, 41, 34, 3, 240, 0, 253, 129, 40, 0, 208, + 7, 28, 32, 48, 82, 28, 41, 34, 3, 240, 0, 253, 121, 40, 0, 209, 5, 168, 20, 33, 0, 34, 64, 240, 0, 253, 22, 224, 6, + 28, 33, 49, 191, 49, 255, 168, 20, 34, 64, 240, 0, 252, 204, 175, 32, 151, 5, 32, 228, 39, 1, 66, 127, 0, 64, 38, 0, + 173, 20, 144, 14, 151, 8, 122, 235, 122, 47, 122, 105, 122, 170, 147, 7, 120, 32, 120, 99, 6, 0, 4, 27, 67, 24, 120, + 163, 2, 27, 67, 24, 120, 227, 67, 24, 75, 97, 66, 152, 208, 3, 121, 40, 40, 0, 209, 0, 224, 162, 2, 9, 4, 18, 67, 17, + 67, 57, 159, 7, 6, 59, 67, 25, 145, 7, 121, 43, 43, 15, 209, 101, 33, 227, 32, 0, 0, 73, 159, 8, 144, 12, 35, 0, 145, + 13, 149, 4, 154, 7, 152, 10, 24, 210, 146, 8, 28, 17, 28, 35, 34, 1, 157, 9, 240, 0, 248, 169, 40, 0, 208, 0, 224, 135, + 72, 78, 153, 14, 92, 34, 92, 99, 2, 18, 4, 27, 67, 26, 77, 76, 155, 13, 152, 8, 92, 225, 93, 99, 67, 10, 6, 27, 67, + 19, 24, 192, 35, 235, 144, 8, 0, 91, 92, 227, 152, 10, 147, 16, 75, 69, 153, 8, 92, 227, 34, 1, 147, 17, 35, 236, 0, + 91, 92, 227, 157, 9, 147, 18, 75, 65, 92, 227, 147, 19, 28, 35, 240, 0, 248, 128, 40, 0, 209, 95, 120, 34, 120, 99, + 6, 18, 4, 27, 67, 19, 120, 162, 73, 54, 2, 18, 67, 19, 120, 226, 67, 19, 66, 139, 209, 10, 154, 43, 66, 150, 209, 2, + 144, 0, 155, 8, 224, 48, 152, 8, 66, 184, 208, 1, 54, 1, 28, 7, 153, 17, 157, 18, 2, 11, 4, 42, 152, 16, 153, 19, 67, + 19, 67, 3, 6, 10, 67, 19, 208, 53, 154, 12, 50, 1, 146, 12, 42, 8, 209, 162, 224, 47, 152, 10, 153, 7, 34, 1, 28, 35, + 159, 9, 240, 0, 248, 79, 40, 0, 209, 45, 120, 34, 120, 99, 6, 18, 4, 27, 67, 19, 120, 162, 73, 29, 2, 18, 67, 19, 120, + 226, 67, 19, 66, 139, 209, 27, 154, 43, 66, 150, 209, 15, 155, 7, 144, 0, 157, 44, 147, 1, 152, 9, 153, 15, 154, 10, + 155, 11, 149, 2, 247, 255, 253, 201, 28, 5, 28, 32, 247, 251, 249, 79, 224, 17, 159, 7, 152, 8, 66, 135, 208, 4, 54, + 1, 151, 8, 224, 1, 157, 4, 151, 8, 153, 5, 66, 141, 208, 1, 53, 16, 231, 65, 28, 32, 247, 251, 249, 61, 37, 0, 176, + 37, 28, 40, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 96, 80, 19, 112, 107, 100, 19, 112, 107, 108, 19, 112, 106, 91, + 19, 112, 106, 96, 87, 66, 70, 83, 0, 0, 1, 199, 0, 0, 1, 201, 0, 0, 1, 215, 0, 0, 1, 217, 71, 40, 71, 56, 181, 16, 109, + 3, 28, 4, 43, 0, 208, 9, 72, 13, 240, 0, 253, 46, 72, 12, 240, 0, 253, 43, 72, 12, 240, 0, 253, 40, 224, 13, 104, 0, + 247, 251, 249, 14, 108, 224, 247, 251, 249, 11, 108, 96, 40, 0, 208, 1, 247, 251, 249, 6, 28, 32, 247, 251, 249, 3, + 188, 16, 188, 1, 71, 0, 19, 112, 106, 12, 19, 112, 106, 100, 19, 112, 105, 116, 181, 240, 28, 3, 51, 72, 176, 131, 136, + 26, 122, 3, 28, 5, 65, 26, 146, 1, 35, 1, 74, 48, 66, 91, 96, 19, 34, 0, 28, 14, 146, 0, 39, 0, 224, 79, 104, 43, 25, + 219, 123, 27, 43, 0, 208, 68, 106, 233, 155, 0, 49, 1, 24, 201, 34, 1, 107, 168, 108, 235, 107, 44, 240, 0, 248, 83, + 108, 233, 28, 48, 34, 6, 240, 0, 251, 106, 40, 0, 209, 51, 32, 12, 247, 251, 248, 177, 30, 6, 209, 9, 72, 32, 240, 0, + 252, 224, 72, 31, 240, 0, 252, 221, 72, 31, 240, 0, 252, 218, 224, 46, 28, 43, 51, 72, 96, 53, 96, 183, 136, 24, 247, + 251, 248, 158, 28, 3, 96, 112, 40, 0, 209, 12, 72, 21, 240, 0, 252, 203, 72, 21, 240, 0, 252, 200, 72, 20, 240, 0, 252, + 197, 28, 48, 247, 251, 248, 172, 224, 21, 106, 233, 154, 0, 49, 1, 24, 137, 107, 168, 107, 44, 154, 1, 240, 0, 248, + 28, 109, 43, 51, 1, 101, 43, 224, 9, 155, 0, 55, 1, 28, 26, 155, 1, 24, 210, 146, 0, 143, 171, 66, 159, 211, 172, 38, + 0, 176, 3, 28, 48, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 96, 80, 19, 112, 106, 12, 19, 112, 106, 144, 19, 112, + 105, 116, 71, 32, 70, 192, 181, 16, 104, 3, 28, 4, 109, 26, 58, 1, 101, 26, 104, 64, 247, 251, 248, 123, 28, 32, 247, + 251, 248, 120, 188, 16, 188, 1, 71, 0, 0, 0, 181, 240, 176, 137, 104, 5, 147, 0, 106, 107, 146, 1, 28, 6, 30, 154, 28, + 8, 64, 208, 4, 2, 12, 18, 104, 104, 146, 4, 70, 132, 152, 4, 104, 116, 48, 128, 0, 64, 90, 32, 122, 47, 106, 42, 144, + 2, 36, 1, 40, 0, 209, 0, 224, 194, 58, 1, 64, 250, 27, 219, 146, 6, 63, 2, 28, 10, 64, 250, 147, 5, 155, 6, 28, 23, + 70, 96, 64, 31, 8, 131, 59, 1, 28, 26, 64, 10, 146, 7, 209, 0, 224, 133, 155, 2, 152, 5, 106, 233, 64, 131, 24, 121, + 147, 3, 24, 201, 75, 87, 104, 26, 108, 235, 66, 138, 209, 5, 74, 85, 28, 24, 104, 17, 74, 85, 104, 18, 224, 18, 107, + 44, 107, 168, 34, 1, 240, 0, 248, 165, 30, 4, 208, 0, 224, 149, 106, 235, 152, 3, 73, 76, 24, 251, 24, 27, 96, 11, 75, + 75, 108, 233, 104, 24, 75, 75, 104, 26, 240, 0, 250, 227, 154, 7, 104, 108, 0, 145, 155, 0, 26, 100, 66, 156, 217, 0, + 28, 28, 108, 235, 152, 1, 24, 89, 28, 34, 240, 0, 250, 213, 152, 0, 153, 1, 27, 0, 25, 9, 144, 0, 145, 1, 55, 1, 40, + 0, 208, 71, 154, 6, 66, 186, 210, 68, 155, 4, 104, 114, 51, 1, 4, 27, 12, 27, 147, 4, 51, 128, 0, 91, 90, 211, 36, 1, + 147, 2, 39, 0, 43, 0, 209, 54, 224, 95, 122, 43, 152, 0, 64, 216, 106, 43, 25, 194, 144, 3, 66, 154, 217, 1, 27, 219, + 147, 3, 154, 5, 106, 233, 155, 2, 24, 121, 64, 147, 24, 201, 107, 168, 107, 44, 154, 3, 155, 1, 240, 0, 248, 86, 40, + 0, 209, 70, 122, 43, 152, 3, 153, 0, 64, 152, 154, 3, 26, 9, 28, 3, 145, 0, 24, 191, 41, 0, 208, 15, 152, 6, 66, 184, + 210, 12, 154, 4, 104, 113, 50, 1, 4, 18, 12, 18, 146, 4, 50, 128, 0, 82, 90, 138, 146, 2, 42, 0, 208, 42, 39, 0, 153, + 1, 24, 201, 145, 1, 104, 107, 154, 0, 66, 154, 210, 197, 36, 0, 42, 0, 208, 34, 155, 5, 158, 2, 106, 233, 64, 158, 24, + 121, 107, 44, 107, 168, 25, 137, 108, 235, 34, 1, 240, 0, 248, 35, 30, 4, 209, 20, 106, 235, 108, 233, 24, 255, 75, + 11, 25, 190, 96, 30, 75, 11, 104, 24, 75, 11, 104, 26, 240, 0, 250, 99, 108, 233, 152, 1, 154, 0, 240, 0, 250, 94, 224, + 2, 36, 1, 224, 0, 28, 4, 176, 9, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 96, 80, 19, 112, 107, 100, 19, 112, + 107, 108, 71, 32, 70, 192, 239, 0, 0, 204, 225, 47, 255, 30, 225, 160, 0, 0, 225, 160, 0, 0, 230, 0, 0, 16, 225, 47, + 255, 30, 230, 0, 0, 48, 225, 47, 255, 30, 230, 0, 0, 80, 225, 47, 255, 30, 230, 0, 0, 112, 225, 47, 255, 30, 230, 0, + 0, 144, 225, 47, 255, 30, 230, 0, 0, 176, 225, 47, 255, 30, 230, 0, 0, 208, 225, 47, 255, 30, 230, 0, 0, 240, 225, 47, + 255, 30, 230, 0, 1, 16, 225, 47, 255, 30, 230, 0, 1, 48, 225, 47, 255, 30, 230, 0, 1, 80, 225, 47, 255, 30, 230, 0, + 1, 112, 225, 47, 255, 30, 230, 0, 1, 144, 225, 47, 255, 30, 230, 0, 1, 176, 225, 47, 255, 30, 230, 0, 1, 208, 225, 47, + 255, 30, 230, 0, 1, 240, 225, 47, 255, 30, 230, 0, 2, 16, 225, 47, 255, 30, 230, 0, 2, 48, 225, 47, 255, 30, 230, 0, + 2, 80, 225, 47, 255, 30, 230, 0, 2, 112, 225, 47, 255, 30, 230, 0, 2, 144, 225, 47, 255, 30, 230, 0, 2, 176, 225, 47, + 255, 30, 230, 0, 2, 208, 225, 47, 255, 30, 230, 0, 2, 240, 225, 47, 255, 30, 230, 0, 3, 16, 225, 47, 255, 30, 230, 0, + 3, 48, 225, 47, 255, 30, 230, 0, 3, 80, 225, 47, 255, 30, 230, 0, 3, 112, 225, 47, 255, 30, 230, 0, 3, 144, 225, 47, + 255, 30, 230, 0, 3, 176, 225, 47, 255, 30, 230, 0, 3, 208, 225, 47, 255, 30, 230, 0, 3, 240, 225, 47, 255, 30, 230, + 0, 4, 16, 225, 47, 255, 30, 230, 0, 4, 48, 225, 47, 255, 30, 230, 0, 4, 80, 225, 47, 255, 30, 230, 0, 4, 112, 225, 47, + 255, 30, 230, 0, 4, 144, 225, 47, 255, 30, 230, 0, 4, 176, 225, 47, 255, 30, 230, 0, 4, 208, 225, 47, 255, 30, 230, + 0, 4, 240, 225, 47, 255, 30, 230, 0, 5, 16, 225, 47, 255, 30, 230, 0, 5, 48, 225, 47, 255, 30, 230, 0, 5, 80, 225, 47, + 255, 30, 230, 0, 6, 144, 225, 47, 255, 30, 230, 0, 7, 240, 225, 47, 255, 30, 230, 0, 8, 16, 225, 47, 255, 30, 230, 0, + 10, 16, 225, 47, 255, 30, 226, 144, 16, 0, 227, 176, 0, 4, 239, 0, 0, 171, 225, 47, 255, 30, 225, 160, 0, 0, 225, 160, + 0, 0, 180, 3, 70, 113, 8, 73, 0, 64, 0, 73, 90, 9, 0, 73, 68, 142, 188, 3, 71, 112, 226, 81, 32, 1, 1, 47, 255, 30, + 58, 0, 0, 54, 225, 80, 0, 1, 154, 0, 0, 34, 225, 17, 0, 2, 10, 0, 0, 35, 227, 17, 2, 14, 1, 160, 17, 129, 3, 160, 48, + 8, 19, 160, 48, 1, 227, 81, 2, 1, 49, 81, 0, 0, 49, 160, 18, 1, 49, 160, 50, 3, 58, 255, 255, 250, 227, 81, 1, 2, 49, + 81, 0, 0, 49, 160, 16, 129, 49, 160, 48, 131, 58, 255, 255, 250, 227, 160, 32, 0, 225, 80, 0, 1, 32, 64, 0, 1, 33, 130, + 32, 3, 225, 80, 0, 161, 32, 64, 0, 161, 33, 130, 32, 163, 225, 80, 1, 33, 32, 64, 1, 33, 33, 130, 33, 35, 225, 80, 1, + 161, 32, 64, 1, 161, 33, 130, 33, 163, 227, 80, 0, 0, 17, 176, 50, 35, 17, 160, 18, 33, 26, 255, 255, 239, 225, 160, + 0, 2, 225, 47, 255, 30, 3, 160, 0, 1, 19, 160, 0, 0, 225, 47, 255, 30, 227, 81, 8, 1, 33, 160, 24, 33, 35, 160, 32, + 16, 51, 160, 32, 0, 227, 81, 12, 1, 33, 160, 20, 33, 34, 130, 32, 8, 227, 81, 0, 16, 33, 160, 18, 33, 34, 130, 32, 4, + 227, 81, 0, 4, 130, 130, 32, 3, 144, 130, 32, 161, 225, 160, 2, 48, 225, 47, 255, 30, 225, 47, 255, 31, 225, 160, 0, + 0, 227, 80, 0, 0, 19, 224, 0, 0, 234, 0, 1, 11, 227, 81, 0, 0, 10, 255, 255, 248, 233, 45, 64, 3, 235, 255, 255, 188, + 232, 189, 64, 6, 224, 3, 0, 146, 224, 65, 16, 3, 225, 47, 255, 30, 227, 81, 0, 0, 10, 0, 0, 67, 224, 32, 192, 1, 66, + 97, 16, 0, 226, 81, 32, 1, 10, 0, 0, 39, 225, 176, 48, 0, 66, 96, 48, 0, 225, 83, 0, 1, 154, 0, 0, 38, 225, 17, 0, 2, + 10, 0, 0, 40, 227, 17, 2, 14, 1, 160, 17, 129, 3, 160, 32, 8, 19, 160, 32, 1, 227, 81, 2, 1, 49, 81, 0, 3, 49, 160, + 18, 1, 49, 160, 34, 2, 58, 255, 255, 250, 227, 81, 1, 2, 49, 81, 0, 3, 49, 160, 16, 129, 49, 160, 32, 130, 58, 255, + 255, 250, 227, 160, 0, 0, 225, 83, 0, 1, 32, 67, 48, 1, 33, 128, 0, 2, 225, 83, 0, 161, 32, 67, 48, 161, 33, 128, 0, + 162, 225, 83, 1, 33, 32, 67, 49, 33, 33, 128, 1, 34, 225, 83, 1, 161, 32, 67, 49, 161, 33, 128, 1, 162, 227, 83, 0, + 0, 17, 176, 34, 34, 17, 160, 18, 33, 26, 255, 255, 239, 227, 92, 0, 0, 66, 96, 0, 0, 225, 47, 255, 30, 225, 60, 0, 0, + 66, 96, 0, 0, 225, 47, 255, 30, 51, 160, 0, 0, 1, 160, 15, 204, 3, 128, 0, 1, 225, 47, 255, 30, 227, 81, 8, 1, 33, 160, + 24, 33, 35, 160, 32, 16, 51, 160, 32, 0, 227, 81, 12, 1, 33, 160, 20, 33, 34, 130, 32, 8, 227, 81, 0, 16, 33, 160, 18, + 33, 34, 130, 32, 4, 227, 81, 0, 4, 130, 130, 32, 3, 144, 130, 32, 161, 227, 92, 0, 0, 225, 160, 2, 51, 66, 96, 0, 0, + 225, 47, 255, 30, 225, 47, 255, 31, 225, 160, 0, 0, 227, 80, 0, 0, 195, 224, 1, 2, 179, 160, 1, 2, 234, 0, 0, 183, 227, + 81, 0, 0, 10, 255, 255, 247, 233, 45, 64, 3, 235, 255, 255, 177, 232, 189, 64, 6, 224, 3, 0, 146, 224, 65, 16, 3, 225, + 47, 255, 30, 71, 112, 70, 192, 33, 16, 6, 2, 14, 3, 65, 200, 67, 19, 6, 2, 14, 0, 67, 16, 65, 200, 67, 24, 71, 112, + 70, 192, 181, 112, 28, 4, 28, 13, 42, 3, 217, 33, 28, 11, 67, 3, 7, 158, 208, 18, 120, 32, 120, 41, 66, 136, 209, 29, + 58, 1, 35, 0, 224, 5, 52, 1, 51, 1, 120, 32, 92, 233, 66, 136, 209, 20, 66, 154, 209, 247, 32, 0, 188, 112, 188, 2, + 71, 8, 28, 13, 28, 4, 201, 8, 200, 64, 66, 158, 209, 4, 58, 4, 28, 4, 28, 13, 42, 3, 216, 244, 32, 0, 42, 0, 209, 222, + 231, 237, 26, 64, 231, 235, 70, 192, 181, 240, 28, 5, 28, 14, 28, 20, 42, 15, 217, 3, 28, 11, 67, 3, 7, 159, 208, 10, + 44, 0, 208, 5, 35, 0, 92, 242, 84, 234, 51, 1, 66, 163, 209, 250, 188, 240, 188, 2, 71, 8, 28, 21, 28, 12, 28, 3, 104, + 38, 96, 30, 104, 102, 96, 94, 104, 166, 96, 158, 104, 230, 61, 16, 96, 222, 52, 16, 51, 16, 45, 15, 216, 242, 58, 16, + 9, 23, 28, 126, 1, 63, 1, 54, 27, 215, 25, 133, 28, 60, 25, 142, 47, 3, 217, 217, 28, 52, 28, 59, 28, 42, 204, 2, 59, + 4, 194, 2, 43, 3, 216, 250, 63, 4, 8, 188, 28, 99, 0, 155, 0, 164, 24, 237, 24, 246, 27, 60, 231, 200, 70, 192, 181, + 112, 28, 3, 7, 132, 208, 13, 42, 0, 208, 64, 6, 13, 58, 1, 14, 45, 36, 3, 224, 2, 42, 0, 208, 57, 58, 1, 112, 29, 51, + 1, 66, 35, 209, 248, 42, 3, 217, 41, 37, 255, 64, 13, 2, 44, 67, 37, 4, 44, 28, 30, 67, 37, 42, 15, 217, 18, 28, 28, + 28, 22, 62, 16, 96, 37, 96, 101, 96, 165, 96, 229, 52, 16, 46, 15, 216, 247, 58, 16, 9, 22, 54, 1, 1, 54, 25, 158, 35, + 15, 64, 26, 42, 3, 217, 12, 28, 52, 28, 19, 59, 4, 196, 32, 43, 3, 216, 251, 58, 4, 8, 147, 51, 1, 0, 155, 24, 246, + 35, 3, 64, 26, 28, 51, 42, 0, 208, 6, 6, 9, 14, 12, 33, 0, 84, 92, 49, 1, 66, 138, 209, 251, 188, 112, 188, 2, 71, 8, + 120, 2, 120, 11, 48, 1, 49, 1, 42, 0, 208, 1, 66, 154, 208, 247, 26, 208, 71, 112, 35, 0, 92, 194, 51, 1, 42, 0, 209, + 251, 30, 88, 71, 112, 70, 192, 181, 240, 28, 3, 32, 0, 42, 0, 208, 72, 28, 8, 67, 24, 36, 3, 30, 85, 64, 4, 209, 42, + 28, 30, 28, 13, 42, 3, 217, 67, 104, 31, 104, 8, 66, 135, 209, 63, 58, 4, 28, 32, 42, 0, 208, 54, 72, 34, 24, 61, 67, + 189, 79, 33, 28, 32, 66, 61, 209, 47, 29, 28, 29, 8, 224, 11, 204, 8, 200, 2, 66, 139, 209, 45, 58, 4, 42, 0, 208, 40, + 77, 25, 25, 89, 67, 153, 66, 57, 209, 35, 28, 38, 28, 5, 42, 3, 216, 239, 28, 35, 28, 1, 42, 0, 208, 33, 30, 85, 120, + 28, 120, 8, 66, 132, 209, 18, 32, 0, 45, 0, 208, 16, 44, 0, 208, 14, 61, 1, 34, 0, 224, 4, 66, 170, 208, 12, 50, 1, + 44, 0, 208, 9, 24, 152, 120, 68, 24, 136, 120, 64, 66, 132, 208, 244, 26, 32, 188, 240, 188, 2, 71, 8, 32, 0, 231, 250, + 28, 41, 28, 51, 30, 85, 231, 222, 120, 36, 120, 0, 26, 32, 231, 242, 70, 192, 254, 254, 254, 255, 128, 128, 128, 128, + 0, 0, 0, 0, 71, 120, 70, 192, 234, 255, 254, 125, 71, 120, 70, 192, 234, 255, 254, 137, 71, 120, 70, 192, 234, 255, + 254, 123, 71, 120, 70, 192, 234, 255, 254, 253, 71, 120, 70, 192, 234, 255, 233, 149, 71, 120, 70, 192, 234, 255, 254, + 119, 71, 120, 70, 192, 234, 255, 254, 184, 71, 120, 70, 192, 234, 255, 254, 163, 71, 120, 70, 192, 234, 255, 233, 174, + 229, 159, 192, 0, 225, 47, 255, 28, 19, 112, 87, 81, 71, 120, 70, 192, 234, 255, 254, 124, 71, 120, 70, 192, 234, 255, + 254, 244, 71, 120, 70, 192, 234, 255, 255, 62, 71, 120, 70, 192, 234, 255, 254, 84, 71, 120, 70, 192, 234, 255, 254, + 80, 71, 120, 70, 192, 234, 255, 254, 112, 71, 120, 70, 192, 234, 255, 254, 86, 71, 120, 70, 192, 234, 255, 254, 88, + 71, 120, 70, 192, 234, 255, 233, 162, 71, 120, 70, 192, 234, 255, 254, 96, 71, 120, 70, 192, 234, 255, 254, 144, 71, + 120, 70, 192, 234, 255, 233, 152, 71, 120, 70, 192, 234, 255, 254, 136, 71, 120, 70, 192, 234, 255, 254, 84, 71, 120, + 70, 192, 234, 255, 233, 106, 71, 120, 70, 192, 234, 255, 254, 124, 71, 120, 70, 192, 234, 255, 254, 34, 71, 120, 70, + 192, 234, 255, 254, 66, 71, 120, 70, 192, 234, 255, 233, 94, 71, 120, 70, 192, 234, 255, 254, 82, 71, 120, 70, 192, + 234, 255, 254, 118, 71, 120, 70, 192, 234, 255, 254, 34, 0, 0, 0, 0, 73, 79, 83, 32, 109, 111, 100, 117, 108, 101, 0, + 0, 255, 255, 255, 255, 1, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 229, 31, + 240, 4, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 15, 66, 64, 19, 114, 198, 36, 16, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 87, 66, 70, 83, 32, 110, 111, 116, 32, 101, + 110, 111, 117, 103, 104, 32, 109, 101, 109, 111, 114, 121, 33, 10, 0, 47, 100, 101, 118, 47, 117, 115, 98, 49, 50, 51, + 0, 47, 100, 101, 118, 47, 117, 115, 98, 49, 50, 51, 47, 79, 70, 70, 0, 102, 105, 114, 115, 116, 32, 114, 101, 97, 100, + 32, 115, 101, 99, 116, 111, 114, 32, 40, 37, 105, 41, 32, 79, 75, 10, 0, 102, 105, 114, 115, 116, 32, 114, 101, 97, + 100, 32, 115, 101, 99, 116, 111, 114, 32, 40, 37, 105, 41, 32, 69, 82, 82, 79, 82, 10, 0, 101, 104, 99, 105, 95, 105, + 110, 116, 95, 119, 111, 114, 107, 105, 110, 103, 95, 99, 97, 108, 108, 98, 97, 99, 107, 95, 112, 97, 114, 116, 49, 44, + 32, 116, 105, 109, 101, 111, 117, 116, 58, 32, 37, 117, 10, 0, 117, 114, 98, 32, 114, 101, 116, 118, 97, 108, 58, 32, + 37, 105, 10, 0, 117, 110, 97, 98, 108, 101, 32, 116, 111, 32, 103, 101, 116, 32, 100, 101, 118, 105, 99, 101, 32, 100, + 101, 115, 99, 46, 46, 46, 10, 0, 103, 101, 116, 116, 105, 110, 103, 32, 85, 83, 66, 95, 82, 69, 81, 95, 71, 69, 84, + 68, 69, 83, 67, 82, 73, 80, 84, 79, 82, 32, 45, 32, 114, 101, 115, 101, 116, 10, 0, 101, 114, 114, 111, 114, 32, 103, + 101, 116, 116, 105, 110, 103, 32, 85, 83, 66, 95, 82, 69, 81, 95, 71, 69, 84, 68, 69, 83, 67, 82, 73, 80, 84, 79, 82, + 10, 0, 103, 101, 116, 116, 105, 110, 103, 32, 85, 83, 66, 95, 82, 69, 81, 95, 71, 69, 84, 68, 69, 83, 67, 82, 73, 80, + 84, 79, 82, 32, 45, 32, 97, 100, 113, 117, 105, 114, 101, 32, 45, 32, 114, 101, 115, 101, 116, 10, 0, 85, 83, 66, 95, + 82, 69, 81, 95, 71, 69, 84, 68, 69, 83, 67, 82, 73, 80, 84, 79, 82, 32, 111, 107, 10, 0, 116, 114, 121, 105, 110, 103, + 32, 85, 83, 66, 95, 82, 69, 81, 95, 83, 69, 84, 65, 68, 68, 82, 69, 83, 83, 58, 32, 37, 100, 10, 0, 117, 110, 97, 98, + 108, 101, 32, 116, 111, 32, 115, 101, 116, 32, 100, 101, 118, 105, 99, 101, 32, 97, 100, 100, 114, 58, 32, 37, 100, + 10, 0, 85, 83, 66, 95, 82, 69, 81, 95, 83, 69, 84, 65, 68, 68, 82, 69, 83, 83, 32, 111, 107, 58, 32, 37, 100, 10, 0, + 101, 114, 114, 111, 114, 32, 99, 104, 101, 99, 107, 105, 110, 103, 32, 85, 83, 66, 95, 82, 69, 81, 95, 71, 69, 84, 68, + 69, 83, 67, 82, 73, 80, 84, 79, 82, 10, 0, 111, 107, 32, 99, 104, 101, 99, 107, 105, 110, 103, 32, 85, 83, 66, 95, 82, + 69, 81, 95, 71, 69, 84, 68, 69, 83, 67, 82, 73, 80, 84, 79, 82, 10, 0, 105, 110, 105, 116, 32, 111, 107, 10, 0, 98, + 117, 102, 102, 101, 114, 32, 61, 61, 32, 78, 85, 76, 76, 32, 40, 110, 111, 32, 109, 101, 109, 41, 10, 0, 95, 95, 117, + 115, 98, 95, 103, 101, 116, 100, 101, 115, 99, 32, 101, 114, 114, 111, 114, 32, 85, 83, 66, 95, 68, 84, 95, 68, 69, + 86, 73, 67, 69, 58, 32, 114, 101, 116, 114, 121, 10, 0, 95, 95, 117, 115, 98, 95, 103, 101, 116, 100, 101, 115, 99, + 32, 101, 114, 114, 111, 114, 32, 85, 83, 66, 95, 68, 84, 95, 68, 69, 86, 73, 67, 69, 10, 0, 117, 100, 100, 45, 62, 99, + 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 115, 32, 61, 61, 32, 78, 85, 76, 76, 32, 40, 110, 111, 32, + 109, 101, 109, 41, 10, 0, 95, 95, 117, 115, 98, 95, 103, 101, 116, 100, 101, 115, 99, 32, 101, 114, 114, 111, 114, 32, + 85, 83, 66, 95, 68, 84, 95, 67, 79, 78, 70, 73, 71, 58, 32, 114, 101, 116, 114, 121, 10, 0, 117, 99, 100, 45, 62, 105, + 110, 116, 101, 114, 102, 97, 99, 101, 115, 32, 61, 61, 32, 78, 85, 76, 76, 32, 40, 110, 111, 32, 109, 101, 109, 41, + 10, 0, 117, 105, 100, 45, 62, 101, 110, 100, 112, 111, 105, 110, 116, 115, 32, 61, 61, 32, 78, 85, 76, 76, 32, 40, 110, + 111, 32, 109, 101, 109, 41, 10, 0, 117, 105, 100, 45, 62, 101, 120, 116, 114, 97, 32, 61, 61, 32, 78, 85, 76, 76, 32, + 40, 110, 111, 32, 109, 101, 109, 41, 10, 0, 10, 113, 116, 100, 32, 101, 114, 114, 111, 114, 33, 58, 0, 32, 66, 65, 66, + 66, 76, 69, 0, 32, 32, 109, 105, 115, 115, 101, 100, 32, 109, 105, 99, 114, 111, 32, 102, 114, 97, 109, 101, 0, 32, + 32, 100, 97, 116, 97, 98, 117, 102, 102, 101, 114, 32, 101, 114, 114, 111, 114, 0, 32, 119, 114, 111, 110, 103, 32, + 97, 99, 107, 0, 32, 116, 111, 111, 32, 109, 97, 110, 121, 32, 101, 114, 114, 111, 114, 115, 0, 105, 110, 116, 101, 114, + 114, 117, 112, 116, 95, 99, 97, 108, 108, 98, 97, 99, 107, 95, 104, 97, 110, 100, 32, 83, 84, 83, 95, 73, 78, 84, 10, + 0, 105, 110, 116, 101, 114, 114, 117, 112, 116, 95, 99, 97, 108, 108, 98, 97, 99, 107, 95, 104, 97, 110, 100, 32, 83, + 84, 83, 95, 80, 67, 68, 10, 0, 117, 115, 98, 115, 116, 111, 114, 97, 103, 101, 32, 114, 101, 115, 101, 116, 58, 32, + 66, 85, 76, 75, 32, 82, 69, 83, 69, 84, 32, 37, 105, 10, 0, 117, 115, 98, 115, 116, 111, 114, 97, 103, 101, 32, 114, + 101, 115, 101, 116, 58, 32, 99, 108, 101, 97, 114, 104, 97, 108, 116, 32, 105, 110, 32, 114, 101, 116, 32, 37, 105, + 10, 0, 117, 115, 98, 115, 116, 111, 114, 97, 103, 101, 32, 114, 101, 115, 101, 116, 58, 32, 99, 108, 101, 97, 114, 104, + 97, 108, 116, 32, 111, 117, 116, 32, 114, 101, 116, 32, 37, 105, 10, 0, 117, 115, 98, 115, 116, 111, 114, 97, 103, 101, + 32, 114, 101, 115, 101, 116, 58, 32, 85, 83, 66, 95, 71, 101, 116, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, + 111, 110, 32, 114, 101, 116, 32, 37, 105, 10, 0, 114, 101, 115, 101, 116, 32, 111, 107, 10, 0, 95, 95, 115, 101, 110, + 100, 95, 99, 98, 119, 32, 114, 101, 116, 32, 37, 105, 10, 0, 95, 95, 85, 83, 66, 95, 66, 108, 107, 77, 115, 103, 84, + 105, 109, 101, 111, 117, 116, 32, 37, 105, 10, 0, 95, 95, 114, 101, 97, 100, 95, 99, 115, 119, 32, 37, 105, 10, 0, 32, + 32, 32, 32, 83, 67, 83, 73, 95, 84, 69, 83, 84, 95, 85, 78, 73, 84, 95, 82, 69, 65, 68, 89, 32, 114, 101, 116, 32, 37, + 105, 10, 0, 32, 32, 32, 32, 83, 67, 83, 73, 95, 82, 69, 81, 85, 69, 83, 84, 95, 83, 69, 78, 83, 69, 32, 114, 101, 116, + 32, 37, 105, 10, 0, 32, 32, 32, 32, 83, 67, 83, 73, 95, 82, 69, 81, 85, 69, 83, 84, 95, 83, 69, 78, 83, 69, 32, 115, + 116, 97, 116, 117, 115, 32, 37, 120, 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, + 41, 58, 32, 85, 83, 66, 95, 71, 101, 116, 68, 101, 115, 99, 114, 105, 112, 116, 111, 114, 115, 32, 37, 105, 10, 0, 85, + 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 100, 101, 118, 105, 99, 101, 32, 99, + 104, 97, 110, 103, 101, 100, 33, 33, 33, 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, + 40, 41, 58, 32, 117, 99, 100, 32, 37, 105, 32, 80, 111, 119, 101, 114, 32, 37, 105, 32, 109, 65, 10, 0, 85, 83, 66, + 83, 116, 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 105, 110, 116, 101, 114, 102, 97, 99, 101, 32, + 115, 117, 98, 99, 108, 97, 115, 115, 32, 37, 105, 32, 97, 116, 97, 95, 112, 114, 111, 116, 32, 37, 105, 32, 10, 0, 73, + 110, 32, 80, 111, 105, 110, 116, 58, 32, 37, 105, 10, 0, 79, 117, 116, 32, 80, 111, 105, 110, 116, 58, 32, 37, 105, + 10, 0, 101, 112, 95, 105, 110, 32, 37, 120, 32, 101, 112, 95, 111, 117, 116, 32, 37, 120, 10, 0, 85, 83, 66, 83, 116, + 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 99, 97, 110, 110, 111, 116, 32, 102, 105, 110, 100, 32, + 97, 110, 121, 32, 105, 110, 116, 101, 114, 102, 97, 99, 101, 33, 33, 33, 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, + 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 99, 111, 110, 102, 58, 32, 37, 120, 32, 97, 108, 116, 73, 110, 116, 101, + 114, 102, 97, 99, 101, 58, 32, 37, 120, 10, 0, 85, 83, 66, 95, 71, 101, 116, 67, 111, 110, 102, 105, 103, 117, 114, + 97, 116, 105, 111, 110, 40, 41, 32, 69, 114, 114, 111, 114, 46, 32, 67, 111, 110, 116, 105, 110, 117, 101, 46, 10, 0, + 65, 99, 116, 117, 97, 108, 32, 99, 111, 110, 102, 58, 32, 37, 120, 32, 32, 32, 110, 101, 120, 116, 32, 99, 111, 110, + 102, 58, 32, 37, 120, 10, 0, 85, 83, 66, 95, 83, 101, 116, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, + 110, 40, 41, 32, 69, 114, 114, 111, 114, 10, 0, 85, 83, 66, 95, 83, 101, 116, 65, 108, 116, 101, 114, 110, 97, 116, + 105, 118, 101, 73, 110, 116, 101, 114, 102, 97, 99, 101, 40, 41, 32, 69, 114, 114, 111, 114, 46, 32, 67, 111, 110, 116, + 105, 110, 117, 101, 10, 0, 85, 83, 66, 95, 83, 101, 116, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, + 40, 41, 32, 38, 32, 85, 83, 66, 95, 83, 101, 116, 65, 108, 116, 101, 114, 110, 97, 116, 105, 118, 101, 73, 110, 116, + 101, 114, 102, 97, 99, 101, 40, 41, 32, 79, 75, 10, 0, 71, 101, 116, 95, 77, 97, 120, 95, 76, 117, 110, 40, 41, 58, + 32, 101, 114, 114, 44, 32, 100, 101, 102, 97, 117, 108, 116, 32, 109, 97, 120, 95, 108, 117, 110, 61, 56, 10, 0, 71, + 101, 116, 95, 77, 97, 120, 95, 76, 117, 110, 40, 41, 58, 32, 79, 75, 58, 32, 37, 105, 10, 0, 85, 83, 66, 83, 116, 111, + 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 116, 114, 121, 95, 115, 116, 97, 116, 117, 115, 32, 37, 105, + 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 79, 75, 44, 32, 114, 101, + 116, 117, 114, 110, 32, 48, 10, 0, 32, 32, 32, 32, 115, 116, 97, 114, 116, 95, 115, 116, 111, 112, 32, 99, 109, 100, + 32, 114, 101, 116, 32, 37, 105, 10, 0, 32, 32, 32, 32, 73, 110, 113, 117, 105, 114, 121, 32, 114, 101, 116, 32, 37, + 105, 10, 0, 32, 32, 32, 32, 68, 101, 118, 105, 99, 101, 32, 84, 121, 112, 101, 58, 32, 37, 120, 10, 0, 32, 32, 32, 32, + 82, 101, 97, 100, 67, 97, 112, 97, 99, 105, 116, 121, 32, 114, 101, 116, 32, 37, 105, 32, 32, 115, 101, 99, 116, 111, + 114, 95, 115, 105, 122, 101, 58, 32, 37, 117, 32, 32, 115, 101, 99, 116, 111, 114, 115, 58, 32, 37, 117, 10, 0, 70, + 97, 115, 116, 32, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 77, 111, 117, 110, 116, 76, 85, 78, 32, 37, 105, + 35, 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 77, 111, 117, 110, 116, 76, 85, 78, 58, 32, 114, 101, 116, + 32, 37, 105, 10, 0, 85, 83, 66, 83, 84, 79, 82, 65, 71, 69, 95, 71, 69, 84, 95, 77, 65, 88, 95, 76, 85, 78, 32, 114, + 101, 116, 32, 37, 105, 32, 109, 97, 120, 108, 117, 110, 32, 37, 105, 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, + 101, 95, 77, 111, 117, 110, 116, 76, 85, 78, 32, 102, 97, 105, 108, 33, 33, 33, 10, 0, 10, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 10, 82, 111, 100, 114, 105, 101, 115, 32, 101, 104, 99, + 109, 111, 100, 117, 108, 101, 32, 49, 46, 48, 10, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 73, 110, 105, 116, + 40, 41, 10, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 10, 10, 0, 85, 83, + 66, 83, 116, 111, 114, 97, 103, 101, 95, 73, 110, 105, 116, 40, 41, 32, 79, 107, 10, 0, 69, 114, 114, 111, 114, 32, + 82, 101, 97, 100, 105, 110, 103, 32, 115, 101, 99, 116, 111, 114, 32, 48, 10, 0, 79, 75, 32, 82, 101, 97, 100, 105, + 110, 103, 32, 115, 101, 99, 116, 111, 114, 32, 48, 10, 0, 85, 110, 112, 108, 117, 103, 58, 32, 114, 101, 115, 101, 116, + 32, 37, 105, 32, 115, 116, 97, 116, 117, 115, 32, 37, 120, 10, 0, 102, 97, 115, 116, 95, 114, 101, 109, 111, 117, 110, + 116, 32, 75, 79, 32, 114, 101, 116, 32, 37, 105, 10, 0, 85, 83, 66, 32, 65, 108, 108, 111, 99, 58, 32, 110, 111, 116, + 32, 101, 110, 111, 117, 103, 104, 32, 109, 101, 109, 111, 114, 121, 33, 10, 0, 119, 98, 102, 115, 32, 101, 114, 114, + 111, 114, 32, 0, 98, 97, 100, 32, 109, 97, 103, 105, 99, 0, 104, 100, 32, 115, 101, 99, 116, 111, 114, 32, 115, 105, + 122, 101, 32, 100, 111, 101, 115, 110, 39, 116, 32, 109, 97, 116, 99, 104, 0, 104, 100, 32, 110, 117, 109, 32, 115, + 101, 99, 116, 111, 114, 32, 100, 111, 101, 115, 110, 39, 116, 32, 109, 97, 116, 99, 104, 0, 78, 84, 70, 83, 0, 70, 65, + 84, 0, 116, 114, 121, 105, 110, 103, 32, 116, 111, 32, 99, 108, 111, 115, 101, 32, 119, 98, 102, 115, 32, 119, 104, + 105, 108, 101, 32, 100, 105, 115, 99, 115, 32, 115, 116, 105, 108, 108, 32, 111, 112, 101, 110, 0, 97, 108, 108, 111, + 99, 97, 116, 105, 110, 103, 32, 109, 101, 109, 111, 114, 121, 0 +}; diff --git a/bin/ehcmodule_5.h b/bin/ehcmodule_5.h new file mode 100644 index 00000000..0f7988a4 --- /dev/null +++ b/bin/ehcmodule_5.h @@ -0,0 +1,3 @@ +#define size_ehcmodule_5 26234 + +extern unsigned char ehcmodule_5[26234]; diff --git a/cios_installer/add_dip_plugin.c b/cios_installer/add_dip_plugin.c new file mode 100644 index 00000000..2f06fdea --- /dev/null +++ b/cios_installer/add_dip_plugin.c @@ -0,0 +1,288 @@ +/* Copyright (C) 2008 Mega Man */ +/* Based on Wii GameCube Homebrew Launcher */ +/* Copyright (C) 2008 WiiGator */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "elf.h" +#include "add_dip_plugin.h" +#include "debug_printf.h" + + +#if 0 +// disabled +// XXXXXXXXXXXXXXXXXXXXX + +#include "dip_plugin_bin.h" +#define round_up(x,n) (-(-(x) & -(n))) + +/** Binary is loaded to this address. */ +#if IOS36 +#define BIN_PATCH_ADDR 0x202080e0 +#define EXTENDED_AREA_START 0x20200000 +#define BSS_START 0x2020a000 +#define BSS_SIZE 0x0002c000 +#elif IOS38 +#define BIN_PATCH_ADDR 0x20208200 +#define EXTENDED_AREA_START 0x20208000 +#define BSS_START 0x20209000 +#define BSS_SIZE 0x0002c000 +#else + #ifdef ADD_DIP_PLUGIN + +#error "Hey! i need IOS36 or IOS38 defined!" + +#else +// fake data to compile the code +#define BIN_PATCH_ADDR 0x202080e0 + #define EXTENDED_AREA_START 0x20200000 + #define BSS_START 0x2020a000 + #define BSS_SIZE 0x0002c000 + +#endif +#endif +/** Start of ELF area which is extneded. */ + + +/** Header for Wii ARM binaries. */ + +typedef struct +{ + /** Size of this header. */ + uint32_t headerSize; + /** Offset to ELF file. */ + uint32_t offset; + /** Size of ELF file. */ + uint32_t size; + /** Padded with zeroes. */ + uint32_t resevered; +} + +arm_binary_header_t; + +typedef struct +{ + uint32_t cmd; + uint32_t function_orig; + uint32_t function_patch; +} + +jmp_table_patch_entry_t; + +/** + * Copy program sections of ELF file to new ELF file; + * @param buffer Pointer to ELF file. + */ +static int copy_sections(uint8_t *buffer, uint8_t *out) +{ + Elf32_Ehdr_t *file_header; + int pos = 0; + int i; + uint32_t outPos = 0; + + /* 0x1000 should be enough to copy ELF header. */ + memcpy(out, buffer, 0x1000); + + /* Use output (copied) elf header. */ + file_header = (Elf32_Ehdr_t *) & out[pos]; + pos += sizeof(Elf32_Ehdr_t); + + if (file_header->magic != ELFMAGIC) + { + debug_printf("Magic 0x%08x is wrong.\n", file_header->magic); + return -2; + } + + outPos = pos + file_header->phnum * sizeof(Elf32_Phdr_t); + //debug_printf("data start = 0x%02x\n", outPos); + + for (i = 0; i < file_header->phnum; i++) + { + Elf32_Phdr_t *program_header; + program_header = (Elf32_Phdr_t *) & out[pos]; + pos += sizeof(Elf32_Phdr_t); + + if ( (program_header->type == PT_LOAD) + && (program_header->memsz != 0) ) + { + unsigned char *src; + unsigned char *dst; + + // Copy to physical address which can be accessed by loader. + src = buffer + program_header->offset; + + if (program_header->offset != sizeof(Elf32_Ehdr_t)) + { + program_header->offset = outPos; + } + + else + { + /* Don't change offset for first section. */ + outPos = program_header->offset; + } + + dst = out + outPos; + + if (program_header->vaddr == EXTENDED_AREA_START) + { + uint32_t origFileSize; + printf("Extended Area finded!!!!\n"); + origFileSize = program_header->filesz; + program_header->filesz = program_header->memsz = BIN_PATCH_ADDR - EXTENDED_AREA_START + dip_plugin_bin_size; + memset(dst, 0, program_header->filesz); + + if (origFileSize != 0) + { + memcpy(dst, src, origFileSize); + } + } + + if (program_header->vaddr == BSS_START) + { + printf("BSS Start finded!!!!\n"); + //debug_printf("Extending BSS.\n"); + program_header->memsz += 0x1000; //BSS_SIZE; + } + + /*debug_printf("VAddr: 0x%08x PAddr: 0x%08x Offset 0x%08x File Size 0x%08x Mem Size 0x%08x\n", + program_header->vaddr, + program_header->paddr, + program_header->offset, + program_header->filesz, + program_header->memsz); + */ + if (program_header->filesz != 0) + { + if (program_header->vaddr == EXTENDED_AREA_START) + { + //debug_printf("Adding dip plugin with binary data in existing ELF section.\n"); + memcpy(dst + BIN_PATCH_ADDR - EXTENDED_AREA_START, dip_plugin_bin, dip_plugin_bin_size); + } + + else + { + memcpy(dst, src, program_header->filesz); + } + + outPos += program_header->filesz; + } + } + } + + return 0; +} + +/** + * Calculate required memory space. + * @param buffer Pointer to ELF file. + */ +static uint32_t calculate_new_size(uint8_t *buffer) +{ + Elf32_Ehdr_t *file_header; + int pos = 0; + int i; + uint32_t maxSize = 0; + + file_header = (Elf32_Ehdr_t *) & buffer[pos]; + pos += sizeof(Elf32_Ehdr_t); + + if (file_header->magic != ELFMAGIC) + { + debug_printf("Magic 0x%08x is wrong.\n", file_header->magic); + return 0; + } + + for (i = 0; i < file_header->phnum; i++) + { + Elf32_Phdr_t *program_header; + + program_header = (Elf32_Phdr_t *) & buffer[pos]; + pos += sizeof(Elf32_Phdr_t); + + if ( (program_header->type == PT_LOAD) + && (program_header->memsz != 0) ) + { + unsigned char *src; + + /*debug_printf("VAddr: 0x%08x PAddr: 0x%08x Offset 0x%08x File Size 0x%08x Mem Size 0x%08x\n", + program_header->vaddr, + program_header->paddr, + program_header->offset, + program_header->filesz, + program_header->memsz); + */ + src = buffer + program_header->offset; + + if (program_header->filesz != 0) + { + uint32_t size; + + size = program_header->offset + program_header->filesz; + + if (size > maxSize) + { + maxSize = size; + } + } + } + } + + /* Real calculation after getting all information .*/ + return maxSize + BIN_PATCH_ADDR - EXTENDED_AREA_START + dip_plugin_bin_size + sizeof(Elf32_Phdr_t); +} + +int add_dip_plugin(uint8_t **buffer) +{ + uint8_t *inElf = NULL; + uint8_t *outElf = NULL; + uint32_t outElfSize; + uint32_t content_size; + + inElf = *buffer; + + debug_printf("Adding DIP plugin\n"); + + outElfSize = calculate_new_size(inElf); + + if (outElfSize <= 0) + { + debug_printf("add dip plugin Patching failed\n"); + return -32; + } + + content_size = round_up(outElfSize, 0x40); + outElf = malloc(content_size); + + if (outElf == NULL) + { + debug_printf("Out of memory\n"); + return -33; + } + + + /* Set default to 0. */ + memset(outElf, 0, content_size); + + debug_printf("\n"); + + if (copy_sections(inElf, outElf) < 0) + { + debug_printf("Failed to patch ELF.\n"); + return -39; + } + + + *buffer = outElf; + free(inElf); + return outElfSize; +} + +// XXXXXXXXXXXXXXXXXXXX +#endif diff --git a/cios_installer/add_dip_plugin.h b/cios_installer/add_dip_plugin.h new file mode 100644 index 00000000..3eafc907 --- /dev/null +++ b/cios_installer/add_dip_plugin.h @@ -0,0 +1,8 @@ +#ifndef _ADD_KERNEL_CODE_H_ +#define _ADD_KERNEL_CODE_H_ + +#include "stdint.h" + +int add_dip_plugin(uint8_t **buffer); + +#endif diff --git a/cios_installer/data/ticket.raw b/cios_installer/data/ticket.raw new file mode 100644 index 00000000..7ce7bc59 Binary files /dev/null and b/cios_installer/data/ticket.raw differ diff --git a/cios_installer/data/tmd.raw b/cios_installer/data/tmd.raw new file mode 100644 index 00000000..f720ef6c Binary files /dev/null and b/cios_installer/data/tmd.raw differ diff --git a/ehcmodule/EHCI.SearchResults b/ehcmodule/EHCI.SearchResults new file mode 100644 index 00000000..3b8ee72f --- /dev/null +++ b/ehcmodule/EHCI.SearchResults @@ -0,0 +1,3 @@ +---- port = Matches (2 in 2 files) ---- +Ehci.c (c:\devkitpro\soft\uloader_v2.6\cios_mload\tinyehci): dev->port = i; +Ehci1.c (c:\devkitpro\soft\uloader_v2.6\cios_mload\tinyehci): dev->port = i; diff --git a/ehcmodule/bin/ehcmodule.elf b/ehcmodule/bin/ehcmodule.elf new file mode 100644 index 00000000..abad624c Binary files /dev/null and b/ehcmodule/bin/ehcmodule.elf differ diff --git a/ehcmodule/bin/ehcmodule.rar b/ehcmodule/bin/ehcmodule.rar new file mode 100644 index 00000000..cb911602 Binary files /dev/null and b/ehcmodule/bin/ehcmodule.rar differ diff --git a/ehcmodule/source/ehc_loop.c.old b/ehcmodule/source/ehc_loop.c.old new file mode 100644 index 00000000..196460b1 --- /dev/null +++ b/ehcmodule/source/ehc_loop.c.old @@ -0,0 +1,498 @@ +/* + Custom IOS module for Wii. + OH0 message loop + Copyright (C) 2009 kwiirk. + Copyright (C) 2008 neimod. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +/******************************************************************************* + * + * oh0_loop.c - IOS module main code + * even if we are "ehc" driver, we still pretend to be "oh0" + * and implement "standard" ios oh0 usb api + * + ******************************************************************************* + * + */ + + + +#include +#include +#include "syscalls.h" +#include "ehci_types.h" +#include "ehci.h" +#include "utils.h" +#include "libwbfs.h" + +void ehci_usleep(int usec); +void ehci_msleep(int msec); + +#undef NULL +#define NULL ((void *)0) +#define IOS_OPEN 0x01 +#define IOS_CLOSE 0x02 +#define IOS_IOCTL 0x06 +#define IOS_IOCTLV 0x07 + +#define USB_IOCTL_CTRLMSG 0 +#define USB_IOCTL_BLKMSG 1 +#define USB_IOCTL_INTRMSG 2 +#define USB_IOCTL_SUSPENDDEV 5 +#define USB_IOCTL_RESUMEDEV 6 +#define USB_IOCTL_GETDEVLIST 12 +#define USB_IOCTL_DEVREMOVALHOOK 26 +#define USB_IOCTL_DEVINSERTHOOK 27 + +#define UMS_BASE (('U'<<24)|('M'<<16)|('S'<<8)) +#define USB_IOCTL_UMS_INIT (UMS_BASE+0x1) +#define USB_IOCTL_UMS_GET_CAPACITY (UMS_BASE+0x2) +#define USB_IOCTL_UMS_READ_SECTORS (UMS_BASE+0x3) +#define USB_IOCTL_UMS_WRITE_SECTORS (UMS_BASE+0x4) + +#define USB_IOCTL_UMS_READ_STRESS (UMS_BASE+0x5) + +#define USB_IOCTL_UMS_SET_VERBOSE (UMS_BASE+0x6) + +#define USB_IOCTL_UMS_UMOUNT (UMS_BASE+0x10) +#define USB_IOCTL_UMS_WATCHDOG (UMS_BASE+0x80) + +#define WBFS_BASE (('W'<<24)|('F'<<16)|('S'<<8)) +#define USB_IOCTL_WBFS_OPEN_DISC (WBFS_BASE+0x1) +#define USB_IOCTL_WBFS_READ_DISC (WBFS_BASE+0x2) + +//#define USB_IOCTL_WBFS_SPEED_LIMIT (WBFS_BASE+0x80) + +void USBStorage_Umount(void); + +//#define DEVICE "/dev/usb/ehc" +#define DEVICE "/dev/usb2" + +int verbose = 0; +#define ioctlv_u8(a) (*((u8*)(a).data)) +#define ioctlv_u16(a) (*((u16*)(a).data)) +#define ioctlv_u32(a) (*((u32*)(a).data)) +#define ioctlv_voidp(a) (a).data + +wbfs_disc_t * wbfs_init_with_partition(u8*discid, int partition); + + +#define WATCHDOG_TIMER 1000*1000*4 + + + + +char *parse_hex(char *base,int *val) +{ + int v = 0,done=0; + char *ptr = base,c; + while(!done) + { + c = *ptr++; + if(c >= '0' && c <= '9') + v = v << 4 | (c-'0'); + else if(c >= 'a' && c <= 'f') + v = v << 4 | (10+c-'a'); + else if(c >= 'A' && c <= 'F') + v = v << 4 | (10+c-'A'); + else + done = 1; + } + if(ptr==base+1)//only incremented once + return 0; //we did not found any hex numbers + *val = v; + return ptr-1; +} +int parse_and_open_device(char *devname,int fd) +{ + char *ptr = devname; + int vid,pid; + if (! (ptr = parse_hex(ptr,&vid))) + return -6; + if ( *ptr != '/' ) + return -6; + ptr++;// skip / + if (! (ptr = parse_hex(ptr,&pid))) + return -6; + if ( *ptr != '\0' ) + return -6; + return ehci_open_device(vid,pid,fd); +} + + +int DVD_speed_limit=0; // ingame it can fix x6 speed + +int watchdog_enable=1; + +// special ingame +int wbfs_disc_read2(wbfs_disc_t*d,u32 offset, u8 *data, u32 len); + +// heap space for WBFS and queue + +extern int heaphandle; + +void msleep(int msec); + +u8 mem_sector[2048] __attribute__ ((aligned (32))); + +void *WBFS_Alloc(int size) +{ + void * ret = 0; + ret= os_heap_alloc(heaphandle, size); + if(ret==0) + {debug_printf("WBFS not enough memory! need %d\n",size); + my_sprint("WBFS not enough memory!", NULL); + while(1) ehci_msleep(100); + } + return ret; +} +void WBFS_Free(void *ptr) +{ + return os_heap_free(heaphandle, ptr); +} + +void my_sprint(char *cad, char *s) +{ +int n=strlen(cad); +int m=0; +int fd; +os_sync_after_write(cad, n); +if(s) {m=strlen(s);os_sync_after_write(s, m);} + +fd=os_open("/dev/fat/log", 1); +if(fd<0) return; +os_write(fd, cad, n); + +if(s) + os_write(fd, s, m); +os_close(fd); +} + +#if 0 + +char my_log[256]; + +void my_dump(char *cad, char *dat, int len) +{ +int n,m; +int fd; + + +fd=ios_open("/dev/fat/log", 1); +if(fd<0) return; + +n=0; +while(*cad) {my_log[n]=*cad++;n++;} +my_log[n]='\n';n++; + +for(m=0;m=253) {ios_write(fd, my_log, n);n=0;} + my_log[n]=((*dat>>8) & 0xf)+48;if(my_log[n]>'9') my_log[n]+=7; n++; + my_log[n]=((*dat) & 0xf)+48;if(my_log[n]>'9') my_log[n]+=7; n++; + my_log[n]=((m & 15)!=15) ? ' ' : '\n'; n++; + dat++; + } + +if(n>0) + { + os_sync_after_write((void *) cad, n); + os_write(fd, my_log, n); + } + +ios_close(fd); +} +#endif + +extern int unplug_device; + +int unplug_procedure(void); + +int ehc_loop(void) +{ + ipcmessage* message; + int timer2_id=-1; + + int last_sector=0; + + int must_read_sectors=0; + + + my_sprint("ehc loop entry", NULL); + + void* queuespace = os_heap_alloc(heaphandle, 0x40); + + + + int queuehandle = os_message_queue_create(queuespace, 16); + + + + os_device_register(DEVICE, queuehandle); + timer2_id=os_create_timer(WATCHDOG_TIMER, WATCHDOG_TIMER, queuehandle, 0x666); + + + int ums_mode = 0; + int already_discovered = 0; + wbfs_disc_t *d = 0; + + int usb_lock=0; + + int watch_time_on=1; + + + while(1) + { + int result = 1; + int ack = 1; + + + // Wait for message to arrive + os_message_queue_receive(queuehandle, (void*)&message, 0); + + // timer message WATCHDOG + if((int) message==0x555) continue; + + if(watch_time_on) + os_stop_timer(timer2_id); // stops watchdog timer + watch_time_on=0; + + + if((int) message==0x666) + { + + if(must_read_sectors && watchdog_enable) + { + int n,m; + + unplug_procedure(); + if(unplug_device==0 && !usb_lock) + { + + n=USBStorage_Get_Capacity((void *) &m); + if(m!=0 && m<2048) // only support sector size minor to 2048 + { + + USBStorage_Read_Sectors(last_sector, 1, mem_sector); + last_sector+=0x1000000/m; // steps of 16MB + if(last_sector>n) last_sector=0; + } + + } + + + watch_time_on=1; + os_restart_timer(timer2_id, WATCHDOG_TIMER); + } + continue; + } + + + //print_hex_dump_bytes("msg",0, message,sizeof(*message)); + switch( message->command ) + { + case IOS_OPEN: + { + //debug_printf("%s try open %sfor fd %d\n",DEVICE,message->open.device,message->open.resultfd); + // Checking device name + if (0 == strcmp(message->open.device, DEVICE)) + { + result = message->open.resultfd; + if(!already_discovered) + ehci_discover(); + already_discovered=1; + } + else if (!ums_mode && 0 == memcmp(message->open.device, DEVICE"/", sizeof(DEVICE))) + result = parse_and_open_device(message->open.device+sizeof(DEVICE),message->open.resultfd); + else + result = -6; + } + break; + + case IOS_CLOSE: + { + //debug_printf("close fd %d\n",message->fd); + if(ums_mode == message->fd) + ums_mode = 0; + else + ehci_close_device(ehci_fd_to_dev(message->fd)); + // do nothing + result = 0; + } + break; + + case IOS_IOCTL: + { + break; + } + case IOS_IOCTLV: + { + ioctlv *vec = message->ioctlv.vector; + void *dev =NULL; + int i,in = message->ioctlv.num_in,io= message->ioctlv.num_io; + if( 0==(message->ioctl.command>>24) && !ums_mode) + dev = ehci_fd_to_dev(message->fd); + os_sync_before_read( vec, (in+io)*sizeof(ioctlv)); + for(i=0;iioctl.command ) + { + case USB_IOCTL_CTRLMSG: + //debug_printf("ctrl message%x\n",dev); + if(!dev)result= -6; + else + result = ehci_control_message(dev,ioctlv_u8(vec[0]),ioctlv_u8(vec[1]), + swab16(ioctlv_u16(vec[2])),swab16(ioctlv_u16(vec[3])), + swab16(ioctlv_u16(vec[4])),ioctlv_voidp(vec[6])); + break; + case USB_IOCTL_BLKMSG: + //debug_printf("bulk message\n"); + if(!dev)result= -6; + else + result = ehci_bulk_message(dev,ioctlv_u8(vec[0]),ioctlv_u16(vec[1]), + ioctlv_voidp(vec[2])); + break; + case USB_IOCTL_INTRMSG: + debug_printf("intr message\n"); + case USB_IOCTL_SUSPENDDEV: + case USB_IOCTL_RESUMEDEV: + debug_printf("or resume/suspend message\n"); + result = 0;//-1;// not supported + break; + case USB_IOCTL_GETDEVLIST: + debug_printf("get dev list\n"); + if(dev)result= -6; + else + result = ehci_get_device_list(ioctlv_u8(vec[0]),ioctlv_u8(vec[1]), + ioctlv_voidp(vec[2]),ioctlv_voidp(vec[3])); + break; + case USB_IOCTL_DEVREMOVALHOOK: + case USB_IOCTL_DEVINSERTHOOK: + debug_printf("removal/insert hook\n"); + ack = 0; // dont reply to those, as we dont detect anything + break; + case USB_IOCTL_UMS_INIT: + must_read_sectors=0; + + result = USBStorage_Init(); + //result=-os_thread_get_priority(); + if(result>=0) {must_read_sectors=1;watchdog_enable=1;} + ums_mode = message->fd; + if(result>=0) my_sprint("UMS Init", NULL); else my_sprint("UMS fail", NULL); + break; + case USB_IOCTL_UMS_UMOUNT: + must_read_sectors=0; + watchdog_enable=0; + USBStorage_Umount(); + result =0; + break; + case USB_IOCTL_UMS_GET_CAPACITY: + result = USBStorage_Get_Capacity(ioctlv_voidp(vec[0])); + break; + case USB_IOCTL_UMS_READ_SECTORS: + if (verbose) + debug_printf("%p read sector %d %d %p\n",&vec[0],ioctlv_u32(vec[0]),ioctlv_u32(vec[1]), ioctlv_voidp(vec[2])); + + result = USBStorage_Read_Sectors(ioctlv_u32(vec[0]),ioctlv_u32(vec[1]), ioctlv_voidp(vec[2])); + + //udelay(ioctlv_u32(vec[1])*125); + if(result) break; + + break; + case USB_IOCTL_UMS_WRITE_SECTORS: + result = USBStorage_Write_Sectors(ioctlv_u32(vec[0]),ioctlv_u32(vec[1]), ioctlv_voidp(vec[2])); + break; + case USB_IOCTL_UMS_READ_STRESS: + result = USBStorage_Read_Stress(ioctlv_u32(vec[0]),ioctlv_u32(vec[1]), ioctlv_voidp(vec[2])); + break; + case USB_IOCTL_UMS_SET_VERBOSE: + verbose = !verbose; + result = 0; + break; + /*case USB_IOCTL_WBFS_SPEED_LIMIT: + DVD_speed_limit=ioctlv_u32(vec[0]); + break;*/ + case USB_IOCTL_UMS_WATCHDOG: + watchdog_enable=ioctlv_u32(vec[0]); + break; + case USB_IOCTL_WBFS_OPEN_DISC: + ums_mode = message->fd; + + int partition=0; + // if (verbose) + debug_printf("ehc:use disc %s\n",ioctlv_voidp(vec[0])); + if(vec[1].len==4) memcpy(&partition, ioctlv_voidp(vec[1]), 4); + d = wbfs_init_with_partition(ioctlv_voidp(vec[0]), partition); + if(!d) + result = -1; + else + {result = 0;watchdog_enable=1;} + my_sprint("WBFS Open()", NULL); + must_read_sectors=1; + break; + case USB_IOCTL_WBFS_READ_DISC: + if (verbose) + debug_printf("r%x %x\n",ioctlv_u32(vec[0]),ioctlv_u32(vec[1])); + else + debug_printf("r%x %x\r",ioctlv_u32(vec[0]),ioctlv_u32(vec[1])); + if(!d || usb_lock) + result = -1; + else + { + usb_lock=1; + //os_stop_timer(timer2_id); + result = wbfs_disc_read2(d,ioctlv_u32(vec[0]),ioctlv_voidp(vec[2]),ioctlv_u32(vec[1])); + usb_lock=0; + /* if(result){ + debug_printf("wbfs failed! %d\n",result); + //result = 0x7800; // wii games shows unrecoverable error.. + result = 0x8000; + }*/ + result=0; + } + + break; + } + for(i=in;i0x20?0x20:vec[i].len); + os_sync_after_write( vec[i].data, vec[i].len); + } + + break; + } + default: + result = -1; + //ack = 0; + break; + } + if(watchdog_enable) + {watch_time_on=1; + os_restart_timer(timer2_id, WATCHDOG_TIMER); + } + //debug_printf("return %d\n",result); + // Acknowledge message + + if (ack) + os_message_queue_ack( (void*)message, result ); + } + + return 0; +} diff --git a/ehcmodule/source/ehc_loop.c.orig b/ehcmodule/source/ehc_loop.c.orig new file mode 100644 index 00000000..59c0412a --- /dev/null +++ b/ehcmodule/source/ehc_loop.c.orig @@ -0,0 +1,522 @@ +/* + Custom IOS module for Wii. + OH0 message loop + Copyright (C) 2009 kwiirk. + Copyright (C) 2008 neimod. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +/******************************************************************************* + * + * oh0_loop.c - IOS module main code + * even if we are "ehc" driver, we still pretend to be "oh0" + * and implement "standard" ios oh0 usb api + * + ******************************************************************************* + * + */ + + + +#include +#include +#include "syscalls.h" +#include "ehci_types.h" +#include "ehci.h" +#include "utils.h" +#include "libwbfs.h" + +void ehci_usleep(int usec); +void ehci_msleep(int msec); + +#undef NULL +#define NULL ((void *)0) +#define IOS_OPEN 0x01 +#define IOS_CLOSE 0x02 +#define IOS_IOCTL 0x06 +#define IOS_IOCTLV 0x07 + +#define USB_IOCTL_CTRLMSG 0 +#define USB_IOCTL_BLKMSG 1 +#define USB_IOCTL_INTRMSG 2 +#define USB_IOCTL_SUSPENDDEV 5 +#define USB_IOCTL_RESUMEDEV 6 +#define USB_IOCTL_GETDEVLIST 12 +#define USB_IOCTL_DEVREMOVALHOOK 26 +#define USB_IOCTL_DEVINSERTHOOK 27 + +#define UMS_BASE (('U'<<24)|('M'<<16)|('S'<<8)) +#define USB_IOCTL_UMS_INIT (UMS_BASE+0x1) +#define USB_IOCTL_UMS_GET_CAPACITY (UMS_BASE+0x2) +#define USB_IOCTL_UMS_READ_SECTORS (UMS_BASE+0x3) +#define USB_IOCTL_UMS_WRITE_SECTORS (UMS_BASE+0x4) + +#define USB_IOCTL_UMS_READ_STRESS (UMS_BASE+0x5) + +#define USB_IOCTL_UMS_SET_VERBOSE (UMS_BASE+0x6) +#define USB_IOCTL_UMS_IS_INSERTED (UMS_BASE+0x7) + +#define USB_IOCTL_UMS_UMOUNT (UMS_BASE+0x10) +#define USB_IOCTL_UMS_START (UMS_BASE+0x11) +#define USB_IOCTL_UMS_STOP (UMS_BASE+0x12) +#define USB_IOCTL_UMS_WATCHDOG (UMS_BASE+0x80) + +#define WBFS_BASE (('W'<<24)|('F'<<16)|('S'<<8)) +#define USB_IOCTL_WBFS_OPEN_DISC (WBFS_BASE+0x1) +#define USB_IOCTL_WBFS_READ_DISC (WBFS_BASE+0x2) + + +s32 USBStorage_Umount(void); +s32 USBStorage_Start(void); +s32 USBStorage_Stop(void); + + +//#define DEVICE "/dev/usb/ehc" +#define DEVICE "/dev/usb2" + +int verbose = 0; +#define ioctlv_u8(a) (*((u8*)(a).data)) +#define ioctlv_u16(a) (*((u16*)(a).data)) +#define ioctlv_u32(a) (*((u32*)(a).data)) +#define ioctlv_voidp(a) (a).data + +wbfs_disc_t * wbfs_init_with_partition(u8*discid, int partition); + + +#define WATCHDOG_TIMER 1000*1000*5 + + +char *parse_hex(char *base,int *val) +{ + int v = 0,done=0; + char *ptr = base,c; + while(!done) + { + c = *ptr++; + if(c >= '0' && c <= '9') + v = v << 4 | (c-'0'); + else if(c >= 'a' && c <= 'f') + v = v << 4 | (10+c-'a'); + else if(c >= 'A' && c <= 'F') + v = v << 4 | (10+c-'A'); + else + done = 1; + } + if(ptr==base+1)//only incremented once + return 0; //we did not found any hex numbers + *val = v; + return ptr-1; +} +int parse_and_open_device(char *devname,int fd) +{ + char *ptr = devname; + int vid,pid; + if (! (ptr = parse_hex(ptr,&vid))) + return -6; + if ( *ptr != '/' ) + return -6; + ptr++;// skip / + if (! (ptr = parse_hex(ptr,&pid))) + return -6; + if ( *ptr != '\0' ) + return -6; + return ehci_open_device(vid,pid,fd); +} + + +int DVD_speed_limit=0; // ingame it can fix x6 speed + +int watchdog_enable=0; + +// special ingame +int wbfs_disc_read2(wbfs_disc_t*d,u32 offset, u8 *data, u32 len); + +// heap space for WBFS and queue + +extern int heaphandle; + +void msleep(int msec); + +u8 mem_sector[2048] __attribute__ ((aligned (32))); + +void *WBFS_Alloc(int size) +{ + void * ret = 0; + ret= os_heap_alloc(heaphandle, size); + if(ret==0) + {debug_printf("WBFS not enough memory! need %d\n",size); + my_sprint("WBFS not enough memory!", NULL); + while(1) ehci_msleep(100); + } + return ret; +} +void WBFS_Free(void *ptr) +{ + return os_heap_free(heaphandle, ptr); +} + +void my_sprint(char *cad, char *s) +{ +#if 0 +int n=strlen(cad); +int m=0; +int fd; +os_sync_after_write(cad, n); +if(s) {m=strlen(s);os_sync_after_write(s, m);} + +fd=os_open("/dev/fat/log", 1); +if(fd<0) return; +os_write(fd, cad, n); + +if(s) + os_write(fd, s, m); +os_close(fd); +#endif +} + +#if 0 + +char my_log[256]; + +void my_dump(char *cad, char *dat, int len) +{ +int n,m; +int fd; + + +fd=ios_open("/dev/fat/log", 1); +if(fd<0) return; + +n=0; +while(*cad) {my_log[n]=*cad++;n++;} +my_log[n]='\n';n++; + +for(m=0;m=253) {ios_write(fd, my_log, n);n=0;} + my_log[n]=((*dat>>8) & 0xf)+48;if(my_log[n]>'9') my_log[n]+=7; n++; + my_log[n]=((*dat) & 0xf)+48;if(my_log[n]>'9') my_log[n]+=7; n++; + my_log[n]=((m & 15)!=15) ? ' ' : '\n'; n++; + dat++; + } + +if(n>0) + { + ios_sync_after_write((void *) cad, n); + ios_write(fd, my_log, n); + } + +ios_close(fd); +} +#endif + +/* +void my_sprint2(char *cad) +{ +int n=strlen(cad); +int fd; +os_sync_after_write(cad, n); +fd=os_open("/dev/fat/log", 1); +if(fd<0) return; +os_write(fd, cad, n); +os_close(fd); +}*/ + +//#ifdef HOMEBREW +extern bool first_access; +//#endif + +int ehc_loop(void) +{ + ipcmessage* message; + int timer2_id=-1; + +//my_sprint2("ehc loop entry"); +// my_sprint("ehc loop entry", NULL); + + void* queuespace = os_heap_alloc(heaphandle, 0x40); + + int queuehandle = os_message_queue_create(queuespace, 32); + + + + + timer2_id=os_create_timer(WATCHDOG_TIMER, WATCHDOG_TIMER, queuehandle, 0x666); + + int ums_mode = 0; + int already_discovered = 0; + wbfs_disc_t *d = 0; + int usb_lock=0; + int cnt_timer=0; + +// wbfs_disc_t *d = 0; + +// int usb_lock=0; + + int watch_time_on=1; + os_device_register(DEVICE, queuehandle); + while(1) + { + int result = 1; + int ack = 1; + + // Wait for message to arrive + os_message_queue_receive(queuehandle, (void*)&message, 0); + + + os_stop_timer(timer2_id); // stops watchdog timer + + // timer message WATCHDOG + if((int) message==0x555) continue; + + if(watch_time_on) + os_stop_timer(timer2_id); // stops watchdog timer + watch_time_on=0; + + + if((int) message==0x666) + { + cnt_timer++; + if(cnt_timer<6) //every 30 secs + { + watch_time_on=1; + os_restart_timer(timer2_id, WATCHDOG_TIMER); + } + else + { + cnt_timer=0; + first_access=true; + watchdog_enable=0; + } + continue; + } + + + + //print_hex_dump_bytes("msg",0, message,sizeof(*message)); + switch( message->command ) + { + case IOS_OPEN: + { +#ifdef HOMEBREW + if (0 == strcmp(message->open.device, DEVICE)) + result = message->open.resultfd; + else + result = -6; + +#else + // Checking device name + if (0 == strcmp(message->open.device, DEVICE)) + { + result = message->open.resultfd; + if(!already_discovered) + ehci_discover(); + already_discovered=1; + } + else if (!ums_mode && 0 == memcmp(message->open.device, DEVICE"/", sizeof(DEVICE))) + result = parse_and_open_device(message->open.device+sizeof(DEVICE),message->open.resultfd); + else + result = -6; +#endif + + } + break; + + case IOS_CLOSE: + { + +#ifndef HOMEBREW + //debug_printf("close fd %d\n",message->fd); + if(ums_mode == message->fd) + ums_mode = 0; + else + ehci_close_device(ehci_fd_to_dev(message->fd)); +#endif + + // do nothing + result = 0; + } + break; + + case IOS_IOCTL: + { + break; + } + case IOS_IOCTLV: + { + ioctlv *vec = message->ioctlv.vector; + void *dev =NULL; + int i,in = message->ioctlv.num_in,io= message->ioctlv.num_io; +#ifdef HOMEBREW + if( 0==(message->ioctl.command>>24)) + dev = ehci_fd_to_dev(message->fd); +#else + if( 0==(message->ioctl.command>>24) && !ums_mode) + dev = ehci_fd_to_dev(message->fd); +#endif + os_sync_before_read( vec, (in+io)*sizeof(ioctlv)); + for(i=0;iioctl.command ) + { + case USB_IOCTL_CTRLMSG: + //debug_printf("ctrl message%x\n",dev); + if(!dev)result= -6; + else + result = ehci_control_message(dev,ioctlv_u8(vec[0]),ioctlv_u8(vec[1]), + swab16(ioctlv_u16(vec[2])),swab16(ioctlv_u16(vec[3])), + swab16(ioctlv_u16(vec[4])),ioctlv_voidp(vec[6])); + break; + case USB_IOCTL_BLKMSG: + //debug_printf("bulk message\n"); + if(!dev)result= -6; + else + result = ehci_bulk_message(dev,ioctlv_u8(vec[0]),ioctlv_u16(vec[1]), + ioctlv_voidp(vec[2])); + break; + case USB_IOCTL_INTRMSG: + debug_printf("intr message\n"); + case USB_IOCTL_SUSPENDDEV: + case USB_IOCTL_RESUMEDEV: + debug_printf("or resume/suspend message\n"); + result = 0;//-1;// not supported + break; + case USB_IOCTL_GETDEVLIST: + debug_printf("get dev list\n"); + if(dev)result= -6; + else + result = ehci_get_device_list(ioctlv_u8(vec[0]),ioctlv_u8(vec[1]), + ioctlv_voidp(vec[2]),ioctlv_voidp(vec[3])); + break; + case USB_IOCTL_DEVREMOVALHOOK: + case USB_IOCTL_DEVINSERTHOOK: + debug_printf("removal/insert hook\n"); + ack = 0; // dont reply to those, as we dont detect anything + break; + case USB_IOCTL_UMS_INIT: + result = USBStorage_Init(); + if(result>=0) watchdog_enable=1; +#ifndef HOMEBREW + ums_mode = message->fd; + //if(result>=0) my_sprint("UMS Init", NULL); else my_sprint("UMS fail", NULL); +#endif + break; + case USB_IOCTL_UMS_UMOUNT: + watchdog_enable=0; + result = USBStorage_Umount(); + break; + case USB_IOCTL_UMS_START: + result = USBStorage_Start(); + break; + case USB_IOCTL_UMS_STOP: + result = USBStorage_Stop(); + break; + case USB_IOCTL_UMS_GET_CAPACITY: + result = USBStorage_Get_Capacity(ioctlv_voidp(vec[0])); + break; + case USB_IOCTL_UMS_READ_SECTORS: + os_stop_timer(timer2_id); + result = USBStorage_Read_Sectors(ioctlv_u32(vec[0]),ioctlv_u32(vec[1]), ioctlv_voidp(vec[2])); + cnt_timer=0; + watchdog_enable=1; + break; + case USB_IOCTL_UMS_WRITE_SECTORS: + os_stop_timer(timer2_id); + result = USBStorage_Write_Sectors(ioctlv_u32(vec[0]),ioctlv_u32(vec[1]), ioctlv_voidp(vec[2])); + cnt_timer=0; + watchdog_enable=1; + break; + case USB_IOCTL_UMS_READ_STRESS: + result = USBStorage_Read_Stress(ioctlv_u32(vec[0]),ioctlv_u32(vec[1]), ioctlv_voidp(vec[2])); + break; + case USB_IOCTL_UMS_SET_VERBOSE: + verbose = !verbose; + result = 0; + break; + case USB_IOCTL_UMS_IS_INSERTED: + result = ehci_is_inserted(); + break; + /*case USB_IOCTL_WBFS_SPEED_LIMIT: + DVD_speed_limit=ioctlv_u32(vec[0]); + break;*/ + case USB_IOCTL_UMS_WATCHDOG: + watchdog_enable=ioctlv_u32(vec[0]); + break; + case USB_IOCTL_WBFS_OPEN_DISC: + ums_mode = message->fd; + + int partition=0; + // if (verbose) + debug_printf("ehc:use disc %s\n",ioctlv_voidp(vec[0])); + if(vec[1].len==4) memcpy(&partition, ioctlv_voidp(vec[1]), 4); + d = wbfs_init_with_partition(ioctlv_voidp(vec[0]), partition); + if(!d) + result = -1; + else + {result = 0;watchdog_enable=1;} + my_sprint("WBFS Open()", NULL); + cnt_timer=0; + break; + case USB_IOCTL_WBFS_READ_DISC: + if(!d || usb_lock) + result = -1; + else + { + usb_lock=1; + //os_stop_timer(timer2_id); + result = wbfs_disc_read2(d,ioctlv_u32(vec[0]),ioctlv_voidp(vec[2]),ioctlv_u32(vec[1])); + usb_lock=0; + cnt_timer=0; + /* if(result){ + debug_printf("wbfs failed! %d\n",result); + //result = 0x7800; // wii games shows unrecoverable error.. + result = 0x8000; + }*/ + result=0; + } + + break; + } + for(i=in;i0x20?0x20:vec[i].len); + os_sync_after_write( vec[i].data, vec[i].len); + } + + break; + } + default: +// os_message_queue_send(queuehandle, (void*)message, 0); + result = -1; + //ack = 0; + break; + } + if(watchdog_enable) + { + watch_time_on=1; + os_restart_timer(timer2_id, WATCHDOG_TIMER); + } + //debug_printf("return %d\n",result); + // Acknowledge message + if (ack) + os_message_queue_ack( (void*)message, result ); + } + + return 0; +} diff --git a/ehcmodule/source/ums.c b/ehcmodule/source/ums.c new file mode 100644 index 00000000..c1d316ca --- /dev/null +++ b/ehcmodule/source/ums.c @@ -0,0 +1,90 @@ +#ifndef HOMEBREW +#include "syscalls.h" +#include "ios_usbstorage.h" +#include + +#define UMS_BASE (('U'<<24)|('M'<<16)|('S'<<8)) +#define USB_IOCTL_UMS_INIT (UMS_BASE+0x1) +#define USB_IOCTL_UMS_GET_CAPACITY (UMS_BASE+0x2) +#define USB_IOCTL_UMS_READ_SECTORS (UMS_BASE+0x3) +#define USB_IOCTL_UMS_WRITE_SECTORS (UMS_BASE+0x4) +#define USB_IOCTL_UMS_READ_STRESS (UMS_BASE+0x5) +#define USB_IOCTL_UMS_SET_VERBOSE (UMS_BASE+0x6) + +static int fd; +static u32 sector_size; +static u32 num_sector; +static int heap; +/* */ +void ums_init(void) +{ + ioctlv *vec; + u32 *p_sector_size; + fd = os_open("/dev/usb2", 1); + + if (fd < 0) + { + debug_printf("unable to open /dev/usb2 %d\n", fd); + return ; + } + + os_ioctlv(fd, USB_IOCTL_UMS_INIT, 0, 0, 0); + + heap = os_heap_create((void*)0x13898000, 0x8000); + vec = os_heap_alloc(heap, sizeof(ioctlv)); + p_sector_size = os_heap_alloc(heap, sizeof(u32)); + + vec[0].data = p_sector_size; + vec[0].len = 4; + num_sector = os_ioctlv(fd, USB_IOCTL_UMS_GET_CAPACITY, 0, 1, vec); + sector_size = *p_sector_size; + debug_printf("found device %d %d: %dkB\n", sector_size, num_sector, sector_size*(num_sector / 1024)); + os_heap_free(heap, vec); + os_heap_free(heap, p_sector_size); +} + +void *ums_alloc(int size) +{ + return os_heap_alloc(heap, size); +} + +void ums_free(void *ptr) +{ + os_heap_free(heap, ptr); +} + +s32 ums_read_sectors(u32 sector, u32 numSectors, void *buffer) +{ + ioctlv *vec; + u32 *p_sector; + u32 *p_nsector; + s32 ret; + vec = os_heap_alloc(heap, sizeof(ioctlv) * 3); + p_sector = os_heap_alloc(heap, sizeof(u32)); + p_nsector = os_heap_alloc(heap, sizeof(u32)); + *p_nsector = numSectors; + *p_sector = sector; + + vec[0].data = p_sector; + vec[0].len = 4; + vec[1].data = p_nsector; + vec[1].len = 4; + vec[2].data = buffer; + vec[2].len = sector_size * numSectors; + ret = os_ioctlv(fd, USB_IOCTL_UMS_READ_SECTORS, 2, 1, vec); + // no need to flush cache, this is done by ehc.. + os_heap_free(heap, vec); + os_heap_free(heap, p_sector); + os_heap_free(heap, p_nsector); + return ret; +} + +void ums_close(void) +{ + os_close(fd); + os_heap_destroy(heap); + fd = -1; + heap = -1; +} + +#endif diff --git a/mload/source/main.c.bak b/mload/source/main.c.bak new file mode 100644 index 00000000..004ba72e --- /dev/null +++ b/mload/source/main.c.bak @@ -0,0 +1,498 @@ +/* + dev/mload: Custom IOS module for Wii, to load ios elfs, initialize USB 2.0 and others uses + This module is derived from haxx.elf + Copyright (C) 2009 Hermes. + Copyright (C) 2008 neimod. + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +/******************************************************************************* + * + * main.c - IOS module main code + * + ******************************************************************************* + * + * + * v1.0 - 26 July 2008 - initial release by neimod + * v1.1 - 5 September 2008 - prepared for public release + * v1.2 - march 2008 - added some IOTCL, put it into its own module, by kwiirk + * + */ + + +#include +#include +#include "syscalls.h" + + +#define IOS_OPEN 0x01 +#define IOS_CLOSE 0x02 +#define IOS_READ 0x03 +#define IOS_WRITE 0x04 +#define IOS_SEEK 0x05 +#define IOS_IOCTL 0x06 +#define IOS_IOCTLV 0x07 + +#define MLOAD_MLOAD_THREAD_ID 0x4D4C4400 + +#define MLOAD_LOAD_MODULE 0x4D4C4480 +#define MLOAD_RUN_MODULE 0x4D4C4481 +#define MLOAD_RUN_THREAD 0x4D4C4482 + +#define MLOAD_STOP_THREAD 0x4D4C4484 +#define MLOAD_CONTINUE_THREAD 0x4D4C4485 + +#define MLOAD_GET_LOAD_BASE 0x4D4C4490 +#define MLOAD_MEMSET 0x4D4C4491 + +#define MLOAD_GET_EHCI_DATA 0x4D4C44A0 + +#define MLOAD_SET_ES_IOCTLV 0x4D4C44B0 + +#define MLOAD_GETW 0x4D4C44C0 +#define MLOAD_GETH 0x4D4C44C1 +#define MLOAD_GETB 0x4D4C44C2 +#define MLOAD_SETW 0x4D4C44C3 +#define MLOAD_SETH 0x4D4C44C4 +#define MLOAD_SETB 0x4D4C44C5 + +#define DEVICE "/dev/mload" + + +extern int ES_ioctlv_ret(void *); + +unsigned ES_ioctlv_vect=((unsigned) ES_ioctlv_ret); + +unsigned int heapspace[0x100/4] __attribute__ ((aligned (32))); + +// from IOS ELF stripper of neimod + +typedef struct +{ + u32 ident0; + u32 ident1; + u32 ident2; + u32 ident3; + u32 machinetype; + u32 version; + u32 entry; + u32 phoff; + u32 shoff; + u32 flags; + u16 ehsize; + u16 phentsize; + u16 phnum; + u16 shentsize; + u16 shnum; + u16 shtrndx; +} elfheader; + +typedef struct +{ + u32 type; + u32 offset; + u32 vaddr; + u32 paddr; + u32 filesz; + u32 memsz; + u32 flags; + u32 align; +} elfphentry; + +#define ioctlv_u8(a) (*((u8*)(a).data)) +#define ioctlv_u16(a) (*((u16*)(a).data)) +#define ioctlv_u32(a) (*((u32*)(a).data)) +#define ioctlv_voidp(a) (a).data + + + +extern u8 *mem_exe; // size 0x80000 (see crt0.s) + + +struct _data_elf +{ + void *start; + int prio; + void *stack; + int size_stack; +} +data_elf; + +#define getbe32(x) ((adr[x]<<24) | (adr[x+1]<<16) | (adr[x+2]<<8) | (adr[x+3])) + + +int load_elf(u32 elf) +{ +int n,m; +int p; +u8 *adr; + +elfheader *head=(void *) elf; +elfphentry *entries; + +if(head->ident0!=0x7F454C46) return -1; +if(head->ident1!=0x01020161) return -1; +if(head->ident2!=0x01000000) return -1; + +p=head->phoff; + +data_elf.start=(void *) head->entry; + +for(n=0; nphnum; n++) + { + entries=(void *) (elf+p); + p+=sizeof(elfphentry); + + if(entries->type == 4) + { + adr=(void *) (elf + entries->offset); + + if(getbe32(0)!=0) return -2; // bad info (sure) + + for(m=4; m < entries->memsz; m+=8) + { + switch(getbe32(m)) + { + case 0x9: + data_elf.start= (void *) getbe32(m+4); + break; + case 0x7D: + data_elf.prio= getbe32(m+4); + break; + case 0x7E: + data_elf.size_stack= getbe32(m+4); + break; + case 0x7F: + data_elf.stack= (void *) (getbe32(m+4)); + break; + + } + + } + + } + else + if(entries->type == 1 && entries->memsz != 0 && entries->vaddr!=0) + { + + os_sync_before_read((void *) entries->vaddr, entries->memsz ); + + memset((void *) entries->vaddr, 0, entries->memsz); + memcpy((void *) entries->vaddr, (void *) (elf + entries->offset), entries->filesz); + + os_sync_after_write((void *) entries->vaddr, entries->memsz ); + + } + } + +return 0; +} + +extern void *ehci; +int tiny_ehci_init(void); + +int main(void) +{ + ipcmessage* message; + unsigned int offset = 0; + + + + mem_exe[0]=0; // don't remove this !!!!! + + + tiny_ehci_init(); + + unsigned int heaphandle = os_heap_create(heapspace, sizeof(heapspace)); + void* queuespace = os_heap_alloc(heaphandle, 0x20); + + unsigned int queuehandle = os_message_queue_create(queuespace, 8); + + os_device_register(DEVICE, queuehandle); + + while(1) + { + int result = 1; + int ack = 1; + + // Wait for message to arrive + os_message_queue_receive(queuehandle, (void*)&message, 0); + + switch( message->command ) + { + case IOS_OPEN: + { + //debug_printf("%s try open %sfor fd %d\n",DEVICE,message->open.device,message->open.resultfd); + // Checking device name + if (0 == strcmp(message->open.device, DEVICE)) + { + result = message->open.resultfd; + } + + else + result = -6; + } + break; + + case IOS_CLOSE: + { + + // do nothing + result = 0; + } + break; + + case IOS_READ: + { + // Read from Starlet memory + + /* + if (message->read.length == 4) + { + *(volatile unsigned long*)(message->read.data) = *(volatile unsigned long*)offset; + } + else if (message->read.length == 2) + { + *(volatile unsigned short*)(message->read.data) = *(volatile unsigned short*)offset; + } + else if (message->read.length == 1) + { + *(volatile unsigned char*)(message->read.data) = *(volatile unsigned char*)offset; + } + else + { + memcpy(message->read.data, (void*)offset, message->read.length); + } + */ + // NOTE: no aligned is better + memcpy(message->read.data, (void*)offset, message->read.length); + // Clean cache + os_sync_after_write( message->read.data, message->read.length ); + offset += message->read.length; + } + break; + + case IOS_WRITE: + { + // Write to Starlet memory + // Invalidate cache + os_sync_before_read( message->write.data, message->write.length ); + + /* + if (message->write.length == 4) + { + *(volatile unsigned long*)offset = *(volatile unsigned long*)(message->write.data); + } + else if (message->write.length == 2) + { + *(volatile unsigned short*)offset = *(volatile unsigned short*)(message->write.data); + } + else if (message->write.length == 1) + { + *(volatile unsigned char*)offset = *(volatile unsigned char*)(message->write.data); + } + else + { + memcpy((void*)offset, message->write.data, message->write.length); + } + */ + memcpy((void*)offset, message->write.data, message->write.length); + offset += message->write.length; + } + break; + + case IOS_SEEK: + { + // Change current offset + switch(message->seek.origin) + { + case SEEK_SET: + { + offset = message->seek.offset; + break; + } + + case SEEK_CUR: + { + offset += message->seek.offset; + break; + } + + case SEEK_END: + { + offset = - message->seek.offset; + break; + } + } + result=offset; + } + break; + + + case IOS_IOCTL: + { + + break; + } + + case IOS_IOCTLV: + { + ioctlv *vec = message->ioctlv.vector; + + int i,in = message->ioctlv.num_in,io= message->ioctlv.num_io; + + os_sync_before_read( vec, (in+io)*sizeof(ioctlv)); + + for(i=0;iioctl.command ) + { + + case MLOAD_MLOAD_THREAD_ID: + + result=os_get_thread_id(); + + break; + + case MLOAD_GET_EHCI_DATA: + + result= (u32) ehci; + break; + + case MLOAD_GET_LOAD_BASE: + + result=0; + ioctlv_u32(vec[0])= 0x13700000; + ioctlv_u32(vec[1])= 0x80000; + break; + + case MLOAD_LOAD_MODULE: + + result = load_elf((u32) ioctlv_voidp(vec[0])); + break; + + case MLOAD_RUN_MODULE: + + result=os_thread_create( data_elf.start, NULL, data_elf.stack, data_elf.size_stack, data_elf.prio, 0); + if(result>=0) os_thread_continue(result); + + break; + + case MLOAD_RUN_THREAD: + + result=os_thread_create((void *) ioctlv_u32(vec[0]), NULL, (void *) ioctlv_u32(vec[1]), ioctlv_u32(vec[2]), ioctlv_u32(vec[3]), 0); + if(result>=0) os_thread_continue(result); + + break; + + case MLOAD_STOP_THREAD: + + result=os_thread_stop(ioctlv_u32(vec[0])); + + + break; + case MLOAD_CONTINUE_THREAD: + + result=os_thread_continue(ioctlv_u32(vec[0])); + + break; + + + case MLOAD_MEMSET: + result=0; + os_sync_before_read((void *) ioctlv_u32(vec[0]), ioctlv_u32(vec[2])); + memset((void *) ioctlv_u32(vec[0]), ioctlv_u32(vec[1]), ioctlv_u32(vec[2])); + + break; + + case MLOAD_SET_ES_IOCTLV: // changes the current vector for dev/es ioctl (put 0 to disable it) + result=0; + ES_ioctlv_vect=ioctlv_u32(vec[0]); + os_sync_after_write( &ES_ioctlv_vect, 4); + break; + + case MLOAD_GETW: + result=0; + ioctlv_u32(vec[1])=*((volatile u32*) ioctlv_u32(vec[0])); + break; + case MLOAD_GETH: + result=0; + ioctlv_u16(vec[1])=*((volatile u16*) ioctlv_u16(vec[0])); + break; + case MLOAD_GETB: + result=0; + ioctlv_u8(vec[1])=*((volatile u8*) ioctlv_u8(vec[0])); + break; + + case MLOAD_SETW: + result=0; + *((volatile u32*) ioctlv_u32(vec[0]))=ioctlv_u32(vec[1]); + break; + case MLOAD_SETH: + result=0; + *((volatile u16*) ioctlv_u16(vec[0]))=ioctlv_u16(vec[1]); + break; + case MLOAD_SETB: + result=0; + *((volatile u8*) ioctlv_u8(vec[0]))=ioctlv_u8(vec[1]); + break; + + + + /* + if (message->read.length == 4) + { + *(volatile unsigned long*)(message->read.data) = *(volatile unsigned long*)offset; + } + else if (message->read.length == 2) + { + *(volatile unsigned short*)(message->read.data) = *(volatile unsigned short*)offset; + } + else if (message->read.length == 1) + { + *(volatile unsigned char*)(message->read.data) = *(volatile unsigned char*)offset; + } + else + { + memcpy(message->read.data, (void*)offset, message->read.length); + } + */ + + } + for(i=in;idevkitPPC) +endif + +include $(DEVKITPPC)/wii_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := + + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +CFLAGS = -mrvl -Wall $(MACHDEP) $(INCLUDE) +CXXFLAGS = $(CFLAGS) + +LDFLAGS = $(MACHDEP) -mrvl -Wl,-Map,$(notdir $@).map + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +#LIBS := -lwiiuse -lbte -ldi -ldb -lfat -logc -lm +LIBS := -lwiiuse -lbte -lfat -ldi -logc -lm + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES), -iquote $(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) \ + -I$(LIBOGC_INC) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + -L$(LIBOGC_LIB) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol + +#--------------------------------------------------------------------------------- +run: + wl $(TARGET).dol + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).dol: $(OUTPUT).elf +$(OUTPUT).elf: $(OFILES) + +%.certs.o : %.certs + @echo $(notdir $<) + $(bin2o) + +%.elf.o : %.elf + @echo $(notdir $<) + $(bin2o) + +%.bin.o : %.bin + @echo $(notdir $<) + $(bin2o) +%.raw.o : %.raw + @echo $(notdir $<) + $(bin2o) + +#--------------------------------------------------------------------------------- +# This rule links in binary data with the .jpg extension +#--------------------------------------------------------------------------------- +%.jpg.o : %.jpg +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + $(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/test/c.bat b/test/c.bat new file mode 100644 index 00000000..2d5beccc --- /dev/null +++ b/test/c.bat @@ -0,0 +1,7 @@ +rem make clean +copy /y C:\devkitPro\msys\home\Julian\cios_mload\ehcmodule\bin\ehcmodule.elf .\data +del /q .\build\ehcmodule.elf.o +del /q usb.dol +del /q usb.elf +make +copy /y USB.dol H:\apps\usb2\boot.dol \ No newline at end of file diff --git a/test/data/ehcmodule.elf b/test/data/ehcmodule.elf new file mode 100644 index 00000000..24f58176 Binary files /dev/null and b/test/data/ehcmodule.elf differ diff --git a/test/dbg.bat b/test/dbg.bat new file mode 100644 index 00000000..6d996731 --- /dev/null +++ b/test/dbg.bat @@ -0,0 +1 @@ +C:\devkitPro\devkitPPC\bin\powerpc-gekko-addr2line -e test.elf %1 \ No newline at end of file diff --git a/test/l.bat b/test/l.bat new file mode 100644 index 00000000..ec11c019 --- /dev/null +++ b/test/l.bat @@ -0,0 +1 @@ +wiiload test.dol \ No newline at end of file diff --git a/test/m.bat b/test/m.bat new file mode 100644 index 00000000..5e6416f0 --- /dev/null +++ b/test/m.bat @@ -0,0 +1,7 @@ +del test.elf +del test.dol +del .\build\ehcmodule.elf.o +del .\data\ehcmodule.elf +copy /y ..\ehcmodule\bin\ehcmodule.elf .\data +make +wiiload test.dol diff --git a/test/m2.bat b/test/m2.bat new file mode 100644 index 00000000..be5106c0 --- /dev/null +++ b/test/m2.bat @@ -0,0 +1,7 @@ +make clean +del .\data\ehcmodule.elf +copy /y ..\ehcmodule\bin\ehcmodule.elf .\data +del test.dol +del usb2_test.dol +make +ren test.dol usb2_test.dol \ No newline at end of file diff --git a/test/source/di2.c b/test/source/di2.c new file mode 100644 index 00000000..6b1a8056 --- /dev/null +++ b/test/source/di2.c @@ -0,0 +1,1072 @@ +/*------------------------------------------------------------- + +di2.h -- Drive Interface library + +Written by rodries +Modified from (and supplemental to) original libdi library: + +Team Twiizers +Copyright (C) 2008 + +Erant +marcan + + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "di2.h" + +extern int di_fd; + +int _DI2_ReadDVD_ReadID(void* buf, uint32_t len, uint32_t lba); +int _DI2_ReadDVD_ReadID_Async(void* buf, uint32_t len, uint32_t lba, ipccallback ipc_cb); + +int _DI_ReadDVD_A8(void* buf, uint32_t len, uint32_t lba); +int _DI_ReadDVD_D0(void* buf, uint32_t len, uint32_t lba); + +int _DI_ReadDVD_A8_Async(void* buf, uint32_t len, uint32_t lba, ipccallback ipc_cb); +int _DI_ReadDVD_D0_Async(void* buf, uint32_t len, uint32_t lba, ipccallback ipc_cb); + +void _DI2_SetCallback(int di_command, ipccallback); +static int _cover_callback(int ret, void* usrdata); + +int state = DVD_INIT | DVD_NO_DISC; + +static mutex_t bufferMutex = LWP_MUTEX_NULL; +static uint32_t outbuf[8] __attribute__((aligned(32))); +static uint32_t dic[8] __attribute__((aligned(32))); +static char di_path[] ATTRIBUTE_ALIGN(32) = "/dev/di"; + +read_func DI2_ReadDVDptr = NULL; +read_func_async DI2_ReadDVDAsyncptr = NULL; +di_callback di_cb = NULL; + +#define DEFAULT_TIME_STOP_MOTOR 60 // 60 secs +#define MIN_TIME_STOP_MOTOR 8 // the min value accepted +static bool motorthreadexit = false; +static lwp_t motorthread = LWP_THREAD_NULL; +static bool motor_stopped = true; +static u64 LastAccess = 0; +static unsigned int TimeStopMotor = DEFAULT_TIME_STOP_MOTOR; + +int _DI2_ReadDVD_Check(void* buf, uint32_t len, uint32_t lba); + +void DI2_SetDVDMotorStopSecs(int secs) //in seconds +{ + + if (secs < MIN_TIME_STOP_MOTOR) + return ; + + LastAccess = ticks_to_secs(gettime()); + + TimeStopMotor = secs; +} + +unsigned int DI2_GetDVDMotorStopSecs() //in seconds +{ + return TimeStopMotor; +} + +static bool DVD_DiscPresent() +{ + uint32_t val; + + DI2_GetCoverRegister(&val); + + if (val & 0x2) + return true; + + return false; +} + +static void * motorthreadfunc(void *arg) +{ + long sleeptime; + bool first = true; + + while (1) + { + if (motor_stopped) + { + first = true; + } + + else + { + if (first) // change state from stop to start + { + sleeptime = 10 * 1000 * 1000; // 10 sec + + while (sleeptime > 0) + { + if (motorthreadexit) + return NULL; + + usleep(100); + + sleeptime -= 100; + } + + LastAccess = ticks_to_secs(gettime()); + first = false; + } + if ((ticks_to_secs(gettime()) - LastAccess) > TimeStopMotor) + { // we have to stop motor + + if (DVD_DiscPresent()) // only stop if dvd is present + DI2_StopMotor(); + } + } + + sleeptime = 1000 * 1000; // 1 sec + + while (sleeptime > 0) + { + if (motorthreadexit) + return NULL; + + usleep(100); + + sleeptime -= 100; + } + } + + return NULL; +} + +void DI2_StartMotorThread() +{ + if (motorthread == LWP_THREAD_NULL) + LWP_CreateThread(&motorthread, motorthreadfunc, NULL, NULL, 0, 63); // create thread +} + +void DI2_StopMotorThread() +{ + motorthreadexit = true; // signal thread to close + + if (motorthread != LWP_THREAD_NULL) + LWP_JoinThread(motorthread, NULL); // wait for thread to finish + + motorthread = LWP_THREAD_NULL; + + motorthreadexit = false; + + motor_stopped = true; +} + +int DI2_StartMotor() +{ + if (state == (DVD_INIT | DVD_NO_DISC) || (DI2_ReadDVDptr == _DI2_ReadDVD_Check)) + return 0; + + if (motor_stopped && motorthreadexit == false) + { + DI2_Mount(); + unsigned int t1, t2; + t1 = ticks_to_secs(gettime()); + + while (DI2_GetStatus() & DVD_INIT) + { + t2 = ticks_to_secs(gettime()); + + if (t2 - t1 > 15) return -1; + + usleep(5000); + } + + motor_stopped = false; + } + + return 0; +} + +void CheckAccess() +{ + DI2_StartMotor(); + LastAccess = ticks_to_secs(gettime()); +} + +///// Cache +#define CACHE_FREE 0xFFFFFFFF +#define BLOCK_SIZE 0x800 +#define CACHEBLOCKS 26 + +typedef struct +{ + uint32_t block; + void *ptr; +} + +cache_page; +static cache_page *cache_read = NULL; + +static void CreateDVDCache() +{ + if (cache_read != NULL) + return ; + + cache_read = (cache_page *) malloc(sizeof(cache_page)); + + if (cache_read == NULL) + return ; + + cache_read->block = CACHE_FREE; + + cache_read->ptr = memalign(32, BLOCK_SIZE * CACHEBLOCKS); + + if (cache_read->ptr == NULL) + { + if (cache_read->ptr) + free(cache_read->ptr); + + free(cache_read); + + cache_read = NULL; + + return ; + } + + memset(cache_read->ptr, 0, BLOCK_SIZE); +} + +static int ReadBlockFromCache(void *buf, uint32_t len, uint32_t block) +{ + int retval; + + if (cache_read == NULL) + return DI2_ReadDVDptr(buf, len, block); + + if ((block >= cache_read->block) && (block + len < (cache_read->block + CACHEBLOCKS))) + { + memcpy(buf, cache_read->ptr + ((block - cache_read->block) * BLOCK_SIZE), BLOCK_SIZE * len); + return 0; + } + + if (len > CACHEBLOCKS) + return DI2_ReadDVDptr(buf, len, block); + + retval = DI2_ReadDVDptr(cache_read->ptr, CACHEBLOCKS, block); + + if (retval) + { + cache_read->block = CACHE_FREE; + return retval; + } + + cache_read->block = block; + memcpy(buf, cache_read->ptr, len * BLOCK_SIZE); + + return 0; +} + +/* + Initialize the DI interface, should always be called first! + */ + +s32 __DI_LoadStub(); + +int DI2_Init(bool dvdx) +{ + static int dvdxinit = 0; + + state = DVD_INIT | DVD_NO_DISC; + + if (dvdx && !dvdxinit) + { + __DI_LoadStub(); // Marcan's 1337 magics happen here! + dvdxinit = 1; + } + + if (di_fd < 0) + di_fd = IOS_Open(di_path, 2); + + if (bufferMutex == LWP_MUTEX_NULL) + LWP_MutexInit(&bufferMutex, false); + + LastAccess = 0; + + CreateDVDCache(); + + return (di_fd >= 0) ? di_fd : -1; +} + +void DI2_Mount() +{ + uint32_t status; + + if (DI2_GetCoverRegister(&status) != 0) + { + state = DVD_NO_DISC; + return ; + } + + if ((status & DVD_COVER_DISC_INSERTED) == 0) + { + state = DVD_NO_DISC; + return ; + } + + state = DVD_INIT | DVD_NO_DISC; + _cover_callback(1, NULL); // Initialize the callback chain + LastAccess = ticks_to_secs(gettime()); + DI2_StopMotorThread(); + + if (cache_read != NULL) + cache_read->block = CACHE_FREE; // reset cache + + LastAccess = ticks_to_secs(gettime()); + + DI2_StartMotorThread(); +} + +void DI2_Close() +{ + DI2_StopMotorThread(); + LastAccess = 0; + + if (di_fd > 0) + { + IOS_Close(di_fd); + } + + di_fd = -1; + + DI2_ReadDVDptr = NULL; + DI2_ReadDVDAsyncptr = NULL; + state = DVD_INIT | DVD_NO_DISC; + + if (bufferMutex) + { + LWP_MutexDestroy(bufferMutex); + bufferMutex = 0; + } +} + +#define COVER_CLOSED (*((uint32_t*)usrdata) & DVD_COVER_DISC_INSERTED) + +int _DI2_ReadDVD_Check(void* buf, uint32_t len, uint32_t lba) +{ + int ret; + + ret = _DI_ReadDVD_D0(buf, len, lba); + + if (ret >= 0) + { + state = state | DVD_D0; + DI2_ReadDVDptr = _DI_ReadDVD_D0; + DI2_ReadDVDAsyncptr = _DI_ReadDVD_D0_Async; + motor_stopped = false; + //printf("libdi: D0 functions detected\n"); + return ret; + } + + ret = _DI_ReadDVD_A8(buf, len, lba); + + if (ret >= 0) + { + state = state | DVD_A8; + DI2_ReadDVDptr = _DI_ReadDVD_A8; + DI2_ReadDVDAsyncptr = _DI_ReadDVD_A8_Async; + motor_stopped = false; + //printf("libdi: A8 functions detected\n"); + return ret; + } + + //printf("libdi: error detection D0/A8 functions\n"); + return ret; +} + +int _DI2_ReadDVD_Check_Async(void* buf, uint32_t len, uint32_t lba, ipccallback ipc_cb) +{ // is bad code has to be done correctly using callback func + int ret; + + ret = _DI_ReadDVD_D0_Async(buf, len, lba, ipc_cb); + + if (ret >= 0) + { + state = state | DVD_D0; + DI2_ReadDVDptr = _DI_ReadDVD_D0; + DI2_ReadDVDAsyncptr = _DI_ReadDVD_D0_Async; + motor_stopped = false; + return ret; + } + + ret = _DI_ReadDVD_A8_Async(buf, len, lba, ipc_cb); + + if (ret >= 0) + { + state = state | DVD_A8; + DI2_ReadDVDptr = _DI_ReadDVD_A8; + DI2_ReadDVDAsyncptr = _DI_ReadDVD_A8_Async; + motor_stopped = false; + return ret; + } + + return ret; +} + +static int _cover_callback(int ret, void* usrdata) +{ + static int cur_state = 0; + static int retry_count = LIBDI_MAX_RETRIES; + const int callback_table[] = + { + DVD_GETCOVER, DVD_WAITFORCOVERCLOSE, DVD_RESET, DVD_IDENTIFY, // This one will complete when the drive is ready. + DVD_READ_DISCID, 0 + }; + + const int return_table[] = + { 1, 1, 4, 1, 1 }; + + if (cur_state > 1) + state &= ~DVD_NO_DISC; + + if (callback_table[cur_state]) + { + if (ret == return_table[cur_state]) + { + if (cur_state == 1 && COVER_CLOSED) // Disc inside, skipping wait for cover. + cur_state += 2; + else + cur_state++; // If the previous callback succeeded, moving on to the next + + retry_count = LIBDI_MAX_RETRIES; + } + + else + { + retry_count--; + + if (retry_count < 0) + { // Drive init failed for unknown reasons. + retry_count = LIBDI_MAX_RETRIES; + cur_state = 0; + state = DVD_UNKNOWN; + return 0; + } + } + + _DI2_SetCallback(callback_table[cur_state - 1], _cover_callback); + + } + + else // Callback chain has completed OK. The drive is ready. + { + state = DVD_READY; + + retry_count = 1; + state = DVD_READY; + + DI2_ReadDVDptr = _DI2_ReadDVD_Check; + DI2_ReadDVDAsyncptr = _DI2_ReadDVD_Check_Async; + + if (di_cb) + di_cb(state, 0); + + retry_count = LIBDI_MAX_RETRIES; + + cur_state = 0; + + } + + return 0; +} + +/* Get current status, will return the API status */ +int DI2_GetStatus() +{ + return state; +} + +void DI2_SetInitCallback(di_callback cb) +{ + di_cb = cb; +} + +void _DI2_SetCallback(int ioctl_nr, ipccallback ipc_cb) +{ + if (!ipc_cb) + return ; + + LWP_MutexLock(bufferMutex); + + memset(dic, 0x00, sizeof(dic)); + + dic[0] = ioctl_nr << 24; + + dic[1] = (ioctl_nr == DVD_RESET) ? 1 : 0; // For reset callback. Dirty, I know... + + IOS_IoctlAsync(di_fd, ioctl_nr, dic, 0x20, outbuf, 0x20, ipc_cb, outbuf); + + //We're done with the buffer now. + LWP_MutexUnlock(bufferMutex); +} + +/* + Request an identification from the drive, returned in a DI_DriveID struct + */ +int DI2_Identify(DI_DriveID* id) +{ + if (!id) + { + errno = EINVAL; + return -1; + } + + CheckAccess(); + LWP_MutexLock(bufferMutex); + dic[0] = DVD_IDENTIFY << 24; + + int ret = IOS_Ioctl(di_fd, DVD_IDENTIFY, dic, 0x20, outbuf, 0x20); + + if (ret == 2) + errno = EIO; + + memcpy(id, outbuf, sizeof(DI_DriveID)); + + LWP_MutexUnlock(bufferMutex); + + return (ret == 1) ? 0 : -ret; +} + +/* + Returns the current error code on the drive. + yagcd has a pretty comprehensive list of possible error codes + */ +int DI2_GetError(uint32_t* error) +{ + if (!error) + { + errno = EINVAL; + return -1; + } + + CheckAccess(); + LWP_MutexLock(bufferMutex); + + dic[0] = DVD_GET_ERROR << 24; + + int ret = IOS_Ioctl(di_fd, DVD_GET_ERROR, dic, 0x20, outbuf, 0x20); + + if (ret == 2) + errno = EIO; + + *error = outbuf[0]; // Error code is returned as an int in the first four bytes of outbuf. + + LWP_MutexUnlock(bufferMutex); + + return (ret == 1) ? 0 : -ret; +} + +/* + Reset the drive. + */ +int DI2_Reset() +{ + LWP_MutexLock(bufferMutex); + + dic[0] = DVD_RESET << 24; + dic[1] = 1; + + int ret = IOS_Ioctl(di_fd, DVD_RESET, dic, 0x20, outbuf, 0x20); + + if (ret == 2) + errno = EIO; + + LWP_MutexUnlock(bufferMutex); + + return (ret == 1) ? 0 : -ret; +} + +/* + Main read function, basically just a wrapper to the function pointer. + Nicer then just exposing the pointer itself + */ +int DI2_ReadDVD(void* buf, uint32_t len, uint32_t lba) +{ + int ret; + + if (DI2_ReadDVDptr) + { + CheckAccess(); + + // Wait for the lock. Doing it here saves me from doing it in all the read functions. + LWP_MutexLock(bufferMutex); + ret = ReadBlockFromCache(buf, len, lba); + LWP_MutexUnlock(bufferMutex); + return ret; + } + + return -1; +} + +int DI2_ReadDVDAsync(void* buf, uint32_t len, uint32_t lba, ipccallback ipc_cb) +{ + int ret; + + if (DI2_ReadDVDAsyncptr) + { + CheckAccess(); + LWP_MutexLock(bufferMutex); + ret = DI2_ReadDVDAsyncptr(buf, len, lba, ipc_cb); + LWP_MutexUnlock(bufferMutex); + return ret; + } + + return -1; +} + +/* + Unknown what this does as of now... + */ +int DI2_ReadDVDConfig(uint32_t* val, uint32_t flag) +{ + if (!val) + { + errno = EINVAL; + return -1; + } + + CheckAccess(); + LWP_MutexLock(bufferMutex); + + dic[0] = DVD_READ_CONFIG << 24; + dic[1] = flag & 0x1; // Update flag, val will be written if this is 1, val won't be written if it's 0. + dic[2] = 0; // Command will fail driveside if this is not zero. + dic[3] = *val; + + int ret = IOS_Ioctl(di_fd, DVD_READ_CONFIG, dic, 0x20, outbuf, 0x20); + + if (ret == 2) + errno = EIO; + + *val = outbuf[0]; + + LWP_MutexUnlock(bufferMutex); + + return (ret == 1) ? 0 : -ret; +} + +/* + Read the copyright information on a DVDVideo + */ +int DI2_ReadDVDCopyright(uint32_t* copyright) +{ + if (!copyright) + { + errno = EINVAL; + return -1; + } + + CheckAccess(); + LWP_MutexLock(bufferMutex); + + dic[0] = DVD_READ_COPYRIGHT << 24; + dic[1] = 0; + + int ret = IOS_Ioctl(di_fd, DVD_READ_COPYRIGHT, dic, 0x20, outbuf, 0x20); + *copyright = *((uint32_t*) outbuf); // Copyright information is returned as an int in the first four bytes of outbuf. + + if (ret == 2) + errno = EIO; + + LWP_MutexUnlock(bufferMutex); + + return (ret == 1) ? 0 : -ret; +} + +/* + Returns 0x800 bytes worth of Disc key + */ +int DI2_ReadDVDDiscKey(void* buf) +{ + int ret; + int retry_count = LIBDI_MAX_RETRIES; + + if (!buf) + { + errno = EINVAL; + return -1; + } + + if ((uint32_t) buf & 0x1F) + { + errno = EFAULT; + return -1; + } + + CheckAccess(); + LWP_MutexLock(bufferMutex); + + dic[0] = DVD_READ_DISCKEY << 24; + dic[1] = 0; // Unknown what this flag does. + + do + { + ret = IOS_Ioctl(di_fd, DVD_READ_DISCKEY, dic, 0x20, buf, 0x800); + retry_count--; + } + + while (ret != 1 && retry_count > 0); + + if (ret == 2) + errno = EIO; + + LWP_MutexUnlock(bufferMutex); + + return (ret == 1) ? 0 : -ret; +} + +/* + This function will read the initial sector on the DVD, which contains stuff like the booktype + */ +int DI2_ReadDVDPhysical(void* buf) +{ + int ret; + int retry_count = LIBDI_MAX_RETRIES; + + if (!buf) + { + errno = EINVAL; + return -1; + } + + if ((uint32_t) buf & 0x1F) + { + errno = EFAULT; + return -1; + } + + CheckAccess(); + LWP_MutexLock(bufferMutex); + + dic[0] = DVD_READ_PHYSICAL << 24; + dic[1] = 0; // Unknown what this flag does. + + do + { + ret = IOS_Ioctl(di_fd, DVD_READ_PHYSICAL, dic, 0x20, buf, 0x800); + retry_count--; + } + + while (ret != 1 && retry_count > 0); + + if (ret == 2) + errno = EIO; + + LWP_MutexUnlock(bufferMutex); + + return (ret == 1) ? 0 : -ret; +} + +int DI2_ReportKey(int keytype, uint32_t lba, void* buf) +{ + if (!buf) + { + errno = EINVAL; + return -1; + } + + if ((uint32_t) buf & 0x1F) + { + errno = EFAULT; + return -1; + } + + CheckAccess(); + LWP_MutexLock(bufferMutex); + + dic[0] = DVD_REPORTKEY << 24; + dic[1] = keytype & 0xFF; + dic[2] = lba; + + int ret = IOS_Ioctl(di_fd, DVD_REPORTKEY, dic, 0x20, buf, 0x20); + + if (ret == 2) + errno = EIO; + + LWP_MutexUnlock(bufferMutex); + + return (ret == 1) ? 0 : -ret; +} + +int DI2_GetCoverRegister(uint32_t* status) +{ + LWP_MutexLock(bufferMutex); + + memset(dic, 0x00, 0x20); + + int ret = IOS_Ioctl(di_fd, DVD_GETCOVER, dic, 0x20, outbuf, 0x20); + + if (ret == 2) + errno = EIO; + + *status = outbuf[0]; + + LWP_MutexUnlock(bufferMutex); + + return (ret == 1) ? 0 : -ret; +} + +/* Internal function for controlling motor operations */ +int _DI2_SetMotor(int flag) +{ + LWP_MutexLock(bufferMutex); + LastAccess = ticks_to_secs(gettime()); + + dic[0] = DVD_SET_MOTOR << 24; + dic[1] = flag & 0x1; // Eject flag. + dic[2] = (flag >> 1) & 0x1; // Don't use this flag, it kills the drive untill next reset. + + int ret = IOS_Ioctl(di_fd, DVD_SET_MOTOR, dic, 0x20, outbuf, 0x20); + + if (ret == 2) + errno = EIO; + + if (ret == 1) + motor_stopped = true; + + LWP_MutexUnlock(bufferMutex); + + return (ret == 1) ? 0 : -ret; +} + +/* Stop the drives motor, needs a reset afterwards for normal operation */ +int DI2_StopMotor() +{ + return _DI2_SetMotor(0); +} + +/* Stop the motor, and eject the disc. Also needs a reset afterwards for normal operation */ +int DI2_Eject() +{ + return _DI2_SetMotor(1); +} + +/* Warning, this will kill your drive untill the next reset. Will not respond to DI commands, + will not take in or eject the disc. Your drive will be d - e - d, dead. + + I deem this function to be harmless, as normal operation will resume after a reset. + However, I am not liable for anyones drive exploding as a result from using this function. + */ +int DI2_KillDrive() +{ + return _DI2_SetMotor(2); +} + +int DI2_ClosePartition() +{ + CheckAccess(); + LWP_MutexLock(bufferMutex); + + dic[0] = DVD_CLOSE_PARTITION << 24; + + int ret = IOS_Ioctl(di_fd, DVD_CLOSE_PARTITION, dic, 0x20, outbuf, 0x20); + + if (ret == 2) + errno = EIO; + + LWP_MutexUnlock(bufferMutex); + + return (ret == 1) ? 0 : -ret; +} + +int DI2_OpenPartition(uint32_t offset) +{ + static ioctlv vectors[5] __attribute__((aligned(32))); + static char certs[0x49e4] __attribute__((aligned(32))); + CheckAccess(); + LWP_MutexLock(bufferMutex); + + dic[0] = DVD_OPEN_PARTITION << 24; + dic[1] = offset; + + vectors[0].data = dic; + vectors[0].len = 0x20; + vectors[1].data = NULL; + vectors[1].len = 0x2a4; + vectors[2].data = NULL; + vectors[2].len = 0; + + vectors[3].data = certs; + vectors[3].len = 0x49e4; + vectors[4].data = outbuf; + vectors[4].len = 0x20; + + int ret = IOS_Ioctlv(di_fd, DVD_OPEN_PARTITION, 3, 2, vectors); + + if (ret == 2) + errno = EIO; + + LWP_MutexUnlock(bufferMutex); + + return (ret == 1) ? 0 : -ret; +} + +int DI2_Read(void *buf, uint32_t size, uint32_t offset) +{ + if (!buf) + { + errno = EINVAL; + return -1; + } + + if ((uint32_t) buf & 0x1F) + { + errno = EFAULT; + return -1; + } + + CheckAccess(); + LWP_MutexLock(bufferMutex); + + dic[0] = DVD_LOW_READ << 24; + dic[1] = size; + dic[2] = offset; + + int ret = IOS_Ioctl(di_fd, DVD_LOW_READ, dic, 0x20, buf, size); + + if (ret == 2) + errno = EIO; + + LWP_MutexUnlock(bufferMutex); + + return (ret == 1) ? 0 : -ret; +} + +int DI2_UnencryptedRead(void *buf, uint32_t size, uint32_t offset) +{ + int ret, retry_count = LIBDI_MAX_RETRIES; + + if (!buf) + { + errno = EINVAL; + return -1; + } + + if ((uint32_t) buf & 0x1F) + { // This only works with 32 byte aligned addresses! + errno = EFAULT; + return -1; + } + + CheckAccess(); + LWP_MutexLock(bufferMutex); + + dic[0] = DVD_READ_UNENCRYPTED << 24; + dic[1] = size; + dic[2] = offset; + + do + { + ret = IOS_Ioctl(di_fd, DVD_READ_UNENCRYPTED, dic, 0x20, buf, size); + retry_count--; + } + + while (ret != 1 && retry_count > 0); + + if (ret == 2) + errno = EIO; + + LWP_MutexUnlock(bufferMutex); + + return (ret == 1) ? 0 : -ret; +} + +int DI2_ReadDiscID(uint64_t *id) +{ + CheckAccess(); + LWP_MutexLock(bufferMutex); + + dic[0] = DVD_READ_DISCID << 24; + + int ret = IOS_Ioctl(di_fd, DVD_READ_DISCID, dic, 0x20, outbuf, 0x20); + + if (ret == 2) + errno = EIO; + + memcpy(id, outbuf, sizeof(*id)); + + LWP_MutexUnlock(bufferMutex); + + return (ret == 1) ? 0 : -ret; +} + +bool diio_Startup() +{ + return true; +} + +bool diio_Shutdown() +{ + return true; +} + +bool diio_ReadSectors(sec_t sector, sec_t numSectors, void *buffer) +{ + return true; +} + +bool diio_WriteSectors(sec_t sector, sec_t numSectors, const void *buffer) +{ + return true; +} + +bool diio_ClearStatus() +{ + return true; +} + +bool diio_IsInserted() +{ + return true; +} + +bool diio_IsInitialized() +{ + return true; +} + +const DISC_INTERFACE __io_wiidvd2 = + { + DEVICE_TYPE_WII_DVD, + FEATURE_MEDIUM_CANREAD | FEATURE_WII_DVD, + (FN_MEDIUM_STARTUP) & diio_Startup, + (FN_MEDIUM_ISINSERTED) & diio_IsInserted, + (FN_MEDIUM_READSECTORS) & diio_ReadSectors, + (FN_MEDIUM_WRITESECTORS) & diio_WriteSectors, + (FN_MEDIUM_CLEARSTATUS) & diio_ClearStatus, + (FN_MEDIUM_SHUTDOWN) & diio_Shutdown + }; diff --git a/test/source/di2.h b/test/source/di2.h new file mode 100644 index 00000000..2bd47705 --- /dev/null +++ b/test/source/di2.h @@ -0,0 +1,98 @@ +/*------------------------------------------------------------- + +di2.h -- Drive Interface library + +Written by rodries +Modified from (and supplemental to) original libdi library: + +Team Twiizers +Copyright (C) 2008 + +Erant +marcan + + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + + +/* +All buffers in this document need to be 32-byte aligned! +*/ + +#ifndef __DI2_H__ +#define __DI2_H__ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* + FUNCTION PROTOTYPES GO HERE! + */ + + int DI2_Init(bool dvdx); + void DI2_Mount(); + void DI2_Close(); + int DI2_GetStatus(); + + int DI2_Identify(DI_DriveID* id); + int DI2_ReadDiscID(u64 *id); + int DI2_GetError(uint32_t* error); + int DI2_GetCoverRegister(uint32_t* status); + int DI2_Reset(); + + int DI2_StopMotor(); + int DI2_Eject(); + int DI2_KillDrive(); + + int DI2_ReadDVD(void* buf, uint32_t len, uint32_t lba); + int DI2_ReadDVDAsync(void* buf, uint32_t len, uint32_t lba, ipccallback ipc_cb); + + int DI2_Read(void *buf, u32 size, u32 offset); + int DI2_UnencryptedRead(void *buf, u32 size, u32 offset); + + int DI2_ReadDVDConfig(uint32_t* val, uint32_t flag); + int DI2_ReadDVDCopyright(uint32_t* copyright); + int DI2_ReadDVDDiscKey(void* buf); + int DI2_ReadDVDPhysical(void* buf); + int DI2_ReportKey(int keytype, uint32_t lba, void* buf); + + int DI2_OpenPartition(u32 offset); + int DI2_ClosePartition(void); + + void DI2_SetDVDMotorStopSecs(int secs); + unsigned int DI2_GetDVDMotorStopSecs(void); + +#ifdef __cplusplus + +} + +#endif + +#endif diff --git a/test/source/libdvdiso.c b/test/source/libdvdiso.c new file mode 100644 index 00000000..ec4af484 --- /dev/null +++ b/test/source/libdvdiso.c @@ -0,0 +1,1526 @@ +#include +#include +#include +#include +#include +#include +#ifndef DEBUG +#include +#include +#include +#include +#include +#endif +#include "libdvdiso.h" +#include "di2.h" + +#define MAXPATH 4096 +#define MAXNAMELEN 208 + + +#define uint8_t unsigned char +#define uint16_t unsigned short +#define uint32_t unsigned int +static mutex_t _DVD_mutex = LWP_MUTEX_NULL; +static bool dvd_initied = false; + +static int totalsectors; +static int totalentries; + +static char currentpath[MAXPATH]; + +static void freedirentrieslist(void); +static void dotab_dvd_add(void); +static int DVD_ScanContent(void); + +struct DIRENTRY +{ + char name[MAXNAMELEN + 1]; + uint32_t size_bytes; + uint32_t size_sectors; + uint32_t firstsector; + uint8_t isfile; + uint8_t isdir; + uint8_t ishidden; + + struct DIRENTRY *next; + + struct DIRENTRY *parent; + + struct DIRENTRY *child; + uint32_t fake_inode; +}; + +static struct DIRENTRY *direntriesptr; + +struct FILESTATE +{ + + struct DIRENTRY *dentry; + uint32_t firstsector; + uint32_t pos_bytes; + uint32_t size_bytes; + uint32_t localsectnum; + uint8_t *localsectbuf; + int fd; +}; + +struct DIR_STATE +{ + + struct DIRENTRY *firstdentry; + + struct DIRENTRY *curdentry; + uint8_t inUse; +}; + +#ifdef DEBUG +FILE *fpin; + +struct _reent +{ + int _errno; +}; + +uint32_t fake_ticks = 1000; +uint32_t gettick(){ return fake_ticks++;}; + +#endif + + +/////////////////////////////////////////// +// CACHE FUNCTION DEFINITIONS // +/////////////////////////////////////////// +#define CACHE_FREE 0xFFFFFFFF + +typedef struct +{ + uint32_t sector; + uint32_t last_used; + void *ptr; +} + +cache_page; + +static cache_page *ReadAheadCache = NULL; +static uint32_t RA_pages = 0; +static uint32_t RA_sectors = 0; +#define SECTOR_SIZE 0x800 + +static void DestroyReadAheadCache(); +static int ReadSectorFromCache(void *buf, uint32_t sector); +static void DVDEnableReadAhead(uint32_t pages, uint32_t sectors); + +/////////////////////////////////////////// +// END CACHE FUNCTION DEFINITIONS // +/////////////////////////////////////////// + +/* +void dump_hex(uint8_t *ptr, int len) +{ + int i,pos=0; + do + { + for(i=0;(i<8)&&(pos= 0) dvd_initied = true; + else dvd_initied = false; + +#else + fpin = fopen("/dev/sr0", "rb"); + + if (!fpin) + { + printf("Error while opening device or file\n"); + return -1; + } + +#endif + +#ifndef DEBUG + dotab_dvd_add(); + +#endif + LWP_MutexInit(&_DVD_mutex, false); + + return retval; +} + +void WIIDVD_Close() +{ + freedirentrieslist(); + DestroyReadAheadCache(); +#ifndef DEBUG + DI2_Close(); +#endif + LWP_MutexDestroy(_DVD_mutex); +} + +int WIIDVD_Mount() +{ +#ifndef DEBUG + DI2_Mount(); + unsigned int t1, t2; + t1 = ticks_to_secs(gettime()); + + while (DI2_GetStatus() & DVD_INIT) + { + t2 = ticks_to_secs(gettime()); + + if (t2 - t1 > 15) return -1; + + usleep(5000); + } + +#endif + DVDEnableReadAhead(10, 26); //default init + + return DVD_ScanContent(); +} + +void WIIDVD_Unmount() +{ + freedirentrieslist(); + DestroyReadAheadCache(); +} + + +int WIIDVD_ReadDVD(void* buf, uint32_t len, uint32_t lba) +{ + int retval; +#ifdef DEBUG + // printf("WIIDVD_ReadDVD: Reading from sector %u len %u\n",lba,len); + + retval = fseek(fpin, lba * 2048, SEEK_SET); + + if (retval)printf(" fseek returned %d\n", retval); + + retval = fread(buf, len, 2048, fpin); + + if (retval != 2048)printf(" fread returned %d\n", retval); + + return 0; + +#else + retval = DI2_ReadDVD(buf, len, lba); + + //if(retval)printf("Error %d reading sectors %d->%d\n",retval,lba,lba+len-1); + return retval; + +#endif +} + +int WIIDVD_DiscPresent() +{ +#ifdef DEBUG + return 1; +#else + uint32_t val; + + if (!dvd_initied) return 0; + + DI2_GetCoverRegister(&val); + + if (val&0x2) return 1; + + return 0; + +#endif +} + +static struct DIRENTRY * name_to_dentry(char *filename, uint8_t file_permitted, uint8_t dir_permitted) //filename must be absolute, without dvd: at beginning +{ + //int done=0; + + struct DIRENTRY * dentryptr; + char *charptr, *nextcharptr; + char nametofind[MAXPATH]; + uint8_t found; + + if (filename[0] == '/')filename++; //Skip initial / + + strcpy(nametofind, filename); + + //printf("name_to_dentry: '%s'\n",nametofind); + + if (!direntriesptr) return NULL; + + dentryptr = direntriesptr->child; + + if (!dentryptr) return NULL; + + charptr = strtok(nametofind, "/"); + + //printf("charptr: '%s'\n",charptr); + if (charptr == NULL) + { + return direntriesptr; + } + + do + { + if (charptr == NULL) return NULL; + + nextcharptr = strtok(NULL, "/"); + + // printf("searching for: '%s'\n",charptr); + + found = 0; + + do + { + // printf("Comparing '%s' with '%s'\n",dentryptr->name,charptr); + + if (strcmp(dentryptr->name, charptr) == 0) + { + found = 1; + + if (nextcharptr) //non-last path item + { + + if (!dentryptr->isdir) return NULL; //we are searching for a file inside this non-directory item + + // printf("Is a directory because the following item is '%s'\n",nextcharptr); + dentryptr = dentryptr->child; + + if (!dentryptr) return NULL; + + // printf("Found child '%s'\n",dentryptr->name); + } + else //last path item + { + if ((dentryptr->isfile) && (!file_permitted)) return NULL; //we don't want files + + if ((dentryptr->isdir) && (!dir_permitted)) return NULL; //we don't want directory + + // if(dentryptr->isfile)printf("Found file '%s'\n",dentryptr->name); + // if(dentryptr->isdir)printf("Found directory '%s'\n",dentryptr->name); + return dentryptr; + } + + } + + else + { + dentryptr = dentryptr->next; + } + } + + while ((!found) && (dentryptr)); + + if (!found) return NULL; + + charptr = nextcharptr; + } + + while (dentryptr); + + return NULL; +} + +static char *absolute_path_without_device(const char *srcpath, char *destpath) +{ + //Thanks to Chishm (libfat) + // Move the path pointer to the start of the actual path + + if (strchr (srcpath, ':') != NULL) + { + srcpath = strchr (srcpath, ':') + 1; + } + + if (strchr (srcpath, ':') != NULL) + { + return NULL; + } + + if (srcpath[0] != '/') //relative path + { + strcpy(destpath, currentpath); + strcat(destpath, srcpath); + } + else + { + strcpy(destpath, srcpath); + } + + return destpath; +} + +#ifndef DEBUG + +static int dentrystat(struct DIRENTRY *dentry, struct stat *st) +{ + if (!st) return -1; + + if (!dentry) return -1; + + st->st_dev = 0; //?!??!? + + st->st_ino = (ino_t)dentry->fake_inode; + + // st->st_mode=(dentry->isdir ? S_IFDIR : S_IFREG) | (S_IRUSR | S_IRGRP | S_IROTH); + st->st_mode = (dentry->isdir ? S_IFDIR : S_IFREG); + + st->st_nlink = 1; // Always one hard link on a ISO entry + + st->st_uid = 1; // Faked + + st->st_rdev = st->st_dev; + + st->st_gid = 2; // Faked + + st->st_size = dentry->size_bytes; + + st->st_atime = 0; //FIXME! + + // st->st_spare1 = 0; + st->st_mtime = 0; //FIXME; + + // st->st_spare2 = 0; + st->st_ctime = 0; //FIXME + + // st->st_spare3 = 0; + st->st_blksize = 0x800; + + st->st_blocks = dentry->size_sectors; + + st->st_spare4[0] = 0; + + st->st_spare4[1] = 0; + + return 0; +} + +#endif + +#ifndef DEBUG + +static int _DVD_dirclose_r (struct _reent *r, DIR_ITER *dirState) +{ + + struct DIR_STATE * mydirstate = (struct DIR_STATE *)dirState->dirStruct; + + // printf("DVD_dirclose\n"); + + if (!(mydirstate->inUse)) + { + r->_errno = EBADF; + return -1; + } + + memset(mydirstate, 0, sizeof(struct DIR_STATE)); + mydirstate->inUse = 0; + return 0; +} + +static int _DVD_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) +{ + + struct DIR_STATE * mydirstate = (struct DIR_STATE *)dirState->dirStruct; + + // printf("DVD_dirnext\n"); + + if (!(mydirstate->inUse)) + { + r->_errno = EBADF; + return -1; + } + + if (mydirstate->curdentry == NULL) + { + r->_errno = ENOENT; + return -1; + } + + // skip .. at root + if (mydirstate->curdentry->fake_inode == 4 && strcmp(mydirstate->curdentry->name, "..") == 0) + { + mydirstate->curdentry = mydirstate->curdentry->next; + + if (mydirstate->curdentry == NULL) + { + r->_errno = ENOENT; + return -1; + } + } + + strncpy(filename, mydirstate->curdentry->name, MAXNAMELEN); + + if (filestat != NULL) + { + dentrystat(mydirstate->curdentry, filestat); + } + + mydirstate->curdentry = mydirstate->curdentry->next; + + return 0; +} + +static int _DVD_dirreset_r (struct _reent *r, DIR_ITER *dirState) +{ + + struct DIR_STATE * mydirstate = (struct DIR_STATE *)dirState->dirStruct; + + // printf("DVD_dirreset\n"); + + if (!(mydirstate->inUse)) + { + r->_errno = EBADF; + return -1; + } + + mydirstate->curdentry = mydirstate->firstdentry; + return 0; +} + +static DIR_ITER* _DVD_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path) +{ + char path_absolute[MAXPATH]; + + struct DIR_STATE * mydirstate = (struct DIR_STATE *)dirState->dirStruct; + + struct DIRENTRY * dentry; + + // printf("DVD_diropen\n"); + + if (absolute_path_without_device(path, path_absolute) == NULL) + { + r->_errno = EINVAL; + return NULL; + } + + dentry = name_to_dentry(path_absolute, 1, 1); //filename must be absolute, without dvd: at beginning + + if (!dentry) + { + r->_errno = ENOENT; + return NULL; + } + + if (!(dentry->isdir)) + { + r->_errno = ENOTDIR; + return NULL; + } + + mydirstate->firstdentry = dentry->child; + mydirstate->curdentry = dentry->child; + mydirstate->inUse = 1; + + // printf("firstdentry: '%s'\n",dentry->name); + return dirState; +} + +static int _DVD_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf) +{ + // printf("DVD_statvfs\n"); + + // FAT clusters = POSIX blocks + buf->f_bsize = 0x800; // File system block size. + buf->f_frsize = 0x800; // Fundamental file system block size. + + buf->f_blocks = totalsectors; // Total number of blocks on file system in units of f_frsize. + buf->f_bfree = 0; // Total number of free blocks. + buf->f_bavail = 0; // Number of free blocks available to non-privileged process. + + // Treat requests for info on inodes as clusters + buf->f_files = totalentries; // Total number of file serial numbers. + buf->f_ffree = 0; // Total number of free file serial numbers. + buf->f_favail = 0; // Number of file serial numbers available to non-privileged process. + + // File system ID. 32bit ioType value + buf->f_fsid = 0; //??!!? + + // Bit mask of f_flag values. + buf->f_flag = ST_NOSUID // No support for ST_ISUID and ST_ISGID file mode bits + | ST_RDONLY ; // Read only file system + // Maximum filename length. + buf->f_namemax = MAXNAMELEN; + return 0; +} + +#endif + +static int _DVD_chdir_r (struct _reent *r, const char *path) +{ + char path_absolute[MAXPATH]; + + struct DIRENTRY * dentry; + + // printf("DVD_chdir\n"); + + if (absolute_path_without_device(path, path_absolute) == NULL) + { + r->_errno = EINVAL; + return -1; + } + + // printf("path_absolute: %s\n",path_absolute); + dentry = name_to_dentry(path_absolute, 1, 1); //filename must be absolute, without dvd: at beginning + + if (!dentry) + { + r->_errno = ENOENT; + return -1; + } + + if (!(dentry->isdir)) + { + r->_errno = ENOTDIR; + return -1; + } + + strcpy(currentpath, path_absolute); + + if (currentpath[0] != 0) + { + if (currentpath[strlen(currentpath) - 1] != '/')strcat(currentpath, "/"); + } + + return 0; +} + +#ifndef DEBUG + +static int _DVD_stat_r (struct _reent *r, const char *path, struct stat *st) +{ + char path_absolute[MAXPATH]; + + struct DIRENTRY * dentry; + + // printf("DVD_stat\n"); + + if (absolute_path_without_device(path, path_absolute) == NULL) + { + r->_errno = EINVAL; + return -1; + } + + dentry = name_to_dentry(path_absolute, 1, 1); //filename must be absolute, without dvd: at beginning + + if (!dentry) + { + r->_errno = ENOENT; + return -1; + } + + dentrystat(dentry, st); + return 0; +} + +static int _DVD_fstat_r (struct _reent *r, int fd, struct stat *st) +{ + + struct FILESTATE *filestate = (struct FILESTATE *)fd; + + // printf("DVD_fstat\n"); + + + if (!filestate) + { + r->_errno = EBADF; + return -1; + } + + if (!(filestate->dentry->isfile)) + { + r->_errno = EISDIR; + return -1; + } + + st->st_ino = filestate->dentry->fake_inode; + st->st_size = filestate->dentry->size_bytes; + return 0; +} + +#endif + +static off_t _DVD_seek_r (struct _reent *r, int fd, off_t pos, int dir) +{ + + struct FILESTATE *filestate = (struct FILESTATE *)fd; + int newpos = 0; + + // printf("DVD_seek fd: %d pos:%d ",fd,pos); + // if(dir==SEEK_SET)printf("SEEK_SET\n"); + // if(dir==SEEK_CUR)printf("SEEK_CUR\n"); + // if(dir==SEEK_END)printf("SEEK_END\n"); + + if (!filestate) + { + r->_errno = EBADF; + return -1; + } + + if (dir == SEEK_SET) + { + newpos = pos; + } + + else if (dir == SEEK_CUR) + { + newpos = pos + (filestate->pos_bytes); + + if (newpos > (filestate->size_bytes))newpos = filestate->size_bytes; + + if (newpos < (filestate->pos_bytes)) + { + r->_errno = EOVERFLOW; + return -1; + } + } + + else if (dir == SEEK_END) + { + newpos = (filestate->size_bytes) - pos; + + if (newpos < 0) + { + r->_errno = EINVAL; + return -1; + } + } + + else + { + r->_errno = EINVAL; + return -1; + } + + // printf(" pos:%d newpos:%d\n",pos,newpos); + filestate->pos_bytes = newpos; + + return newpos; +} + +static ssize_t _DVD_read_r (struct _reent *r, int fd, char *ptr, size_t len) +{ + + struct FILESTATE *filestate = (struct FILESTATE *)fd; + int bytestoread, bytesread, smallread, localpos, sectorneeded, retval; + + + if (!filestate) + { + r->_errno = EBADF; + return -1; + } + + // printf("DVD_read fd: %d curpos: %d :len:%d ",fd,filestate->pos_bytes,len); + + if ((len + (filestate->pos_bytes)) > (filestate->size_bytes)) + { + len = (filestate->size_bytes) - (filestate->pos_bytes); + + if (len < 0)len = 0; + + r->_errno = EOVERFLOW; + + if (len == 0) return 0; + } + + bytestoread = len; + bytesread = 0; + + do + { + smallread = bytestoread; + + sectorneeded = ((filestate->pos_bytes) >> 11) + (filestate->firstsector); + localpos = (filestate->pos_bytes) & 0x7FF; + + if (smallread > (0x800 - localpos))smallread = 0x800 - localpos; + + + if (filestate->localsectbuf == NULL) + { + filestate->localsectbuf = (uint8_t *)memalign(32, 0x800); + + if (filestate->localsectbuf == NULL) + { + r->_errno = ENOMEM; + return -1; + } + + filestate->localsectnum = sectorneeded + 1; //make sure we don't use this empty buffer! + } + + if (sectorneeded != filestate->localsectnum) + { + filestate->localsectnum = sectorneeded; + retval = ReadSectorFromCache(filestate->localsectbuf, sectorneeded); + + if (retval) + { + r->_errno = EIO; + return -1; + } + } + + // printf("bytestoread: %d localpos: %d, smallread: %d\n",bytestoread,localpos,smallread); + // dump_hex(&(filestate->localsectbuf[localpos]),smallread); + memcpy(ptr, (filestate->localsectbuf) + localpos, smallread); + + ptr += smallread; + + bytestoread -= smallread; + + bytesread += smallread; + + filestate->pos_bytes += smallread; + } + + while (bytestoread > 0); + + // printf("Read %d bytes\n",bytesread); + return bytesread; +} + +static int _DVD_close_r (struct _reent *r, int fd) +{ + + struct FILESTATE *filestate = (struct FILESTATE *)fd; + // printf("DVD_close\n"); + + if (filestate == NULL) + { + r->_errno = ENOENT; + return -1; + } + + if (filestate->localsectbuf)free(filestate->localsectbuf); + + return 0; +} + +int _DVD_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode) +{ + + struct FILESTATE *filestate = (struct FILESTATE *)fileStruct; + char file_absolute[MAXPATH]; + + struct DIRENTRY * dentry; + + // printf("DVD_open\n"); + + if (!WIIDVD_DiscPresent()) + { + r->_errno = ENODEV; + return -1; + } + + if (absolute_path_without_device(path, file_absolute) == NULL) + { + r->_errno = EINVAL; + return -1; + } + + // Determine which mode the file is openned for + if ((flags & 0x03) != O_RDONLY) + { + r->_errno = EROFS; + return -1; + } + + // printf("Trying to open '%s'\n",file_absolute); + dentry = name_to_dentry(file_absolute, 1, 1); //filename must be absolute, without dvd: at beginning + + if (!dentry) + { + r->_errno = ENOENT; + return -1; + } + + if (dentry->isdir) + { + r->_errno = EISDIR; + return -1; + } + + filestate->dentry = dentry; + filestate->pos_bytes = 0; + filestate->size_bytes = dentry->size_bytes; + filestate->firstsector = dentry->firstsector; + filestate->localsectnum = 0; + filestate->localsectbuf = NULL; + + // printf("Opened file '%s', firstsector: %d\n",filestate->dentry->name,filestate->firstsector); + return (int)filestate; +} + +static uint32_t bothendian32_to_uint32(uint8_t *buff) +{ + uint32_t val; + val = ((uint32_t)buff[4]) << 24; + val |= ((uint32_t)buff[5]) << 16; + val |= ((uint32_t)buff[6]) << 8; + val |= ((uint32_t)buff[7]); + return val; +} + +/* +static uint32_t bigendian32_to_uint32(uint8_t *buff) +{ + uint32_t val; + val=((uint32_t)buff[0])<<24; + val|=((uint32_t)buff[1])<<16; + val|=((uint32_t)buff[2])<<8; + val|=((uint32_t)buff[3]); + return val; +} + +static uint16_t bigendian16_to_uint16(uint8_t *buff) +{ + uint16_t val; + val=((uint16_t)buff[0])<<8; + val|=((uint16_t)buff[1]); + return val; +} +*/ + +static void freedirentryandchilds(struct DIRENTRY *dentry) +{ + + struct DIRENTRY *dentrynext; + + if (!dentry) return ; + + do + { + dentrynext = dentry->next; + + if (dentry->child)freedirentryandchilds(dentry->child); + + free(dentry); + + dentry = dentrynext; + } + + while (dentry); + +} + +static void freedirentrieslist() +{ + freedirentryandchilds(direntriesptr); + direntriesptr = 0; +} + +#ifdef DEBUG + +static void debug_dump_direntry(int tablevel, struct DIRENTRY *dentry) +{ + char spaces[33] = " "; + spaces[tablevel*2] = 0; + + if (!dentry) + { + printf("%sNull Pointer!!!\n", spaces); + } + + + printf("%sName: '%s' FirstSector: %d isfile: %d isdir: %d ishidden: %d\r\n", spaces, dentry->name, dentry->firstsector, dentry->isfile, dentry->isdir, dentry->ishidden); + +} + +static void debug_dump_tree_recurse(int level, struct DIRENTRY *dentry, char *path_parent) +{ + char path[MAXPATH]; + char spaces[33] = " "; + spaces[level*2] = 0; + + printf("%sPath: %s\n", spaces, path_parent); + + do + { + if (dentry->isdir) + { + if (dentry->child) + { + strcpy(path, path_parent); + strcat(path, dentry->name); + strcat(path, "/"); + debug_dump_tree_recurse(level + 1, dentry->child, path); + } + } + + else + { + printf("%sFile: %s%s\n", spaces, path_parent, dentry->name); + // debug_dump_direntry(level,dentry); + } + + dentry = dentry->next; + } + + while (dentry); + +} + +static void debug_dump_tree() +{ + if (direntriesptr)debug_dump_tree_recurse(0, direntriesptr, "dvd:/"); +} + +#endif + +static int DVD_ScanContent_recurse(int jolietmode, struct DIRENTRY * parent, uint8_t level, uint32_t sectnum, uint32_t dirlen, int *fake_inodecounter) +{ + int retval, i; + uint8_t * ptr, avoid_recursion; + uint32_t sect_from, sect_to, sect, pos; + uint8_t len; + uint8_t rockridge_enabled = 0; + uint8_t rockridge_bytestoskip = 0; + + struct DIRENTRY *direntryptr, *direntryptr_prev; + uint8_t *localsectbuf; + + localsectbuf = (uint8_t *)memalign(32, 0x800); + + if (!localsectbuf) return -1; + + sect_from = sectnum; + + sect_to = sectnum + ((dirlen - 1) >> 11); + + // for(i=0;ifake_inode = *fake_inodecounter; + + direntryptr->firstsector = bothendian32_to_uint32(&ptr[2]); + + direntryptr->size_bytes = bothendian32_to_uint32(&ptr[10]); + + direntryptr->size_sectors = ((direntryptr->size_bytes) - 1) >> 11; + + flags = ptr[25]; + + direntryptr->next = NULL; + + direntryptr->isfile = (flags & 0x02) ? 0 : 1; + + direntryptr->isdir = (flags & 0x02) ? 1 : 0; + + direntryptr->ishidden = (flags & 0x01) ? 1 : 0; + + avoid_recursion = 0; + + namelen = ptr[32]; + + if (jolietmode) + { + // if(namelen>128)return -1; + + // printf("namelen: %d\n",namelen); + // dump_hex(&ptr[33],32); + + if (namelen == 1) + { + if ((ptr[33] == 0)) // . (self) + { + strcpy(direntryptr->name, "."); + avoid_recursion = 1; + } + if ((ptr[33] == 1)) // .. (parent) + { + strcpy(direntryptr->name, ".."); + avoid_recursion = 1; + } + } + + else //if namelen != 1 + { + uint8_t *charptr; + namelen >>= 1; + + charptr = &ptr[33]; + + for (i = 0;i < namelen;i++) + { + if ((charptr[0] == 0) && (charptr[1] >= 32) && (charptr[1] < 0x80)) + direntryptr->name[i] = charptr[1]; + else + direntryptr->name[i] = '_'; //we don't support multi-byte characters or extended ascii characters, so replace the strange things with this symbol + + charptr += 2; + } + + direntryptr->name[i] = 0; + } + } + + else //non-joliet mode, so ISO9660:1999 mode with or without RockRidge extension + { + unsigned int bytesused; + + if (namelen > 207) return -1; + + memcpy(direntryptr->name, &ptr[33], namelen); + + bytesused = 33 + namelen; + + if ((namelen&0x01) == 0)bytesused++; //padding + + if ((namelen == 1) && (direntryptr->name[0] == 0) && (direntryptr->name[1] == 0)) // . (self) + { + strcpy(direntryptr->name, "."); + avoid_recursion = 1; + + if ((level == 0) && (!jolietmode)) + { + if ((ptr[bytesused] == 'S') && (ptr[bytesused + 1] == 'P') && (ptr[bytesused + 2] == 7) && (ptr[bytesused + 3] == 1) && (ptr[bytesused + 4] == 0xBE) && (ptr[bytesused + 5] == 0xEF)) + { + rockridge_enabled = 1; + rockridge_bytestoskip = ptr[bytesused + 6]; + } + } + + } + + if ((namelen == 1) && (direntryptr->name[0] == 1) && (direntryptr->name[1] == 0)) // .. (parent) + { + strcpy(direntryptr->name, ".."); + avoid_recursion = 1; + } + + + //check for RockRidge extension + if ((len > bytesused) && (rockridge_enabled)) //we have other data in System Use field + { + + uint8_t *RRAttribute_ptr; + uint8_t RRAttribute_len; + uint8_t RRAttribute_ver; + + bytesused += rockridge_bytestoskip; + // printf("entry %s namelen %d bytesused: %u \n",direntryptr->name,namelen,bytesused); + // dump_hex(&ptr[bytesused],len-bytesused); + + do + { + RRAttribute_ptr = &ptr[bytesused]; + RRAttribute_len = RRAttribute_ptr[2] - 4; + RRAttribute_ver = RRAttribute_ptr[3]; + + // printf("Signature: %c%c len(payload): %d ver: %d datalen: %d\n",RRAttribute_ptr[0],RRAttribute_ptr[1],RRAttribute_ptr[2],RRAttribute_ptr[3],RRAttribute_len); + // dump_hex(RRAttribute_ptr,RRAttribute_len+4); + + if (RRAttribute_ver == 1) + { + + if ((RRAttribute_ptr[0] == 'N') && (RRAttribute_ptr[1] == 'M')) //Name attribute + { + namelen = RRAttribute_len - 1; + memcpy(direntryptr->name, &RRAttribute_ptr[5], namelen); + direntryptr->name[namelen] = 0; + } + } + + bytesused += RRAttribute_ptr[2]; + } + + while ((bytesused < len) && (RRAttribute_ptr[2] > 0)); + } + } + + if (namelen > 2) + { + if ((direntryptr->name[namelen - 2] == ';') && (direntryptr->name[namelen - 1] == '1'))direntryptr->name[namelen - 2] = 0; + + if (direntryptr->name[namelen - 1] == ';')direntryptr->name[namelen - 1] = 0; + } + + if (!direntryptr_prev) //first item in chain + { + + if (parent)parent->child = direntryptr; + else direntriesptr = direntryptr; + } + else + { + direntryptr_prev->next = direntryptr; + } + + // debug_dump_direntry(level,direntryptr); + + if ((direntryptr->isdir) && (!avoid_recursion)) + DVD_ScanContent_recurse(jolietmode, direntryptr, level + 1, direntryptr->firstsector, direntryptr->size_bytes, fake_inodecounter); + } + + pos += len; + direntryptr_prev = direntryptr; + } + + while ((len != 0) && (pos < 2048)); + } + + free(localsectbuf); + return 0; +} + +static int DVD_ScanContent() +{ + int retval, currsect, done; + uint32_t rootsect = 0; + uint32_t rootdirlength = 0; + int fake_inodecounter = 1; + int fstype = -1; //0:standard ISO9660, 1:joliet + uint8_t *sectbuf; + uint8_t maxsectorstotry = 100; + + if (!WIIDVD_DiscPresent()) return -1; + + sectbuf = (uint8_t *)memalign(32, 0x800); + + if (!sectbuf) return -2; + + currsect = 16; + + done = 0; + + do + { + + //read first volume descriptor + retval = ReadSectorFromCache(sectbuf, currsect); + // retval=WIIDVD_ReadDVD(sectbuf,1,currsect); + + if (retval != 0) + { + return -1; + } + + // printf("Descriptor @ sect %d:\n",currsect); + // dump_hex(sectbuf,7); + + if ((sectbuf[1] == 67) && (sectbuf[2] == 68) && (sectbuf[3] == 48) && (sectbuf[4] == 48) && (sectbuf[5] == 49) && (sectbuf[6] == 1)) //volume descriptor signature + { + + if (sectbuf[0] == 255)done = 1; //end marker; + + if ((sectbuf[0] == 0x02) && (fstype <= 1)) //Supplementart Volume Descriptor + { + // printf("Supplementary Volume Descriptor found @ sect %d\n",currsect); + // printf(" Escape sequence:"); + // dump_hex(§buf[88],32); + // printf("\n"); + totalsectors = bothendian32_to_uint32(§buf[80]); + // printf("Total sectors: %u\n",totalsectors); + + rootsect = bothendian32_to_uint32(§buf[158]); + // printf("Root Sector: %d\n",rootsect); + rootdirlength = bothendian32_to_uint32(§buf[166]); + // printf("Root Length: %d\n",rootdirlength); + + if ((sectbuf[88] == 0x25) && (sectbuf[89] == 0x2F) && ((sectbuf[90] == 0x40) || (sectbuf[90] == 0x43) || (sectbuf[90] == 0x45))) + { + // if(sectbuf[90]==0x40)printf(" joliet Level1 descriptor found\n"); + // if(sectbuf[90]==0x43)printf(" joliet Level2 descriptor found\n"); + // if(sectbuf[90]==0x45)printf(" joliet Level3 descriptor found\n"); + fstype = 1; //joliet + } + + } + + if ((sectbuf[0] == 0x01) && (fstype <= 0)) //Primary Volume Descriptor + { + // printf("Primary Volume Descriptor found @ sect %d\n",currsect); + totalsectors = bothendian32_to_uint32(§buf[80]); + // printf("Total sectors: %u\n",totalsectors); + + rootsect = bothendian32_to_uint32(§buf[158]); + // printf("Root Sector: %d\n",rootsect); + rootdirlength = bothendian32_to_uint32(§buf[166]); + // printf("Root Length: %d\n",rootdirlength); + fstype = 0; + } + + } + currsect++; + maxsectorstotry--; + } + + while ((!done) && (maxsectorstotry)); + + if (fstype >= 0) + { + + struct DIRENTRY *direntryptr; + + if (direntriesptr)freedirentrieslist(); + + direntryptr = (struct DIRENTRY *)malloc(sizeof(struct DIRENTRY)); + + if (direntryptr == NULL) return -1; + + totalentries++; + + memset(direntryptr, 0, sizeof(struct DIRENTRY)); + + direntryptr->fake_inode = fake_inodecounter++; + + direntryptr->firstsector = rootsect; + + direntryptr->size_bytes = rootdirlength; + + direntryptr->size_sectors = ((direntryptr->size_bytes) - 1) >> 11; + + direntryptr->next = NULL; + + direntryptr->child = NULL; + + direntryptr->name[0] = 0; + + direntryptr->isfile = 0; + + direntryptr->isdir = 1; + + direntriesptr = direntryptr; + + return DVD_ScanContent_recurse(fstype == 1 ? 1 : 0, direntryptr, 0, rootsect, rootdirlength, &fake_inodecounter); + + // debug_dump_tree(); + } + + free(sectbuf); + return 0; +} + +#ifndef DEBUG +const devoptab_t dotab_dvd = + { + "dvd", + + sizeof (struct FILESTATE), + _DVD_open_r, + _DVD_close_r, + NULL, + _DVD_read_r, + _DVD_seek_r, + _DVD_fstat_r, + _DVD_stat_r, + NULL, + NULL, + _DVD_chdir_r, + NULL, + NULL, + + sizeof (struct DIR_STATE), + _DVD_diropen_r, + _DVD_dirreset_r, + _DVD_dirnext_r, + _DVD_dirclose_r, + _DVD_statvfs_r, + NULL, // device ftruncate_r + NULL, // device fsync_r + NULL /* Device data */ + + }; + + +static void dotab_dvd_add(void) +{ + AddDevice(&dotab_dvd); +} + +#endif + + +#ifdef DEBUG +int main(int argc, char *argv[]) +{ + printf("PC DEBUG MODE\n"); + WIIDVD_Init(); + WIIDVD_Mount(); + debug_dump_tree(); + WIIDVD_Close(); + return 0; +} + +#endif + +/////////////////////////////////////////// +// CACHE FUNCTIONS // +/////////////////////////////////////////// + + +static inline void _DVD_lock() +{ + LWP_MutexLock(_DVD_mutex); +} + +static inline void _DVD_unlock() +{ + LWP_MutexUnlock(_DVD_mutex); +} + +static void DestroyReadAheadCache() +{ + int i; + + if (ReadAheadCache == NULL) + { + RA_pages = 0; + RA_sectors = 0; + return ; + } + + for (i = 0; i < RA_pages; i++) + { + if (ReadAheadCache[i].ptr != NULL) + free(ReadAheadCache[i].ptr); + } + + free(ReadAheadCache); + ReadAheadCache = NULL; + RA_pages = 0; + RA_sectors = 0; +} + +static void DVDEnableReadAhead(uint32_t pages, uint32_t sectors) +{ + int i, j; + + DestroyReadAheadCache(); + + if (pages == 0 || sectors == 0) return ; + + if (sectors > 26)sectors = 26; + + RA_pages = pages; + + RA_sectors = sectors; + + ReadAheadCache = (cache_page *)malloc(sizeof(cache_page) * RA_pages); + + if (ReadAheadCache == NULL) return ; + + for (i = 0; i < RA_pages; i++) + { + ReadAheadCache[i].sector = CACHE_FREE; + ReadAheadCache[i].last_used = 0; + ReadAheadCache[i].ptr = memalign(32, SECTOR_SIZE * RA_sectors); + + if (ReadAheadCache[i].ptr == NULL) + { + for (j = i - 1;j >= 0;j--) + if (ReadAheadCache[j].ptr)free(ReadAheadCache[j].ptr); + + free(ReadAheadCache); + + ReadAheadCache = NULL; + + return ; + } + + memset(ReadAheadCache[i].ptr, 0, SECTOR_SIZE * RA_sectors); + } +} + +static int ReadSectorFromCache(void *buf, uint32_t sector) +{ + int retval; + int i, leastUsed; + + if (ReadAheadCache == NULL) return WIIDVD_ReadDVD(buf, 1, sector); + + _DVD_lock(); + + leastUsed = 0; + + for (i = 0; i < RA_pages; i++) + { + if ( (sector >= ReadAheadCache[i].sector) && (sector < (ReadAheadCache[i].sector + RA_sectors)) ) + { + ReadAheadCache[i].last_used = gettick(); + memcpy(buf, ReadAheadCache[i].ptr + ((sector - ReadAheadCache[i].sector) * SECTOR_SIZE), SECTOR_SIZE); + _DVD_unlock(); + return 0; + } + } + + + for (i = 1; i < RA_pages; i++) + { + if ((ReadAheadCache[i].last_used < ReadAheadCache[leastUsed].last_used)) + leastUsed = i; + } + + retval = WIIDVD_ReadDVD(ReadAheadCache[leastUsed].ptr, RA_sectors, sector); + + if (retval) + { + ReadAheadCache[leastUsed].sector = CACHE_FREE; + ReadAheadCache[leastUsed].last_used = 0; + _DVD_unlock(); + return retval; + } + + ReadAheadCache[leastUsed].sector = sector; + ReadAheadCache[leastUsed].last_used = gettick(); + memcpy(buf, ReadAheadCache[leastUsed].ptr, SECTOR_SIZE); + _DVD_unlock(); + + return 0; +} diff --git a/test/source/libdvdiso.h b/test/source/libdvdiso.h new file mode 100644 index 00000000..2d2089f2 --- /dev/null +++ b/test/source/libdvdiso.h @@ -0,0 +1,20 @@ +#ifndef _LWISO9660_DEVOPTAB_H +#define _LWISO9660_DEVOPTAB_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + extern int WIIDVD_Init(bool dvdx); + extern void WIIDVD_Close(void); + extern int WIIDVD_Mount(void); + extern void WIIDVD_Unmount(void); + extern int WIIDVD_DiscPresent(void); + +#ifdef __cplusplus +} + +#endif + +#endif diff --git a/test/source/libmload/include/mload.h b/test/source/libmload/include/mload.h new file mode 100644 index 00000000..29c8e6a6 --- /dev/null +++ b/test/source/libmload/include/mload.h @@ -0,0 +1,203 @@ +/* mload.c (for PPC) (c) 2009, Hermes + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __MLOAD_H__ +#define __MLOAD_H__ + + +#include +#include +#include +#include +#include +#include "unistd.h" + +#define MLOAD_MLOAD_THREAD_ID 0x4D4C4400 +#define MLOAD_LOAD_MODULE 0x4D4C4480 +#define MLOAD_RUN_MODULE 0x4D4C4481 +#define MLOAD_RUN_THREAD 0x4D4C4482 + +#define MLOAD_STOP_THREAD 0x4D4C4484 +#define MLOAD_CONTINUE_THREAD 0x4D4C4485 + +#define MLOAD_GET_LOAD_BASE 0x4D4C4490 +#define MLOAD_MEMSET 0x4D4C4491 + +#define MLOAD_GET_EHCI_DATA 0x4D4C44A0 + +#define MLOAD_SET_ES_IOCTLV 0x4D4C44B0 + +#ifdef __cplusplus +extern "C" +{ +#endif + + + // from IOS ELF stripper of neimod + +#define getbe32(x) ((adr[x]<<24) | (adr[x+1]<<16) | (adr[x+2]<<8) | (adr[x+3])) + + typedef struct + { + u32 ident0; + u32 ident1; + u32 ident2; + u32 ident3; + u32 machinetype; + u32 version; + u32 entry; + u32 phoff; + u32 shoff; + u32 flags; + u16 ehsize; + u16 phentsize; + u16 phnum; + u16 shentsize; + u16 shnum; + u16 shtrndx; + } + + elfheader; + + typedef struct + { + u32 type; + u32 offset; + u32 vaddr; + u32 paddr; + u32 filesz; + u32 memsz; + u32 flags; + u32 align; + } + + elfphentry; + + typedef struct + { + void *start; + int prio; + void *stack; + int size_stack; + } + + data_elf; + + /*--------------------------------------------------------------------------------------------------------------*/ + + // to init/test if the device is running + + int mload_init(); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // to close the device (remember call it when rebooting the IOS!) + + int mload_close(); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // to get the thread id of mload + + int mload_get_thread_id(); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // get the base and the size of the memory readable/writable to load modules + + int mload_get_load_base(u32 *starlet_base, int *size); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // load and run a module from starlet (it need to allocate MEM2 to send the elf file) + // the module must be a elf made with stripios + + int mload_module(void *addr, int len); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // load a module from the PPC + // the module must be a elf made with stripios + + int mload_elf(void *my_elf, data_elf *data_elf); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // run one thread (you can use to load modules or binary files) + + int mload_run_thread(void *starlet_addr, void *starlet_top_stack, int stack_size, int priority); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // stops one starlet thread + + int mload_stop_thread(int id); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // continue one stopped starlet thread + + int mload_continue_thread(int id); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // fix starlet address to read/write (uses SEEK_SET, etc as mode) + + int mload_seek(int offset, int mode); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // read bytes from starlet (it update the offset) + + int mload_read(void* buf, u32 size); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // write bytes from starlet (it update the offset) + + int mload_write(const void * buf, u32 size); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // fill a block (similar to memset) + + int mload_memset(void *starlet_addr, int set, int len); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // get the ehci datas ( ehcmodule.elf uses this address) + + void * mload_get_ehci_data(); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // set the dev/es ioctlv in routine + + int mload_set_ES_ioctlv_vector(void *starlet_addr); + + /*--------------------------------------------------------------------------------------------------------------*/ + + + +#ifdef __cplusplus + +} + +#endif + + +#endif diff --git a/test/source/libmload/source/mload.c b/test/source/libmload/source/mload.c new file mode 100644 index 00000000..c03dff50 --- /dev/null +++ b/test/source/libmload/source/mload.c @@ -0,0 +1,410 @@ +/* mload.c (for PPC) (c) 2009, Hermes + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "mload.h" + +static const char mload_fs[] ATTRIBUTE_ALIGN(32) = "/dev/mload"; + +static s32 mload_fd = -1; + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to init/test if the device is running + +int mload_init() +{ + int n; + + if (mload_fd >= 0) return 0; + + for (n = 0;n < 10;n++) // try 2.5 seconds + { + mload_fd = IOS_Open(mload_fs, 0); + + if (mload_fd >= 0) break; + + usleep(250*1000); + } + + return mload_fd; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to close the device (remember call it when rebooting the IOS!) + +int mload_close() +{ + int ret; + + if (mload_fd < 0) return -1; + + ret = IOS_Close(mload_fd); + + mload_fd = -1; + + return ret; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to get the thread id of mload + +int mload_get_thread_id() +{ + int ret; + s32 hid = -1; + + + if (mload_init() < 0) return -1; + + hid = iosCreateHeap(0x800); + + if (hid < 0) return hid; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_MLOAD_THREAD_ID, ":"); + + + iosDestroyHeap(hid); + + return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// get the base and the size of the memory readable/writable to load modules + +int mload_get_load_base(u32 *starlet_base, int *size) +{ + int ret; + s32 hid = -1; + + + if (mload_init() < 0) return -1; + + hid = iosCreateHeap(0x800); + + if (hid < 0) return hid; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_GET_LOAD_BASE, ":ii", starlet_base, size); + + + iosDestroyHeap(hid); + + return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// load and run a module from starlet (it need to allocate MEM2 to send the elf file) +// the module must be a elf made with stripios + +int mload_module(void *addr, int len) +{ + int ret; + void *buf = NULL; + s32 hid = -1; + + if (mload_init() < 0) return -1; + + hid = iosCreateHeap(len + 0x800); + + if (hid < 0) return hid; + + buf = iosAlloc(hid, len); + +if (!buf) {ret = -1;goto out;} + + + memcpy(buf, addr, len); + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_LOAD_MODULE, ":d", buf, len); + + if (ret < 0) goto out; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_RUN_MODULE, ":"); + +if (ret < 0) {ret = -666;goto out;} + +out: + + iosDestroyHeap(hid); + + return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// load a module from the PPC +// the module must be a elf made with stripios + +int mload_elf(void *my_elf, data_elf *data_elf) +{ + int n, m; + int p; + u8 *adr; + u32 elf = (u32) my_elf; + + if (elf & 3) return -1; // aligned to 4 please! + + elfheader *head = (void *) elf; + + elfphentry *entries; + + if (head->ident0 != 0x7F454C46) return -1; + + if (head->ident1 != 0x01020161) return -1; + + if (head->ident2 != 0x01000000) return -1; + + p = head->phoff; + + data_elf->start = (void *) head->entry; + + for (n = 0; n < head->phnum; n++) + { + entries = (void *) (elf + p); + p += sizeof(elfphentry); + + if (entries->type == 4) + { + adr = (void *) (elf + entries->offset); + + if (getbe32(0) != 0) return -2; // bad info (sure) + + for (m = 4; m < entries->memsz; m += 8) + { + switch (getbe32(m)) + { + case 0x9: + data_elf->start = (void *) getbe32(m + 4); + break; + case 0x7D: + data_elf->prio = getbe32(m + 4); + break; + case 0x7E: + data_elf->size_stack = getbe32(m + 4); + break; + case 0x7F: + data_elf->stack = (void *) (getbe32(m + 4)); + break; + + } + + } + + } + + else + if (entries->type == 1 && entries->memsz != 0 && entries->vaddr != 0) + { + + if (mload_memset((void *) entries->vaddr, 0, entries->memsz) < 0) return -1; + + if (mload_seek(entries->vaddr, SEEK_SET) < 0) return -1; + + if (mload_write((void *) (elf + entries->offset), entries->filesz) < 0) return -1; + + } + } + + return 0; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// run one thread (you can use to load modules or binary files) + +int mload_run_thread(void *starlet_addr, void *starlet_top_stack, int stack_size, int priority) +{ + int ret; + s32 hid = -1; + + + if (mload_init() < 0) return -1; + + hid = iosCreateHeap(0x800); + + if (hid < 0) return hid; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_RUN_THREAD, "iiii:", starlet_addr, starlet_top_stack, stack_size, priority); + + + iosDestroyHeap(hid); + + return ret; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// stops one starlet thread + +int mload_stop_thread(int id) +{ + int ret; + s32 hid = -1; + + + if (mload_init() < 0) return -1; + + hid = iosCreateHeap(0x800); + + if (hid < 0) return hid; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_STOP_THREAD, "i:", id); + + + iosDestroyHeap(hid); + + return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// continue one stopped starlet thread + +int mload_continue_thread(int id) +{ + int ret; + s32 hid = -1; + + + if (mload_init() < 0) return -1; + + hid = iosCreateHeap(0x800); + + if (hid < 0) return hid; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_CONTINUE_THREAD, "i:", id); + + + iosDestroyHeap(hid); + + return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// fix starlet address to read/write (uses SEEK_SET, etc as mode) + +int mload_seek(int offset, int mode) +{ + if (mload_init() < 0) return -1; + + return IOS_Seek(mload_fd, offset, mode); +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// read bytes from starlet (it update the offset) + +int mload_read(void* buf, u32 size) +{ + if (mload_init() < 0) return -1; + + return IOS_Read(mload_fd, buf, size); +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// write bytes from starlet (it update the offset) + +int mload_write(const void * buf, u32 size) +{ + if (mload_init() < 0) return -1; + + return IOS_Write(mload_fd, buf, size); +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// fill a block (similar to memset) + +int mload_memset(void *starlet_addr, int set, int len) +{ + int ret; + s32 hid = -1; + + + if (mload_init() < 0) return -1; + + hid = iosCreateHeap(0x800); + + if (hid < 0) return hid; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_MEMSET, "iii:", starlet_addr, set, len); + + + iosDestroyHeap(hid); + + return ret; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// get the ehci datas ( ehcmodule.elf uses this address) + +void * mload_get_ehci_data() +{ + int ret; + s32 hid = -1; + + + if (mload_init() < 0) return NULL; + + hid = iosCreateHeap(0x800); + + if (hid < 0) return NULL; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_GET_EHCI_DATA, ":"); + + if (ret < 0) return NULL; + + iosDestroyHeap(hid); + + return (void *) ret; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// set the dev/es ioctlv in routine + +int mload_set_ES_ioctlv_vector(void *starlet_addr) +{ + int ret; + s32 hid = -1; + + + if (mload_init() < 0) return -1; + + hid = iosCreateHeap(0x800); + + if (hid < 0) return hid; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_SET_ES_IOCTLV, "i:", starlet_addr); + + + iosDestroyHeap(hid); + + return ret; +} + diff --git a/test/source/main.c b/test/source/main.c new file mode 100644 index 00000000..aa0a91f9 --- /dev/null +++ b/test/source/main.c @@ -0,0 +1,441 @@ +/************************************************************/ +/* USB speed test */ +/* */ +/* */ +/************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "mload.h" +#include "../build/ehcmodule_elf.h" +//#include "../build/fat_elf.h" +data_elf my_data_elf; +int my_thread_id = 0; + + +#include + +#include +#include +#include +#include "usb2storage.h" + + + +const DISC_INTERFACE* sd = &__io_wiisd; +const DISC_INTERFACE* usb = &__io_usbstorage; +//const DISC_INTERFACE* usb1 = &__io_usb1storage; + +static bool reset_pressed = false; +static bool power_pressed = false; + +static void *xfb = NULL; +static GXRModeObj *rmode = NULL; +static const u32 CACHE_PAGES = 8; + +static u8 memsector[4*1024]; + + +void initialise_video() +{ + VIDEO_Init(); + rmode = VIDEO_GetPreferredMode(NULL); + xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); + console_init(xfb, 20, 20, rmode->fbWidth, rmode->xfbHeight, rmode->fbWidth * VI_DISPLAY_PIX_SZ); + VIDEO_Configure(rmode); + VIDEO_SetNextFramebuffer(xfb); + VIDEO_SetBlack(FALSE); + VIDEO_Flush(); + VIDEO_WaitVSync(); + + if (rmode->viTVMode & VI_NON_INTERLACE) VIDEO_WaitVSync(); + + printf("\x1b[2;0H"); + + VIDEO_WaitVSync(); +} + +#include +//#define ticks_to_secs(ticks) ((u32)((u64)(ticks)/(u64)(TB_TIMER_CLOCK*1000))) +//#define ticks_to_msecs(ticks) ((u32)((u64)(ticks)/(u64)(TB_TIMER_CLOCK))) + +void showdir(char *path) +{ + char filename[MAXPATHLEN]; + DIR_ITER *dp; + + struct stat fstat; + //chdir("usb:/"); + dp = diropen(path); + + if (dp == NULL) + { + printf("Error diropen\n"); + return ; + } + + else printf("Ok diropen\n"); + + int cnt = 0; + + while ( dirnext( dp, filename, &fstat ) == 0 ) + { + //if(cnt==20) break; + cnt++; + + if ( fstat.st_mode & S_IFDIR ) printf("/"); + + printf("%s\n", filename); + + } + + dirclose(dp); + printf("\nEnd show dirrectory (%d).\n", cnt); +} + +static void reset_cb (void) +{ + reset_pressed = true; +} + +static void power_cb (void) +{ + power_pressed = true; +} + +#include + + +#define VERSION "1.52" + +void save_log() // at now all data is sent to usbgecko, sd log disabled +{ + FILE *fp; + u8 *temp_data = NULL; + u32 level, len; + + temp_data = memalign(32, 256 * 1024); + + mload_seek(0x13750000, SEEK_SET); + mload_read(temp_data, 128*1024); + + //mload_init(); + + //mload_close(); + + for (len = 0;len < 4096 && temp_data[len] != 0;len++); + + fatUnmount("sd"); + + if (fatMount("sd", sd, 0, 128, 2) ) + { + + fp = fopen("sd:/log_usb2.txt", "wb"); + + if (fp != 0) + { + fprintf(fp, "USB2 device test program. v%s\n------------------------------\n", VERSION); + fwrite(temp_data, 1, len , fp); + + fclose(fp); + } + } + + if (usb_isgeckoalive(1)) + { + level = IRQ_Disable(); + usb_sendbuffer(1, temp_data, len); + IRQ_Restore(level); + } + + //printf("\nlog:\n-------\n%s", temp_data); + free(temp_data); +} + +void TryUSBDevice() +{ + printf("Initializing usb device.\n"); + bool s; + + USB2Enable(true); + s = usb->startup(); + usleep(10000); + + if (s > 0) + s = __usb2storage_ReadSectors(0, 1, memsector); + + usleep(10000); + + + if (s > 0) + { + + printf("usb device detected, your device is fully compatible with this usb2 driver\n"); + } + + else + { + //(s==-1) printf("usb device NOT INSERTED, log file not created\n"); + //se + { + printf("usb device NOT detected or not inserted, report 'ubs2 init' value and log file\n"); + printf("Log saved to sd:/log_usb2.txt file\n"); + } + } + + save_log(); +} + +static s32 initialise_network() +{ + s32 result; + + while ((result = net_init()) == -EAGAIN) + { + if (reset_pressed) exit(1); + + usleep(100); + } + + return result; +} + +int wait_for_network_initialisation() +{ + printf("Waiting for network to initialise...\n"); + + if (initialise_network() >= 0) + { + char myIP[16]; + + if (if_config(myIP, NULL, NULL, true) < 0) + { + printf("Error reading IP address\n"); + return 0; + } + + else + { + printf("Network initialised. Wii IP address: %s\n", myIP); + return 1; + } + } + + printf("Unable to initialise network\n"); + return 0; +} + +#define ROUNDDOWN32(v) (((u32)(v)-0x1f)&~0x1f) +static bool usblan_detected(void) +{ + u8 *buffer; + u8 dummy; + u8 i; + u16 vid, pid; + + USB_Initialize(); + + buffer = (u8*)ROUNDDOWN32(((u32)SYS_GetArena2Hi() - (32 * 1024))); + + memset(buffer, 0, 8 << 3); + + if (USB_GetDeviceList("/dev/usb/oh0", buffer, 8, 0, &dummy) < 0) + { + return false; + } + + for (i = 0; i < 8; i++) + { + memcpy(&vid, (buffer + (i << 3) + 4), 2); + memcpy(&pid, (buffer + (i << 3) + 6), 2); + + if ( (vid == 0x0b95) && (pid == 0x7720)) + { + return true; + } + } + + return false; +} + +void init_mload() +{ + int ret; + ret = mload_init(); + + if (ret < 0) + { + printf("fail to get dev/mload, mload cios not detected. Quit\n"); + sleep(3); + exit(0); + //goto out; + } +} + +void loadehcimodule() +{ + u32 addr; + int len; + + mload_get_load_base(&addr, &len); + + if (((u32) ehcmodule_elf) & 3) {printf("Unaligned elf!\n"); return ;} + + mload_elf((void *) ehcmodule_elf, &my_data_elf); + my_thread_id = mload_run_thread(my_data_elf.start, my_data_elf.stack, my_data_elf.size_stack, /*my_data_elf.prio*/0x47); + //my_thread_id=mload_run_thread(my_data_elf.start, my_data_elf.stack, my_data_elf.size_stack, my_data_elf.prio); + + if (my_thread_id < 0) + { + printf("EHCI module can NOT be loaded (%i).. Quit\n", my_thread_id); + sleep(3); + exit(0); + } + + printf("EHCI module loaded (%i).\n", my_thread_id); + VIDEO_WaitVSync(); +} + +int main(int argc, char **argv) +{ + int ret; + bool usblan; + int usblanport; + + initialise_video(); + CON_EnableGecko(1, 0); + + usblan = usblan_detected(); + printf("ios 202 reload\n"); + IOS_ReloadIOS(202); + printf("ios 202 reloaded\n"); + init_mload(); + mload_reset_log(); + loadehcimodule(); + + if (usblan) + { + printf("uslan detected\n"); + usblanport = GetUSB2LanPort(); + printf("usblanport: %i\n", usblanport); + USB2Storage_Close(); + + mload_close(); + IOS_ReloadIOS(202); + init_mload(); + loadehcimodule(); + printf("SetUSBMode(%i)\n", usblanport); + + if (usblanport == 1) + SetUSB2Mode(1); + else + SetUSB2Mode(2); + + } + + else + printf("usblan NOT found\n"); + + SYS_SetResetCallback (reset_cb); + + SYS_SetPowerCallback (power_cb); + + // usleep(10000); + //WIIDVD_Init(false); + // usleep(10000); + + TryUSBDevice(); // init usb2 device + + // usleep(10000); + // save_log(); + // usleep(10000); + /* + if(WIIDVD_DiscPresent()) printf("dvd inserted\n"); + else printf("dvd NOT inserted\n"); + usleep(10000); + save_log(); + + if(fatMount("usb",usb,0,3,128)) + { + showdir("usb:/"); + } + else printf("can't mount usb\n"); + + sleep(6); + return 0; + wait_for_network_initialisation(); // check if lan is working + sleep(1); + */ + printf("Press Home or reset to exit. Press A or power to try to detect usb device again.\n"); + + VIDEO_WaitVSync(); + + VIDEO_WaitVSync(); + + while (1) //wait home press to exit + { + WPAD_ScanPads(); + + if (reset_pressed) break; + + u32 pressed = WPAD_ButtonsDown(0); + + if ( pressed & WPAD_BUTTON_HOME ) + { + mload_close(); + printf ("\n\nExit ..."); + exit(0); + } + + if ( (pressed & WPAD_BUTTON_A) || power_pressed) + { + power_pressed = false; + + TryUSBDevice(); + //usleep(5000); + power_pressed = false; + /* + if(fatMount("usb",usb,0,3,128)) + { + showdir("usb:/"); + } + else printf("can't mount usb\n"); + */ + //TryUSBDevice(); + /* + printf("deinit (5secs)\n"); + sleep(1); + net_deinit(); + sleep(4); + printf("init\n"); + wait_for_network_initialisation(); + */ + //power_pressed=false; + } + + usleep(5000); + VIDEO_WaitVSync(); + } + +out: + mload_close(); + printf ("\n\nExit ..."); + exit(0); +} diff --git a/test/source/mload.c b/test/source/mload.c new file mode 100644 index 00000000..e4fd17af --- /dev/null +++ b/test/source/mload.c @@ -0,0 +1,493 @@ +/* mload.c (for PPC) (c) 2009, Hermes + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "mload.h" + +static const char mload_fs[] ATTRIBUTE_ALIGN(32) = "/dev/mload"; + +static s32 mload_fd = -1; +static s32 hid = -1; + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to init/test if the device is running + +int mload_init() +{ + int n; + + if (hid < 0) hid = iosCreateHeap(0x800); + + if (hid < 0) + { + if (mload_fd >= 0) + IOS_Close(mload_fd); + + mload_fd = -1; + + return hid; + } + + if (mload_fd >= 0) + { + return 0; + } + + for (n = 0;n < 20;n++) // try 5 seconds + { + mload_fd = IOS_Open(mload_fs, 0); + + if (mload_fd >= 0) break; + + usleep(250*1000); + } + + if (mload_fd < 0) + { + + if (hid >= 0) + { + iosDestroyHeap(hid); + hid = -1; + } + } + + return mload_fd; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to close the device (remember call it when rebooting the IOS!) + +int mload_close() +{ + int ret; + + if (hid >= 0) + { + iosDestroyHeap(hid); + hid = -1; + } + + if (mload_fd < 0) return -1; + + ret = IOS_Close(mload_fd); + + mload_fd = -1; + + return ret; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to get the thread id of mload + +int mload_get_thread_id() +{ + int ret; + + if (mload_init() < 0) return -1; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_MLOAD_THREAD_ID, ":"); + + return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// get the base and the size of the memory readable/writable to load modules + +int mload_get_load_base(u32 *starlet_base, int *size) +{ + int ret; + + if (mload_init() < 0) return -1; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_GET_LOAD_BASE, ":ii", starlet_base, size); + + return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// load and run a module from starlet (it need to allocate MEM2 to send the elf file) +// the module must be a elf made with stripios + +int mload_module(void *addr, int len) +{ + int ret; + void *buf = NULL; + + + if (mload_init() < 0) return -1; + + if (hid >= 0) + { + iosDestroyHeap(hid); + hid = -1; + } + + hid = iosCreateHeap(len + 0x800); + + if (hid < 0) return hid; + + buf = iosAlloc(hid, len); + +if (!buf) {ret = -1;goto out;} + + + memcpy(buf, addr, len); + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_LOAD_MODULE, ":d", buf, len); + + if (ret < 0) goto out; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_RUN_MODULE, ":"); + +if (ret < 0) {ret = -666;goto out;} + +out: + + if (hid >= 0) + { + iosDestroyHeap(hid); + hid = -1; + } + + return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// load a module from the PPC +// the module must be a elf made with stripios + +int mload_elf(void *my_elf, data_elf *data_elf) +{ + int n, m; + int p; + u8 *adr; + u32 elf = (u32) my_elf; + + if (elf & 3) return -1; // aligned to 4 please! + + elfheader *head = (void *) elf; + + elfphentry *entries; + + if (head->ident0 != 0x7F454C46) return -1; + + if (head->ident1 != 0x01020161) return -1; + + if (head->ident2 != 0x01000000) return -1; + + p = head->phoff; + + data_elf->start = (void *) head->entry; + + for (n = 0; n < head->phnum; n++) + { + entries = (void *) (elf + p); + p += sizeof(elfphentry); + + if (entries->type == 4) + { + adr = (void *) (elf + entries->offset); + + if (getbe32(0) != 0) return -2; // bad info (sure) + + for (m = 4; m < entries->memsz; m += 8) + { + switch (getbe32(m)) + { + case 0x9: + data_elf->start = (void *) getbe32(m + 4); + break; + case 0x7D: + data_elf->prio = getbe32(m + 4); + break; + case 0x7E: + data_elf->size_stack = getbe32(m + 4); + break; + case 0x7F: + data_elf->stack = (void *) (getbe32(m + 4)); + break; + + } + + } + + } + + else + if (entries->type == 1 && entries->memsz != 0 && entries->vaddr != 0) + { + + if (mload_memset((void *) entries->vaddr, 0, entries->memsz) < 0) return -1; + + if (mload_seek(entries->vaddr, SEEK_SET) < 0) return -1; + + if (mload_write((void *) (elf + entries->offset), entries->filesz) < 0) return -1; + + } + } + + return 0; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// run one thread (you can use to load modules or binary files) + +int mload_run_thread(void *starlet_addr, void *starlet_top_stack, int stack_size, int priority) +{ + int ret; + + + if (mload_init() < 0) return -1; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_RUN_THREAD, "iiii:", starlet_addr, starlet_top_stack, stack_size, priority); + + + return ret; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// stops one starlet thread + +int mload_stop_thread(int id) +{ + int ret; + + if (mload_init() < 0) return -1; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_STOP_THREAD, "i:", id); + + return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// continue one stopped starlet thread + +int mload_continue_thread(int id) +{ + int ret; + + if (mload_init() < 0) return -1; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_CONTINUE_THREAD, "i:", id); + + return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// fix starlet address to read/write (uses SEEK_SET, etc as mode) + +int mload_seek(int offset, int mode) +{ + if (mload_init() < 0) return -1; + + return IOS_Seek(mload_fd, offset, mode); +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// read bytes from starlet (it update the offset) + +int mload_read(void* buf, u32 size) +{ + if (mload_init() < 0) return -1; + + return IOS_Read(mload_fd, buf, size); +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// write bytes from starlet (it update the offset) + +int mload_write(const void * buf, u32 size) +{ + if (mload_init() < 0) return -1; + + return IOS_Write(mload_fd, buf, size); +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// fill a block (similar to memset) + +int mload_memset(void *starlet_addr, int set, int len) +{ + int ret; + + if (mload_init() < 0) return -1; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_MEMSET, "iii:", starlet_addr, set, len); + + + return ret; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// get the ehci datas ( ehcmodule.elf uses this address) + +void * mload_get_ehci_data() +{ + int ret; + + if (mload_init() < 0) return NULL; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_GET_EHCI_DATA, ":"); + + if (ret < 0) return NULL; + + return (void *) ret; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// set the dev/es ioctlv in routine + +int mload_set_ES_ioctlv_vector(void *starlet_addr) +{ + int ret; + + if (mload_init() < 0) return -1; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_SET_ES_IOCTLV, "i:", starlet_addr); + + return ret; +} + + + +int mload_getw(const void * addr, u32 *dat) +{ + int ret; + + if (mload_init() < 0) return -1; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_GETW, "i:i", addr, dat); + + return ret; +} + +int mload_geth(const void * addr, u16 *dat) +{ + int ret; + + if (mload_init() < 0) return -1; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_GETH, "i:h", addr, dat); + + return ret; +} + +int mload_getb(const void * addr, u8 *dat) +{ + int ret; + + if (mload_init() < 0) return -1; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_GETB, "i:b", addr, dat); + + return ret; +} + +int mload_setw(const void * addr, u32 dat) +{ + int ret; + + if (mload_init() < 0) return -1; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_SETW, "ii:", addr, dat); + + return ret; +} + +int mload_seth(const void * addr, u16 dat) +{ + int ret; + + if (mload_init() < 0) return -1; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_SETH, "ih:", addr, dat); + + return ret; +} + +int mload_setb(const void * addr, u8 dat) +{ + int ret; + + if (mload_init() < 0) return -1; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_SETB, "ib:", addr, dat); + + return ret; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to get log buffer +// this function return the size of the log buffer and prepare it to read with mload_read() the datas + +int mload_get_log() +{ + int ret; + + if (mload_init() < 0) return -1; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_GET_LOG, ":"); + + return ret; + +} + +void mload_reset_log() +{ + if (mload_init() < 0) return ; + + IOS_IoctlvFormat(hid, mload_fd, MLOAD_RESET_LOG, ":"); +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to get IOS base for dev/es to create the cIOS + +int mload_get_IOS_base() +{ + int ret; + + if (mload_init() < 0) return -1; + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_GET_IOS_BASE, ":"); + + return ret; + +} + + diff --git a/test/source/mload.h b/test/source/mload.h new file mode 100644 index 00000000..ff900fa7 --- /dev/null +++ b/test/source/mload.h @@ -0,0 +1,240 @@ +/* mload.c (for PPC) (c) 2009, Hermes + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __MLOAD_H__ +#define __MLOAD_H__ + + +#include +#include +#include +#include +#include +#include "unistd.h" + +#define MLOAD_MLOAD_THREAD_ID 0x4D4C4400 +#define MLOAD_GET_IOS_BASE 0x4D4C4401 +#define MLOAD_LOAD_MODULE 0x4D4C4480 +#define MLOAD_RUN_MODULE 0x4D4C4481 +#define MLOAD_RUN_THREAD 0x4D4C4482 + +#define MLOAD_STOP_THREAD 0x4D4C4484 +#define MLOAD_CONTINUE_THREAD 0x4D4C4485 + +#define MLOAD_GET_LOAD_BASE 0x4D4C4490 +#define MLOAD_MEMSET 0x4D4C4491 + +#define MLOAD_GET_EHCI_DATA 0x4D4C44A0 +#define MLOAD_GET_LOG 0x4D4C44A1 +#define MLOAD_RESET_LOG 0x4D4C44A2 + +#define MLOAD_SET_ES_IOCTLV 0x4D4C44B0 + +#define MLOAD_GETW 0x4D4C44C0 +#define MLOAD_GETH 0x4D4C44C1 +#define MLOAD_GETB 0x4D4C44C2 +#define MLOAD_SETW 0x4D4C44C3 +#define MLOAD_SETH 0x4D4C44C4 +#define MLOAD_SETB 0x4D4C44C5 + +#ifdef __cplusplus +extern "C" +{ +#endif + + + // from IOS ELF stripper of neimod + +#define getbe32(x) ((adr[x]<<24) | (adr[x+1]<<16) | (adr[x+2]<<8) | (adr[x+3])) + + typedef struct + { + u32 ident0; + u32 ident1; + u32 ident2; + u32 ident3; + u32 machinetype; + u32 version; + u32 entry; + u32 phoff; + u32 shoff; + u32 flags; + u16 ehsize; + u16 phentsize; + u16 phnum; + u16 shentsize; + u16 shnum; + u16 shtrndx; + } + + elfheader; + + typedef struct + { + u32 type; + u32 offset; + u32 vaddr; + u32 paddr; + u32 filesz; + u32 memsz; + u32 flags; + u32 align; + } + + elfphentry; + + typedef struct + { + void *start; + int prio; + void *stack; + int size_stack; + } + + data_elf; + + /*--------------------------------------------------------------------------------------------------------------*/ + + // to init/test if the device is running + + int mload_init(); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // to close the device (remember call it when rebooting the IOS!) + + int mload_close(); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // to get the thread id of mload + + int mload_get_thread_id(); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // get the base and the size of the memory readable/writable to load modules + + int mload_get_load_base(u32 *starlet_base, int *size); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // load and run a module from starlet (it need to allocate MEM2 to send the elf file) + // the module must be a elf made with stripios + + int mload_module(void *addr, int len); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // load a module from the PPC + // the module must be a elf made with stripios + + int mload_elf(void *my_elf, data_elf *data_elf); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // run one thread (you can use to load modules or binary files) + + int mload_run_thread(void *starlet_addr, void *starlet_top_stack, int stack_size, int priority); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // stops one starlet thread + + int mload_stop_thread(int id); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // continue one stopped starlet thread + + int mload_continue_thread(int id); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // fix starlet address to read/write (uses SEEK_SET, etc as mode) + + int mload_seek(int offset, int mode); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // read bytes from starlet (it update the offset) + + int mload_read(void* buf, u32 size); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // write bytes from starlet (it update the offset) + + int mload_write(const void * buf, u32 size); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // fill a block (similar to memset) + + int mload_memset(void *starlet_addr, int set, int len); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // get the ehci datas ( ehcmodule.elf uses this address) + + void * mload_get_ehci_data(); + + /*--------------------------------------------------------------------------------------------------------------*/ + + // set the dev/es ioctlv in routine + + int mload_set_ES_ioctlv_vector(void *starlet_addr); + + /*--------------------------------------------------------------------------------------------------------------*/ + + + // to get log buffer + // this function return the size of the log buffer and prepare it to read with mload_read() the datas + + int mload_get_log(); + + void mload_reset_log(); + + /*--------------------------------------------------------------------------------------------------------------*/ + + + // to get IOS base for dev/es to create the cIOS + + int mload_get_IOS_base(); + + /*--------------------------------------------------------------------------------------------------------------*/ + + int mload_getw(const void * addr, u32 *dat); + + int mload_geth(const void * addr, u16 *dat); + + int mload_getb(const void * addr, u8 *dat); + + int mload_setw(const void * addr, u32 dat); + + int mload_seth(const void * addr, u16 dat); + + int mload_setb(const void * addr, u8 dat); + +#ifdef __cplusplus + +} + +#endif + + +#endif diff --git a/test/source/usb2storage.c b/test/source/usb2storage.c new file mode 100644 index 00000000..5a1edf26 --- /dev/null +++ b/test/source/usb2storage.c @@ -0,0 +1,411 @@ +/**************************************************************************** +* WiiMC +* usb2storage.c -- USB mass storage support, inside starlet +* Copyright (C) 2008 Kwiirk +* Improved for homebrew by rodries and Tantric +* +* IOS 202 and the ehcmodule must be loaded before using this! +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any +* damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any +* purpose, including commercial applications, and to alter it and +* redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you +* must not claim that you wrote the original software. If you use +* this software in a product, an acknowledgment in the product +* documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and +* must not be misrepresented as being the original software. +* +* 3. This notice may not be removed or altered from any source +* distribution. +* +***************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//#define DEBUG_USB2 + +#ifdef DEBUG_USB2 +#define debug_printf(fmt, args...) \ + fprintf(stderr, "%s:%d:" fmt, __FUNCTION__, __LINE__, ##args) +#else +#define debug_printf(fmt, args...) +#endif // DEBUG_USB2 + +#define UMS_BASE (('U'<<24)|('M'<<16)|('S'<<8)) +#define USB_IOCTL_UMS_INIT (UMS_BASE+0x1) +#define USB_IOCTL_UMS_GET_CAPACITY (UMS_BASE+0x2) +#define USB_IOCTL_UMS_READ_SECTORS (UMS_BASE+0x3) +#define USB_IOCTL_UMS_WRITE_SECTORS (UMS_BASE+0x4) +#define USB_IOCTL_UMS_READ_STRESS (UMS_BASE+0x5) +#define USB_IOCTL_UMS_SET_VERBOSE (UMS_BASE+0x6) +#define USB_IOCTL_UMS_IS_INSERTED (UMS_BASE+0x7) +#define USB_IOCTL_UMS_USB_MODE (UMS_BASE+0x8) +#define USB_IOCTL_UMS_GET_USBLAN_PORT (UMS_BASE+0x9) + +#define USB_IOCTL_UMS_UMOUNT (UMS_BASE+0x10) +#define USB_IOCTL_UMS_START (UMS_BASE+0x11) +#define USB_IOCTL_UMS_STOP (UMS_BASE+0x12) +#define USB_IOCTL_UMS_EXIT (UMS_BASE+0x16) + +#define UMS_HEAPSIZE 2*1024 +#define UMS_MAXPATH 16 + +static s32 hId = -1; +static s32 __usb2fd = -1; +static u32 sector_size; +static mutex_t usb2_mutex = LWP_MUTEX_NULL; +static u8 *fixed_buffer = NULL; + +#define ROUNDDOWN32(v) (((u32)(v)-0x1f)&~0x1f) +#define USB2_BUFFER 128*1024 +static heap_cntrl usb2_heap; +static u8 __usb2_heap_created = 0; + +static DISC_INTERFACE __io_usb1storage; +static int usb1disc_inited = 0; +extern const DISC_INTERFACE __io_usb2storage; +static int currentMode = 2; // 1 = use USB1 interface, 2 = use USB2 interface + +static s32 USB2CreateHeap() +{ + u32 level; + void *usb2_heap_ptr; + + if (__usb2_heap_created != 0) + return IPC_OK; + + _CPU_ISR_Disable(level); + + usb2_heap_ptr = (void *) ROUNDDOWN32(((u32)SYS_GetArena2Hi() - (USB2_BUFFER + (4 * 1024)))); + + if ((u32) usb2_heap_ptr < (u32) SYS_GetArena2Lo()) + { + _CPU_ISR_Restore(level); + return IPC_ENOMEM; + } + + SYS_SetArena2Hi(usb2_heap_ptr); + __lwp_heap_init(&usb2_heap, usb2_heap_ptr, (USB2_BUFFER + (4 * 1024)), 32); + __usb2_heap_created = 1; + _CPU_ISR_Restore(level); + return IPC_OK; +} + +static s32 USB2Storage_Initialize() +{ + char *devicepath = NULL; + + if (usb2_mutex == LWP_MUTEX_NULL) + LWP_MutexInit(&usb2_mutex, false); + + if (hId == -1) + hId = iosCreateHeap(UMS_HEAPSIZE); + + if (hId < 0) + { + debug_printf("error IPC_ENOMEM\n"); + return IPC_ENOMEM; + } + + if (USB2CreateHeap() != IPC_OK) + { + debug_printf("error USB2 IPC_ENOMEM\n"); + return IPC_ENOMEM; + } + + if (fixed_buffer == NULL) + fixed_buffer = __lwp_heap_allocate(&usb2_heap, USB2_BUFFER); + + LWP_MutexLock(usb2_mutex); + + if (__usb2fd <= 0) + { + devicepath = iosAlloc(hId, UMS_MAXPATH); + + if (devicepath == NULL) + { + LWP_MutexUnlock(usb2_mutex); + return IPC_ENOMEM; + } + + snprintf(devicepath, USB_MAXPATH, "/dev/usb2"); + __usb2fd = IOS_Open(devicepath, 0); + iosFree(hId, devicepath); + } + + if (__usb2fd <= 0) + return -1; + + return 0; +} + +static s32 USB2Storage_Open() +{ + s32 ret = USB_OK; + u32 size = 0; + + if (USB2Storage_Initialize() < 0) + return -1; + + ret = IOS_IoctlvFormat(hId, __usb2fd, USB_IOCTL_UMS_INIT, ":"); + + debug_printf("usb2 init value: %i\n", ret); + + if (ret < 0) + debug_printf("usb2 error init\n"); + else + size = IOS_IoctlvFormat(hId, __usb2fd, USB_IOCTL_UMS_GET_CAPACITY, ":i", + §or_size); + + debug_printf("usb2 GET_CAPACITY: %d\n", size); + + if (ret < 0 || size <= 0) + ret = -2012; + else + ret = 1; + + debug_printf("usb2 USB2Storage_Open ret: %d\n", ret); + + return ret; +} + +void USB2Storage_Close() +{ + if (__usb2fd <= 0) + return ; + + IOS_Close(__usb2fd); + + __usb2fd = -1; +} + +static inline int is_MEM2_buffer(const void *buffer) +{ + u32 high_addr = ((u32) buffer) >> 24; + return (high_addr == 0x90) || (high_addr == 0xD0); +} + +void USB2Enable(bool enable) +{ + if (!usb1disc_inited) + { + usb1disc_inited = 1; + memcpy(&__io_usb1storage, &__io_usbstorage, sizeof(DISC_INTERFACE)); + } + + if (!enable) + { + memcpy(&__io_usbstorage, &__io_usb1storage, sizeof(DISC_INTERFACE)); + } + + else + { + USB2Storage_Initialize(); + memcpy(&__io_usbstorage, &__io_usb2storage, sizeof(DISC_INTERFACE)); + } +} + +static bool __usb2storage_Startup(void) +{ + s32 ret; + + ret = USB2Storage_Open(); + + if (__usb2fd > 0 ) + { + currentMode = 2; + return ret > 0; + } + + if (__io_usb1storage.startup()) + { + currentMode = 1; + return true; + } + + return false; +} + +static bool __usb2storage_IsInserted(void) +{ + if (!USB2Storage_Initialize() < 0) + return false; + + + if (usb2_mutex == LWP_MUTEX_NULL) + return false; + + if (__usb2fd > 0) + { + LWP_MutexLock(usb2_mutex); + int retval = IOS_IoctlvFormat(hId, __usb2fd, USB_IOCTL_UMS_IS_INSERTED, ":"); + LWP_MutexUnlock(usb2_mutex); + debug_printf("isinserted usb2 retval: %d \n", retval); + + if (retval > 0) + return true; + } + + if (__io_usb1storage.isInserted() > 0) + { + return true; + } + + return false; +} + +bool __usb2storage_ReadSectors(u32 sector, u32 numSectors, void *buffer) +{ + s32 ret = 1; + u32 sectors = 0; + uint8_t *dest = buffer; + + if (currentMode == 1) + return __io_usb1storage.readSectors(sector, numSectors, buffer); + + if (__usb2fd < 1 || usb2_mutex == LWP_MUTEX_NULL) + return false; + + LWP_MutexLock(usb2_mutex); + + while (numSectors > 0) + { + if (numSectors * sector_size > USB2_BUFFER) + sectors = USB2_BUFFER / sector_size; + else + sectors = numSectors; + + if (!is_MEM2_buffer(dest)) //libfat is not providing us good buffers :-( + { + ret = IOS_IoctlvFormat(hId, __usb2fd, USB_IOCTL_UMS_READ_SECTORS, "ii:d", + sector, sectors, fixed_buffer, sector_size * sectors); + memcpy(dest, fixed_buffer, sector_size * sectors); + } + else + ret = IOS_IoctlvFormat(hId, __usb2fd, USB_IOCTL_UMS_READ_SECTORS, "ii:d", + sector, sectors, dest, sector_size * sectors); + + dest += sector_size * sectors; + + if (ret < 1) break; + + sector += sectors; + + numSectors -= sectors; + } + + if (ret < 1) USB2Storage_Close(); + + LWP_MutexUnlock(usb2_mutex); + + if (ret < 1) return false; + + return true; +} + +static bool __usb2storage_WriteSectors(u32 sector, u32 numSectors, const void *buffer) +{ + s32 ret = 1; + u32 sectors = 0; + uint8_t *dest = (uint8_t *) buffer; + + if (currentMode == 1) + return __io_usb1storage.writeSectors(sector, numSectors, buffer); + + if (__usb2fd < 1 || usb2_mutex == LWP_MUTEX_NULL) + return false; + + LWP_MutexLock(usb2_mutex); + + while (numSectors > 0 && ret > 0) + { + if (numSectors * sector_size > USB2_BUFFER) + sectors = USB2_BUFFER / sector_size; + else + sectors = numSectors; + + numSectors -= sectors; + + if (!is_MEM2_buffer(dest)) // libfat is not providing us good buffers :-( + { + memcpy(fixed_buffer, dest, sector_size * sectors); + ret = IOS_IoctlvFormat(hId, __usb2fd, USB_IOCTL_UMS_WRITE_SECTORS, + "ii:d", sector, sectors, fixed_buffer, sector_size + * sectors); + } + else + ret = IOS_IoctlvFormat(hId, __usb2fd, USB_IOCTL_UMS_WRITE_SECTORS, + "ii:d", sector, sectors, dest, sector_size * sectors); + + if (ret < 1)break; + + dest += sector_size * sectors; + + sector += sectors; + } + + LWP_MutexUnlock(usb2_mutex); + + if (ret < 1 ) return false; + + return true; +} + +static bool __usb2storage_ClearStatus(void) +{ + if (currentMode == 1) + return __io_usb1storage.clearStatus(); + + return true; +} + +static bool __usb2storage_Shutdown(void) +{ + if (currentMode == 1) + return __io_usb1storage.shutdown(); + + return true; +} + +void SetUSB2Mode(int mode) +{ + USB2Storage_Initialize(); + IOS_IoctlvFormat(hId, __usb2fd, USB_IOCTL_UMS_USB_MODE, "b:", (u8)mode); +} + +int GetUSB2LanPort(void) +{ + USB2Storage_Initialize(); + return IOS_IoctlvFormat(hId, __usb2fd, USB_IOCTL_UMS_GET_USBLAN_PORT, ":"); +} + +const DISC_INTERFACE __io_usb2storage = + { + DEVICE_TYPE_WII_USB, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_USB, + (FN_MEDIUM_STARTUP) & __usb2storage_Startup, + (FN_MEDIUM_ISINSERTED) & __usb2storage_IsInserted, + (FN_MEDIUM_READSECTORS) & __usb2storage_ReadSectors, + (FN_MEDIUM_WRITESECTORS) & __usb2storage_WriteSectors, + (FN_MEDIUM_CLEARSTATUS) & __usb2storage_ClearStatus, + (FN_MEDIUM_SHUTDOWN) & __usb2storage_Shutdown + }; diff --git a/test/source/usb2storage.h b/test/source/usb2storage.h new file mode 100644 index 00000000..99c92385 --- /dev/null +++ b/test/source/usb2storage.h @@ -0,0 +1,33 @@ +/**************************************************************************** +* WiiMC +* usb2storage.h -- USB mass storage support, inside starlet +* Copyright (C) 2008 Kwiirk +* Improved for homebrew by rodries and Tantric +* +* IOS 202 and the ehcimodule must be loaded before using this! +***************************************************************************/ + +#ifndef __USB2STORAGE_H__ +#define __USB2STORAGE_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + + void USB2Enable(bool e); + void USB2Storage_Close(); + int GetUSB2LanPort(void); + void SetUSB2Mode(int mode); + //mode 0: port0 disabled & port1 disabled + //mode 1: port0 enabled & port1 disabled + //mode 2: port0 disabled & port1 enabled + //mode 3: port0 enabled & port1 enabled + +#ifdef __cplusplus + +} + +#endif + +#endif diff --git a/test/test.rar b/test/test.rar new file mode 100644 index 00000000..096bebaf Binary files /dev/null and b/test/test.rar differ diff --git a/tinyehci/ehci.c.old b/tinyehci/ehci.c.old new file mode 100644 index 00000000..c548a333 --- /dev/null +++ b/tinyehci/ehci.c.old @@ -0,0 +1,980 @@ +/* simplest usb-ehci driver which features: + + control and bulk transfers only + only one transfer pending + driver is synchronous (waiting for the end of the transfer) + endianess independant + no uncached memory allocation needed + + this driver is originally based on the GPL linux ehci-hcd driver + + * Original Copyright (c) 2001 by David Brownell + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +void my_sprint(char *cad, char *s); + +/* magic numbers that can affect system performance */ +#define EHCI_TUNE_CERR 0 /* 0-3 qtd retries; 0 == don't stop */ /* by Hermes: i have replaced 3 by 0 and now it donīt hang when i extract the device*/ +#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ +#define EHCI_TUNE_RL_TT 0 +#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ +#define EHCI_TUNE_MULT_TT 1 +#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ +extern int verbose; +#ifdef DEBUG +static int num_command_before_no_verbose = 100; +#endif +static void +dbg_qtd (const char *label, struct ehci_qtd *qtd) +{ + ehci_dbg( "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, + hc32_to_cpup( &qtd->hw_next), + hc32_to_cpup( &qtd->hw_alt_next), + hc32_to_cpup( &qtd->hw_token), + hc32_to_cpup( &qtd->hw_buf [0])); + if (qtd->hw_buf [1]) + ehci_dbg( " p1=%08x p2=%08x p3=%08x p4=%08x\n", + hc32_to_cpup( &qtd->hw_buf[1]), + hc32_to_cpup( &qtd->hw_buf[2]), + hc32_to_cpup( &qtd->hw_buf[3]), + hc32_to_cpup( &qtd->hw_buf[4])); +} + +static void +dbg_qh (const char *label, struct ehci_qh *qh) +{ + ehci_dbg ( "%s qh %p n%08x info %x %x qtd %x\n", label, + qh, + hc32_to_cpu(qh->hw_next), + hc32_to_cpu(qh->hw_info1), + hc32_to_cpu(qh->hw_info2), + hc32_to_cpu(qh->hw_current)); + dbg_qtd ("overlay", (struct ehci_qtd *) &qh->hw_qtd_next); +} + +static void +dbg_command (void) +{ +#ifdef DEBUG + u32 command=ehci_readl( &ehci->regs->command); + u32 async=ehci_readl( &ehci->regs->async_next); + + ehci_dbg ("async_next: %08x\n",async); + ehci_dbg ( + "command %06x %s=%d ithresh=%d%s%s%s%s %s %s\n", + command, + (command & CMD_PARK) ? "park" : "(park)", + CMD_PARK_CNT (command), + (command >> 16) & 0x3f, + (command & CMD_LRESET) ? " LReset" : "", + (command & CMD_IAAD) ? " IAAD" : "", + (command & CMD_ASE) ? " Async" : "", + (command & CMD_PSE) ? " Periodic" : "", + (command & CMD_RESET) ? " Reset" : "", + (command & CMD_RUN) ? "RUN" : "HALT" + ); +#endif +} +static void +dbg_status (void) +{ +#ifdef DEBUG + u32 status=ehci_readl( &ehci->regs->status); + ehci_dbg ( + "status %04x%s%s%s%s%s%s%s%s%s%s\n", + status, + (status & STS_ASS) ? " Async" : "", + (status & STS_PSS) ? " Periodic" : "", + (status & STS_RECL) ? " Recl" : "", + (status & STS_HALT) ? " Halt" : "", + (status & STS_IAA) ? " IAA" : "", + (status & STS_FATAL) ? " FATAL" : "", + (status & STS_FLR) ? " FLR" : "", + (status & STS_PCD) ? " PCD" : "", + (status & STS_ERR) ? " ERR" : "", + (status & STS_INT) ? " INT" : "" + ); +#endif +} + +void debug_qtds(void) +{ + struct ehci_qh *qh = ehci->async; + struct ehci_qtd *qtd; + dbg_qh ("qh",qh); + dbg_command (); + dbg_status (); + for(qtd = qh->qtd_head; qtd; qtd = qtd->next) + { + ehci_dma_unmap_bidir(qtd->qtd_dma,sizeof(struct ehci_qtd)); + dbg_qtd("qtd",qtd); + ehci_dma_map_bidir(qtd,sizeof(struct ehci_qtd)); + } + +} +void dump_qh(struct ehci_qh *qh) +{ + struct ehci_qtd *qtd; + dbg_command (); + dbg_status (); + ehci_dma_unmap_bidir(qh->qh_dma,sizeof(struct ehci_qh)); + dbg_qh("qh",qh); + print_hex_dump_bytes("qh:",DUMP_PREFIX_OFFSET,(void*)qh,12*4); + for(qtd = qh->qtd_head; qtd; qtd = qtd->next){ + u32 *buf; + ehci_dma_unmap_bidir(qtd->qtd_dma,sizeof(struct ehci_qtd)); + dbg_qtd("qtd",qtd); + print_hex_dump_bytes("qtd:",DUMP_PREFIX_OFFSET,(void*)qtd,8*4); + buf = (u32*)hc32_to_cpu(qtd->hw_buf[0]); + if(buf) + print_hex_dump_bytes("qtd buf:",DUMP_PREFIX_OFFSET,(void*)(buf),8*4); + + } +} + +/*-------------------------------------------------------------------------*/ + +/* + * handshake - spin reading hc until handshake completes or fails + * @ptr: address of hc register to be read + * @mask: bits to look at in result of read + * @done: value of those bits when handshake succeeds + * @usec: timeout in microseconds + * + * Returns negative errno, or zero on success + * + * Success happens when the "mask" bits have the specified value (hardware + * handshake done). There are two failure modes: "usec" have passed (major + * hardware flakeout), or the register reads as all-ones (hardware removed). + * + * That last failure should_only happen in cases like physical cardbus eject + * before driver shutdown. But it also seems to be caused by bugs in cardbus + * bridge shutdown: shutting down the bridge before the devices using it. + */ +int unplug_device=0; + + +static int handshake (void __iomem *pstatus, void __iomem *ptr, + u32 mask, u32 done, int usec) +{ + u32 result,status; + + + do { + status = ehci_readl( pstatus); + result = ehci_readl( ptr); + if ((result == ~(u32)0) || (PORT_OWNER&status) || !(PORT_CONNECT&status)) /* card removed */ + {unplug_device=1; + return -ENODEV; + } + result &= mask; + + if (result == done) + { +#ifdef DEBUG + if(num_command_before_no_verbose) + { + num_command_before_no_verbose--; + if(num_command_before_no_verbose==0) + verbose=0; + } +#endif + return 0; + } + udelay (100); /* Hermes: 100 microseconds is a god time to response and better for multithread (i think). + the new timer uses syscalls and queues to release the thread focus */ + usec-=100; + } while (usec > 0); +#ifdef DEBUG + verbose = 1; + num_command_before_no_verbose=100; +#endif + ehci_dbg("\nhandshake timeout!!\n\n"); + //dump_qh(ehci->async); + //dump_qh(ehci->asyncqh); + //BUG(); + + unplug_device=1; +#ifndef HOMEBREW + return -ENODEV; /* Hermes: with ENODEV works the unplugin method receiving datas (fatal error) + ENODEV return without retries and unplug_device can works without interferences. + i think is no a good idea too much retries when is possible the device needs one drastic action + */ +#endif + return -ETIMEDOUT; +} + +#include "ehci-mem.c" +/* one-time init, only for memory state */ +static int ehci_init(void) +{ + int retval; + if ((retval = ehci_mem_init()) < 0) + return retval; + /* + * dedicate a qh for the async ring head, since we couldn't unlink + * a 'real' qh without stopping the async schedule [4.8]. use it + * as the 'reclamation list head' too. + * its dummy is used in hw_alt_next of many tds, to prevent the qh + * from automatically advancing to the next td after short reads. + */ + ehci->async->hw_next = QH_NEXT( ehci->async->qh_dma); + ehci->async->hw_info1 = cpu_to_hc32( QH_HEAD); + ehci->async->hw_token = cpu_to_hc32( QTD_STS_HALT); + ehci->async->hw_qtd_next = EHCI_LIST_END(); + ehci->async->hw_alt_next = EHCI_LIST_END();//QTD_NEXT( ehci->async->dummy->qtd_dma); + ehci->ctrl_buffer = USB_Alloc(sizeof(usbctrlrequest)); + ehci->command = 0; + + ehci_writel( 0x008000002, &ehci->regs->command); + ehci_writel( ehci->periodic_dma, &ehci->regs->frame_list); + ehci_writel( ehci->async->qh_dma, &ehci->regs->async_next); + ehci_writel( 0x00010009, &ehci->regs->command); + ehci_writel( 1, &ehci->regs->configured_flag); + ehci_writel( 0x00010029, &ehci->regs->command); + + + return 0; +} + +/* fill a qtd, returning how much of the buffer we were able to queue up */ +static int +qtd_fill(struct ehci_qtd *qtd, dma_addr_t buf, + size_t len, int token, int maxpacket) +{ + int i, count; + u64 addr = buf; + //ehci_dbg("fill qtd with dma %X len %X\n",buf,len); + /* one buffer entry per 4K ... first might be short or unaligned */ + qtd->hw_buf[0] = cpu_to_hc32( (u32)addr); + qtd->hw_buf_hi[0] = 0; + count = 0x1000 - (buf & 0x0fff); /* rest of that page */ + if (likely (len < count)) /* ... iff needed */ + count = len; + else { + buf += 0x1000; + buf &= ~0x0fff; + + /* per-qtd limit: from 16K to 20K (best alignment) */ + for (i = 1; count < len && i < 5; i++) { + addr = buf; + qtd->hw_buf[i] = cpu_to_hc32( (u32)addr); + qtd->hw_buf_hi[i] = cpu_to_hc32( + (u32)(addr >> 32)); + buf += 0x1000; + if ((count + 0x1000) < len) + count += 0x1000; + else + count = len; + } + + /* short packets may only terminate transfers */ + if (count != len) + count -= (count % maxpacket); + } + qtd->hw_token = cpu_to_hc32( (count << 16) | token); + qtd->length = count; + + return count; +} + +// high bandwidth multiplier, as encoded in highspeed endpoint descriptors +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) +// ... and packet size, for any kind of endpoint descriptor +#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) + +/* + * reverse of qh_urb_transaction: free a list of TDs. + * also count the actual transfer length. + * + */ +static void qh_end_transfer (void) +{ + struct ehci_qtd *qtd; + struct ehci_qh *qh = ehci->asyncqh; + u32 token; + int error = 0; + for(qtd = qh->qtd_head; qtd; qtd = qtd->next){ + token = hc32_to_cpu( qtd->hw_token); + if (likely (QTD_PID (token) != 2)) + qtd->urb->actual_length += qtd->length - QTD_LENGTH (token); + if (!(qtd->length ==0 && ((token & 0xff)==QTD_STS_HALT)) && + (token & QTD_STS_HALT)) { + ehci_dbg("\nqtd error!:"); + if (token & QTD_STS_BABBLE) { + ehci_dbg(" BABBLE"); + } + if (token & QTD_STS_MMF) { + /* fs/ls interrupt xfer missed the complete-split */ + ehci_dbg(" missed micro frame"); + } + if (token & QTD_STS_DBE) { + ehci_dbg(" databuffer error"); + } + if (token & QTD_STS_XACT) { + ehci_dbg(" wrong ack"); + } + if (QTD_CERR (token)==0) + ehci_dbg(" toomany errors"); + ehci_dbg("\n"); + error = -1; + } + } + if(error){ +#ifdef DEBUG + verbose = 1; + num_command_before_no_verbose=100; +#endif + //dump_qh(ehci->asyncqh); + qtd->urb->actual_length = error; + } + ehci->qtd_used = 0; +} + +/* + * create a list of filled qtds for this URB; won't link into qh. + */ +struct ehci_qtd *qh_urb_transaction ( + struct ehci_urb *urb +) { + struct ehci_qtd *qtd, *qtd_prev; + struct ehci_qtd *head; + dma_addr_t buf; + int len, maxpacket; + int is_input; + u32 token; + + /* + * URBs map to sequences of QTDs: one logical transaction + */ + head = qtd = ehci_qtd_alloc (); + qtd->urb = urb; + + urb->actual_length = 0; + token = QTD_STS_ACTIVE; + token |= (EHCI_TUNE_CERR << 10); + /* for split transactions, SplitXState initialized to zero */ + + len = urb->transfer_buffer_length; + is_input = urb->input; + if (urb->ep==0) {/* is control */ + /* SETUP pid */ + qtd_fill( qtd, urb->setup_dma, + sizeof (usbctrlrequest), + token | (2 /* "setup" */ << 8), 8); + + /* ... and always at least one more pid */ + token ^= QTD_TOGGLE; + qtd_prev = qtd; + qtd = ehci_qtd_alloc (); + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT( qtd->qtd_dma); + qtd_prev->next = qtd; + + /* for zero length DATA stages, STATUS is always IN */ + if (len == 0) + token |= (1 /* "in" */ << 8); + } + + /* + * data transfer stage: buffer setup + */ + buf = urb->transfer_dma; + + if (is_input) + token |= (1 /* "in" */ << 8); + /* else it's already initted to "out" pid (0 << 8) */ + + maxpacket = max_packet(urb->maxpacket); + + /* + * buffer gets wrapped in one or more qtds; + * last one may be "short" (including zero len) + * and may serve as a control status ack + */ + for (;;) { + int this_qtd_len; + + this_qtd_len = qtd_fill( qtd, buf, len, token, maxpacket); + len -= this_qtd_len; + buf += this_qtd_len; + + /* + * short reads advance to a "magic" dummy instead of the next + * qtd ... that forces the queue to stop, for manual cleanup. + * (this will usually be overridden later.) + */ + if (is_input) + qtd->hw_alt_next = ehci->asyncqh->hw_alt_next; + + /* qh makes control packets use qtd toggle; maybe switch it */ + if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) + token ^= QTD_TOGGLE; + + if (likely (len <= 0)) + break; + + qtd_prev = qtd; + qtd = ehci_qtd_alloc (); + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT( qtd->qtd_dma); + qtd_prev->next = qtd; + } + + qtd->hw_alt_next = EHCI_LIST_END(); + + /* + * control requests may need a terminating data "status" ack; + * bulk ones may need a terminating short packet (zero length). + */ + if (likely (urb->transfer_buffer_length != 0)) { + int one_more = 0; + + if (urb->ep==0) { + one_more = 1; + token ^= 0x0100; /* "in" <--> "out" */ + token |= QTD_TOGGLE; /* force DATA1 */ + } else if(!(urb->transfer_buffer_length % maxpacket)) { + //one_more = 1; + } + if (one_more) { + qtd_prev = qtd; + qtd = ehci_qtd_alloc (); + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT( qtd->qtd_dma); + qtd_prev->next = qtd; + + /* never any data in such packets */ + qtd_fill( qtd, 0, 0, token, 0); + } + } + + /* by default, enable interrupt on urb completion */ + qtd->hw_token |= cpu_to_hc32( QTD_IOC); + return head; +} +//u32 usb_timeout=2000*1000; +u32 usb_timeout=200*1000; + + int ehci_do_urb ( + struct ehci_device *dev, + struct ehci_urb *urb) +{ + struct ehci_qh *qh; + struct ehci_qtd *qtd; + u32 info1 = 0, info2 = 0; + int is_input; + int maxp = 0; + int retval=0; + + //ehci_dbg ("do urb %X %X ep %X\n",urb->setup_buffer,urb->transfer_buffer,urb->ep); + if(urb->ep==0) //control message + urb->setup_dma = ehci_dma_map_to(urb->setup_buffer,sizeof (usbctrlrequest)); + + if(urb->transfer_buffer_length){ + if(urb->input) + urb->transfer_dma = ehci_dma_map_to(urb->transfer_buffer,urb->transfer_buffer_length); + else + urb->transfer_dma = ehci_dma_map_from(urb->transfer_buffer,urb->transfer_buffer_length); + } + qh = ehci->asyncqh; + memset(qh,0,12*4); + qtd = qh_urb_transaction ( urb); + qh ->qtd_head = qtd; + + + info1 |= ((urb->ep)&0xf)<<8; + info1 |= dev->id; + is_input = urb->input; + maxp = urb->maxpacket; + + info1 |= (2 << 12); /* EPS "high" */ + if(urb->ep==0)// control + { + info1 |= (EHCI_TUNE_RL_HS << 28); + info1 |= 64 << 16; /* usb2 fixed maxpacket */ + info1 |= 1 << 14; /* toggle from qtd */ + info2 |= (EHCI_TUNE_MULT_HS << 30); + }else//bulk + { + info1 |= (EHCI_TUNE_RL_HS << 28); + /* The USB spec says that high speed bulk endpoints + * always use 512 byte maxpacket. But some device + * vendors decided to ignore that, and MSFT is happy + * to help them do so. So now people expect to use + * such nonconformant devices with Linux too; sigh. + */ + info1 |= max_packet(maxp) << 16; + info2 |= (EHCI_TUNE_MULT_HS << 30); + + } + //ehci_dbg("HW info: %08X\n",info1); + qh->hw_info1 = cpu_to_hc32( info1); + qh->hw_info2 = cpu_to_hc32( info2); + + qh->hw_next = QH_NEXT(qh->qh_dma); + qh->hw_qtd_next = QTD_NEXT( qtd->qtd_dma); + qh->hw_alt_next = EHCI_LIST_END(); + + if(urb->ep!=0){ + if(get_toggle(dev,urb->ep)) + qh->hw_token |= cpu_to_hc32(QTD_TOGGLE); + else + qh->hw_token &= ~cpu_to_hc32( QTD_TOGGLE); + //ehci_dbg("toggle for ep %x: %d %x\n",urb->ep,get_toggle(dev,urb->ep),qh->hw_token); + } + + qh->hw_token &= cpu_to_hc32( QTD_TOGGLE | QTD_STS_PING); + + qh->hw_next = QH_NEXT(ehci->async->qh_dma); + + + ehci_dma_map_bidir(qh,sizeof(struct ehci_qh)); + for(qtd = qh->qtd_head; qtd; qtd = qtd->next) + ehci_dma_map_bidir(qtd,sizeof(struct ehci_qtd)); +#if 0 + if(urb->ep!=0){ + dump_qh(ehci->async); + dump_qh(ehci->asyncqh); + } +#endif + // start (link qh) + ehci->async->hw_next = QH_NEXT(qh->qh_dma); + ehci_dma_map_bidir(ehci->async,sizeof(struct ehci_qh)); + + retval = handshake(&ehci->regs->port_status[dev->port],&ehci->regs->status,STS_INT,STS_INT,usb_timeout); + + //print_hex_dump_bytes ("qh mem",0,(void*)qh,17*4); + //retval = poll_transfer_end(1000*1000); + ehci_dma_unmap_bidir(ehci->async->qh_dma,sizeof(struct ehci_qh)); + ehci_dma_unmap_bidir(qh->qh_dma,sizeof(struct ehci_qh)); + for(qtd = qh->qtd_head; qtd; qtd = qtd->next) + ehci_dma_unmap_bidir(qtd->qtd_dma,sizeof(struct ehci_qtd)); + + // stop (unlink qh) + ehci->async->hw_next = QH_NEXT(ehci->async->qh_dma); + ehci_dma_map_bidir(ehci->async,sizeof(struct ehci_qh)); + ehci_dma_unmap_bidir(ehci->async->qh_dma,sizeof(struct ehci_qh)); + + // ack + ehci_writel( STS_RECL|STS_IAA|STS_INT, &ehci->regs->status); + + if(urb->ep!=0){ + set_toggle(dev,urb->ep,(qh->hw_token & cpu_to_hc32(QTD_TOGGLE))?1:0); + //ehci_dbg("toggle for ep %x: %d %d %x %X\n",urb->ep,get_toggle(dev,urb->ep),(qh->hw_token & cpu_to_hc32(QTD_TOGGLE)),qh->hw_token,dev->toggles); + } + + if(retval>=0) + // wait hc really stopped + retval = handshake(&ehci->regs->port_status[dev->port],&ehci->regs->async_next,~0,ehci->async->qh_dma,60*1000); + //release memory, and actualise urb->actual_length + qh_end_transfer(); + + + if(urb->transfer_buffer_length){ + if(urb->input) + ehci_dma_unmap_to(urb->transfer_dma,urb->transfer_buffer_length); + else + ehci_dma_unmap_from(urb->transfer_dma,urb->transfer_buffer_length); + } + if(urb->ep==0) //control message + ehci_dma_unmap_to(urb->setup_dma,sizeof (usbctrlrequest)); + if(retval==0){ + + return urb->actual_length; + + ehci_dbg ( "un successfull urb %d!!\n", retval); + } + return retval; +} + +s32 ehci_control_message(struct ehci_device *dev,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u32 wLength,void *buf) +{ + struct ehci_urb urb; + usbctrlrequest *req = ehci->ctrl_buffer; + if(verbose) + ehci_dbg ( "control msg: rt%02X r%02X v%04X i%04X s%04x %p\n", bmRequestType, bmRequest, wValue, wIndex,wLength,buf); + req->bRequestType = bmRequestType; + req->bRequest = bmRequest; + req->wValue = swab16(wValue); + req->wIndex = swab16(wIndex); + req->wLength = swab16(wLength); + urb.setup_buffer = req; + urb.ep = 0; + urb.input = (bmRequestType&USB_CTRLTYPE_DIR_DEVICE2HOST)!=0; + urb.maxpacket = 64; + + + urb.transfer_buffer_length = wLength; + + if (((u32)buf) > 0x13880000){// HW cannot access this buffer, we allow this for convenience + int ret; + urb.transfer_buffer = USB_Alloc(wLength); + if(urb.transfer_buffer==NULL) + { + //my_sprint("USB Alloc: not enough memory! transfer_buffer", NULL); + return -ENOMEM; + } + if (verbose) + ehci_dbg("alloc another buffer %p %p\n",buf,urb.transfer_buffer); + memcpy(urb.transfer_buffer,buf,wLength); + ret = ehci_do_urb(dev,&urb); + memcpy(buf,urb.transfer_buffer,wLength); + USB_Free(urb.transfer_buffer); + return ret; + } + else + + { + urb.transfer_buffer = buf; + return ehci_do_urb(dev,&urb); + } +} +s32 ehci_bulk_message(struct ehci_device *dev,u8 bEndpoint,u32 wLength,void *rpData) +{ + struct ehci_urb urb; + s32 ret; + urb.setup_buffer = NULL; + urb.ep = bEndpoint; + urb.input = (bEndpoint&0x80)!=0; + urb.maxpacket = 512; + urb.transfer_buffer_length = wLength; + urb.transfer_buffer = rpData; + if(verbose) + ehci_dbg ( "bulk msg: ep:%02X size:%02X addr:%04X", bEndpoint, wLength, rpData); + + ret= ehci_do_urb(dev,&urb); + /* + int i; + for(i=0;i<1;i++) + { + ret= ehci_do_urb(dev,&urb); + //if(ret<0) msleep(500); + //else break; + } + //if(ret<0)my_sprint("urb error",NULL); + */ + if(verbose) + ehci_dbg ( "==>%d\n", ret); + return ret; +} + + +int ehci_reset_port(int port) +{ + u32 __iomem *status_reg = &ehci->regs->port_status[port]; + + struct ehci_device *dev = &ehci->devices[port]; + u32 status = ehci_readl(status_reg); + + int retval = 0; + dev->id = 0; + + + if ((PORT_OWNER&status) || !(PORT_CONNECT&status)) + { + ehci_writel( PORT_OWNER,status_reg); + ehci_dbg ( "port %d had no usb2 device connected at startup %X \n", port,ehci_readl(status_reg)); + return -ENODEV;// no USB2 device connected + } + ehci_dbg ( "port %d has usb2 device connected! reset it...\n", port); + +char log[2048]; +char cad[200]; + +log[0]='\0'; +status = ehci_readl(status_reg); +debug_sprintf(cad,"status 1: %04x\n",status); +strcat(log,cad); +//ehci_writel( portsc,status_reg); +msleep(5); +status &= ~PORT_PE; +status |= PORT_RESET; +debug_sprintf(cad,"ehci_writel 1: %04x\n",status); +strcat(log,cad); +ehci_writel( status,status_reg); + + //ehci_writel( 0x1803,status_reg); + + //msleep(50); + //ehci_writel( 0x1903,status_reg); + //ehci_writel( PORT_OWNER|PORT_POWER|PORT_RESET,status_reg); +#ifdef HOMEBREW + msleep(20); +#else + msleep(500); // wait 500ms for the reset sequence +#endif +status = ehci_readl(status_reg); +debug_sprintf(cad,"status 2: %04x\n",status); +strcat(log,cad); +status &= ~PORT_RESET; +debug_sprintf(cad,"ehci_writel 2: %04x\n",status);strcat(log,cad); +ehci_writel( status,status_reg); + + + //ehci_writel( ehci_readl(status_reg)& (~PORT_RESET),status_reg); + + + retval = handshake(status_reg, status_reg, + PORT_RESET, 0, 750*2); + + if (retval != 0) { + debug_sprintf(cad,"port reset error status: %04x\n",ehci_readl(status_reg)); + strcat(log,cad); + my_sprint(log,NULL); + ehci_dbg ( "port %d reset error %d\n", + port, retval); + return retval; + } + + ehci_dbg ( "port %d reseted status:%04x...\n", port,ehci_readl(status_reg)); +status = ehci_readl(status_reg); + + +if (!(status & PORT_PE)) { + // that means is low speed device so release + status |= PORT_OWNER; + status &= ~PORT_RWC_BITS; + ehci_writel( status, status_reg); + msleep(10); + status = ehci_readl(status_reg); +debug_sprintf(cad,"PORT_PE status: %04x\n",status); +strcat(log,cad); +my_sprint(log,NULL); + return retval; + +} +//my_sprint(log,NULL); +debug_sprintf(cad,"port status2: %04x\n",status); +strcat(log,cad); + +//portsc = ehci_readl(status_reg); +if (status & PORT_RESUME) +{ + ehci_writel(status & ~(PORT_RWC_BITS | PORT_RESUME),status_reg); + (void) ehci_readl(&ehci->regs->command); + status = ehci_readl(status_reg); + ehci_writel(status & ~PORT_RESET,status_reg); + status = ehci_readl(status_reg); +} +/* +portsc &= ~(PORT_RWC_BITS | PORT_RESUME|(3<<10)) ; +ehci_writel( portsc,status_reg); +msleep(50); + debug_sprintf(cad,"%d - %08x ",ehci_readl(status_reg),ehci_readl(status_reg)); + my_sprint("port resume status: ",cad); +*/ +debug_sprintf(cad,"port reseted status: %04x\n",status); +strcat(log,cad); + + +#ifdef HOMEBREW + msleep(400); +#else + msleep(1000); +#endif + +// ehci_control_message(dev,USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_CLASS | USB_CTRLTYPE_REC_INTERFACE, +// 0xFF,0,0,0,0); +/* +ehci_control_message(dev, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_CLASS | USB_CTRLTYPE_REC_INTERFACE), 0xFF, 0, dev->interface, 0, NULL); +USBStorage_Inquiry(dev, 0); +*/ + + // now the device has the default device id +// retval = ehci_control_message(dev,USB_CTRLTYPE_DIR_HOST2DEVICE, +// USB_REQ_GETDESCRIPTOR,USB_DT_DEVICE<<8,0,sizeof(dev->desc),&dev->desc); +// retval = ehci_control_message(dev,0x80, +// USB_REQ_GETDESCRIPTOR,USB_DT_DEVICE<<8,0,sizeof(dev->desc),&dev->desc); + +//void *buf; +//buf = USB_Alloc(0x20); + +// retval = ehci_control_message(dev,0x80, +// 0x6,0x100,0,0x12,&dev->desc); +/* + retval = ehci_control_message(dev,0x80, + 0x6,0x200,0,0x9,buf); + retval = ehci_control_message(dev,0x80, + 0x6,0x200,0,0x20,buf); + retval = ehci_control_message(dev,0x80, + 0x6,0x300,0,0x2,buf); + retval = ehci_control_message(dev,0x80, + 0x6,0x300,0,0x4,buf); + + retval = ehci_control_message(dev,0x80, + USB_REQ_SETCONFIG,1,0,0,0); + retval = ehci_control_message(dev,0x80, + USB_REQ_SETINTERFACE,0,0,0,0); +*/ +//USB_Free(buf); +/* + if (retval < 0) { + debug_sprintf(cad,"unable to get device desc: %04x %04x\n",status,USB_DT_DEVICE<<8);strcat(log,cad); + my_sprint(log,NULL); + ehci_dbg ( "unable to get device desc...\n"); + return retval; + }else + { + debug_sprintf(cad," device desc ok\n");strcat(log,cad); + } +*/ + +// retval = ehci_control_message(dev,USB_CTRLTYPE_DIR_HOST2DEVICE, +// USB_REQ_SETADDRESS,port+1,0,0,0); + retval = ehci_control_message(dev,0x80, + USB_REQ_SETADDRESS,port+1,0,0,0); + if (retval < 0) { + //my_sprint("unable to set device addr",NULL); + debug_sprintf(cad,"unable to set device addr\n");strcat(log,cad); + ehci_dbg ( "unable to set device addr...\n"); + //return retval; + }else + { + debug_sprintf(cad," set device addr ok\n");strcat(log,cad); + } +// retval = ehci_control_message(dev,USB_CTRLTYPE_DIR_DEVICE2HOST, +// USB_REQ_GETDESCRIPTOR,USB_DT_DEVICE<<8,0,sizeof(dev->desc),&dev->desc); + + + dev->toggles = 0; + + dev->id = port+1; + + + debug_sprintf(cad,"device %d: %X %X...\n", dev->id,le16_to_cpu(dev->desc.idVendor),le16_to_cpu(dev->desc.idProduct)); + strcat(log,cad); + debug_sprintf(cad,"device reset ok\n"); + strcat(log,cad); + my_sprint(log,NULL); + + ehci_dbg ( "device %d: %X %X...\n", dev->id,le16_to_cpu(dev->desc.idVendor),le16_to_cpu(dev->desc.idProduct)); + + /* + result = usb_stor_control_msg(us, us->send_ctrl_pipe, + USB_REQ_SET_FEATURE, + USB_TYPE_STANDARD | USB_RECIP_DEVICE, + 0x01, 0x0, NULL, 0x0, 1000); + */ + return retval; +} + +int ehci_reset_port2(int port) +{ +u32 __iomem *status_reg = &ehci->regs->port_status[port]; +int ret=ehci_reset_port(port); +//if(ret==-ENODEV) + { + //msleep(1000); // power off 1 second + msleep(10); // power off 1 second + ehci_writel( 0x1001 ,status_reg); // power on + } +return ret; +} + +int ehci_reset_device(struct ehci_device *dev) +{ + return ehci_reset_port(dev->port); +} +#include "usbstorage.h" +int ehci_discover(void) +{ + int i; + // precondition: the ehci should be halted + for(i = 0;inum_port; i++){ + struct ehci_device *dev = &ehci->devices[i]; + dev->port = i; + ehci_reset_port(i); + } + return 0; +} +/* wii: quickly release non ehci or not connected ports, + as we can't kick OHCI drivers laters if we discover a device for them. +*/ +int ehci_release_ports(void) +{ + int i; + u32 __iomem *status_reg = &ehci->regs->port_status[2]; + while(ehci_readl(&ehci->regs->port_status[2]) == 0x1000) udelay(100);// wait port 2 to init + msleep(100);// wait another msec.. + for(i = 0;inum_port; i++){ + status_reg = &ehci->regs->port_status[i]; + u32 status = ehci_readl(status_reg); + if (i==2 || !(PORT_CONNECT&status) || PORT_USB11(status)) + ehci_writel( PORT_OWNER,status_reg); // release port. + } + return 0; +} + +int ehci_open_device(int vid,int pid,int fd) +{ + int i; + for(i=0;inum_port;i++) + { + //ehci_dbg("try device: %d\n",i); + if(ehci->devices[i].fd == 0 && + le16_to_cpu(ehci->devices[i].desc.idVendor) == vid && + le16_to_cpu(ehci->devices[i].desc.idProduct) == pid) + { + //ehci_dbg("found device: %x %x\n",vid,pid); + ehci->devices[i].fd = fd; + return fd; + } + } + return -6; +} +int ehci_close_device(struct ehci_device *dev) +{ + if (dev) + dev->fd = 0; + return 0; +} + void * ehci_fd_to_dev(int fd) +{ + int i; + for(i=0;inum_port;i++) + { + struct ehci_device *dev = &ehci->devices[i]; + //ehci_dbg ( "device %d:fd:%d %X %X...\n", dev->id,dev->fd,le16_to_cpu(dev->desc.idVendor),le16_to_cpu(dev->desc.idProduct)); + if(dev->fd == fd){ + return dev; + } + } + ehci_dbg("unkown fd! %d\n",fd); + return 0; +} +#define g_ehci #error +int ehci_get_device_list(u8 maxdev,u8 b0,u8*num,u16*buf) +{ + int i,j = 0; + for(i=0;inum_port && jdevices[i]; + if(dev->id != 0){ + //ehci_dbg ( "device %d: %X %X...\n", dev->id,le16_to_cpu(dev->desc.idVendor),le16_to_cpu(dev->desc.idProduct)); + buf[j*4] = 0; + buf[j*4+1] = 0; + buf[j*4+2] = le16_to_cpu(dev->desc.idVendor); + buf[j*4+3] = le16_to_cpu(dev->desc.idProduct); + j++; + } + } + //ehci_dbg("found %d devices\n",j); + *num = j; + return 0; +} +#include "usb.c" +#include "usbstorage.c" diff --git a/tinyehci/ehci.c.old2 b/tinyehci/ehci.c.old2 new file mode 100644 index 00000000..41e5017e --- /dev/null +++ b/tinyehci/ehci.c.old2 @@ -0,0 +1,862 @@ +void my_sprint(char *cad, char *s); +/* simplest usb-ehci driver which features: + + control and bulk transfers only + only one transfer pending + driver is synchronous (waiting for the end of the transfer) + endianess independant + no uncached memory allocation needed + + this driver is originally based on the GPL linux ehci-hcd driver + + * Original Copyright (c) 2001 by David Brownell + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* magic numbers that can affect system performance */ +#define EHCI_TUNE_CERR 0 /* 0-3 qtd retries; 0 == don't stop */ /* by Hermes: i have replaced 3 by 0 and now it donīt hang when i extract the device*/ +#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ +#define EHCI_TUNE_RL_TT 0 +#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ +#define EHCI_TUNE_MULT_TT 1 +#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ +extern int verbose; +#ifdef DEBUG +static int num_command_before_no_verbose = 100; +#endif +static void +dbg_qtd (const char *label, struct ehci_qtd *qtd) +{ + ehci_dbg( "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, + hc32_to_cpup( &qtd->hw_next), + hc32_to_cpup( &qtd->hw_alt_next), + hc32_to_cpup( &qtd->hw_token), + hc32_to_cpup( &qtd->hw_buf [0])); + if (qtd->hw_buf [1]) + ehci_dbg( " p1=%08x p2=%08x p3=%08x p4=%08x\n", + hc32_to_cpup( &qtd->hw_buf[1]), + hc32_to_cpup( &qtd->hw_buf[2]), + hc32_to_cpup( &qtd->hw_buf[3]), + hc32_to_cpup( &qtd->hw_buf[4])); +} + +static void +dbg_qh (const char *label, struct ehci_qh *qh) +{ + ehci_dbg ( "%s qh %p n%08x info %x %x qtd %x\n", label, + qh, + hc32_to_cpu(qh->hw_next), + hc32_to_cpu(qh->hw_info1), + hc32_to_cpu(qh->hw_info2), + hc32_to_cpu(qh->hw_current)); + dbg_qtd ("overlay", (struct ehci_qtd *) &qh->hw_qtd_next); +} + +static void +dbg_command (void) +{ +#ifdef DEBUG + u32 command=ehci_readl( &ehci->regs->command); + u32 async=ehci_readl( &ehci->regs->async_next); + + ehci_dbg ("async_next: %08x\n",async); + ehci_dbg ( + "command %06x %s=%d ithresh=%d%s%s%s%s %s %s\n", + command, + (command & CMD_PARK) ? "park" : "(park)", + CMD_PARK_CNT (command), + (command >> 16) & 0x3f, + (command & CMD_LRESET) ? " LReset" : "", + (command & CMD_IAAD) ? " IAAD" : "", + (command & CMD_ASE) ? " Async" : "", + (command & CMD_PSE) ? " Periodic" : "", + (command & CMD_RESET) ? " Reset" : "", + (command & CMD_RUN) ? "RUN" : "HALT" + ); +#endif +} +static void +dbg_status (void) +{ +#ifdef DEBUG + u32 status=ehci_readl( &ehci->regs->status); + ehci_dbg ( + "status %04x%s%s%s%s%s%s%s%s%s%s\n", + status, + (status & STS_ASS) ? " Async" : "", + (status & STS_PSS) ? " Periodic" : "", + (status & STS_RECL) ? " Recl" : "", + (status & STS_HALT) ? " Halt" : "", + (status & STS_IAA) ? " IAA" : "", + (status & STS_FATAL) ? " FATAL" : "", + (status & STS_FLR) ? " FLR" : "", + (status & STS_PCD) ? " PCD" : "", + (status & STS_ERR) ? " ERR" : "", + (status & STS_INT) ? " INT" : "" + ); +#endif +} + +void debug_qtds(void) +{ + struct ehci_qh *qh = ehci->async; + struct ehci_qtd *qtd; + dbg_qh ("qh",qh); + dbg_command (); + dbg_status (); + for(qtd = qh->qtd_head; qtd; qtd = qtd->next) + { + ehci_dma_unmap_bidir(qtd->qtd_dma,sizeof(struct ehci_qtd)); + dbg_qtd("qtd",qtd); + ehci_dma_map_bidir(qtd,sizeof(struct ehci_qtd)); + } + +} +void dump_qh(struct ehci_qh *qh) +{ + struct ehci_qtd *qtd; + dbg_command (); + dbg_status (); + ehci_dma_unmap_bidir(qh->qh_dma,sizeof(struct ehci_qh)); + dbg_qh("qh",qh); + print_hex_dump_bytes("qh:",DUMP_PREFIX_OFFSET,(void*)qh,12*4); + for(qtd = qh->qtd_head; qtd; qtd = qtd->next){ + u32 *buf; + ehci_dma_unmap_bidir(qtd->qtd_dma,sizeof(struct ehci_qtd)); + dbg_qtd("qtd",qtd); + print_hex_dump_bytes("qtd:",DUMP_PREFIX_OFFSET,(void*)qtd,8*4); + buf = (u32*)hc32_to_cpu(qtd->hw_buf[0]); + if(buf) + print_hex_dump_bytes("qtd buf:",DUMP_PREFIX_OFFSET,(void*)(buf),8*4); + + } +} + +/*-------------------------------------------------------------------------*/ + +/* + * handshake - spin reading hc until handshake completes or fails + * @ptr: address of hc register to be read + * @mask: bits to look at in result of read + * @done: value of those bits when handshake succeeds + * @usec: timeout in microseconds + * + * Returns negative errno, or zero on success + * + * Success happens when the "mask" bits have the specified value (hardware + * handshake done). There are two failure modes: "usec" have passed (major + * hardware flakeout), or the register reads as all-ones (hardware removed). + * + * That last failure should_only happen in cases like physical cardbus eject + * before driver shutdown. But it also seems to be caused by bugs in cardbus + * bridge shutdown: shutting down the bridge before the devices using it. + */ +int unplug_device=0; + + +static int handshake (void __iomem *pstatus, void __iomem *ptr, + u32 mask, u32 done, int usec) +{ + u32 result,status; + + + do { + status = ehci_readl( pstatus); + result = ehci_readl( ptr); + if ((result == ~(u32)0) || (PORT_OWNER&status) || !(PORT_CONNECT&status)) /* card removed */ + {unplug_device=1; + return -ENODEV; + } + result &= mask; + + if (result == done) + { +#ifdef DEBUG + if(num_command_before_no_verbose) + { + num_command_before_no_verbose--; + if(num_command_before_no_verbose==0) + verbose=0; + } +#endif + return 0; + } + udelay (100); /* Hermes: 100 microseconds is a god time to response and better for multithread (i think). + the new timer uses syscalls and queues to release the thread focus */ + usec-=100; + } while (usec > 0); +#ifdef DEBUG + verbose = 1; + num_command_before_no_verbose=100; +#endif + ehci_dbg("\nhandshake timeout!!\n\n"); + //dump_qh(ehci->async); + //dump_qh(ehci->asyncqh); + //BUG(); + + unplug_device=1; +#ifndef HOMEBREW + return -ENODEV; /* Hermes: with ENODEV works the unplugin method receiving datas (fatal error) + ENODEV return without retries and unplug_device can works without interferences. + i think is no a good idea too much retries when is possible the device needs one drastic action + */ +#endif + return -ETIMEDOUT; +} + +#include "ehci-mem.c" +/* one-time init, only for memory state */ +static int ehci_init(void) +{ + int retval; + if ((retval = ehci_mem_init()) < 0) + return retval; + /* + * dedicate a qh for the async ring head, since we couldn't unlink + * a 'real' qh without stopping the async schedule [4.8]. use it + * as the 'reclamation list head' too. + * its dummy is used in hw_alt_next of many tds, to prevent the qh + * from automatically advancing to the next td after short reads. + */ + ehci->async->hw_next = QH_NEXT( ehci->async->qh_dma); + ehci->async->hw_info1 = cpu_to_hc32( QH_HEAD); + ehci->async->hw_token = cpu_to_hc32( QTD_STS_HALT); + ehci->async->hw_qtd_next = EHCI_LIST_END(); + ehci->async->hw_alt_next = EHCI_LIST_END();//QTD_NEXT( ehci->async->dummy->qtd_dma); + ehci->ctrl_buffer = USB_Alloc(sizeof(usbctrlrequest)); + ehci->command = 0; + + ehci_writel( 0x008000002, &ehci->regs->command); + ehci_writel( ehci->periodic_dma, &ehci->regs->frame_list); + ehci_writel( ehci->async->qh_dma, &ehci->regs->async_next); + ehci_writel( 0x00010009, &ehci->regs->command); + ehci_writel( 1, &ehci->regs->configured_flag); + ehci_writel( 0x00010029, &ehci->regs->command); + + + return 0; +} + +/* fill a qtd, returning how much of the buffer we were able to queue up */ +static int +qtd_fill(struct ehci_qtd *qtd, dma_addr_t buf, + size_t len, int token, int maxpacket) +{ + int i, count; + u64 addr = buf; + //ehci_dbg("fill qtd with dma %X len %X\n",buf,len); + /* one buffer entry per 4K ... first might be short or unaligned */ + qtd->hw_buf[0] = cpu_to_hc32( (u32)addr); + qtd->hw_buf_hi[0] = 0; + count = 0x1000 - (buf & 0x0fff); /* rest of that page */ + if (likely (len < count)) /* ... iff needed */ + count = len; + else { + buf += 0x1000; + buf &= ~0x0fff; + + /* per-qtd limit: from 16K to 20K (best alignment) */ + for (i = 1; count < len && i < 5; i++) { + addr = buf; + qtd->hw_buf[i] = cpu_to_hc32( (u32)addr); + qtd->hw_buf_hi[i] = cpu_to_hc32( + (u32)(addr >> 32)); + buf += 0x1000; + if ((count + 0x1000) < len) + count += 0x1000; + else + count = len; + } + + /* short packets may only terminate transfers */ + if (count != len) + count -= (count % maxpacket); + } + qtd->hw_token = cpu_to_hc32( (count << 16) | token); + qtd->length = count; + + return count; +} + +// high bandwidth multiplier, as encoded in highspeed endpoint descriptors +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) +// ... and packet size, for any kind of endpoint descriptor +#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) + +/* + * reverse of qh_urb_transaction: free a list of TDs. + * also count the actual transfer length. + * + */ +static void qh_end_transfer (void) +{ + struct ehci_qtd *qtd; + struct ehci_qh *qh = ehci->asyncqh; + u32 token; + int error = 0; + for(qtd = qh->qtd_head; qtd; qtd = qtd->next){ + token = hc32_to_cpu( qtd->hw_token); + if (likely (QTD_PID (token) != 2)) + qtd->urb->actual_length += qtd->length - QTD_LENGTH (token); + if (!(qtd->length ==0 && ((token & 0xff)==QTD_STS_HALT)) && + (token & QTD_STS_HALT)) { + ehci_dbg("\nqtd error!:"); + if (token & QTD_STS_BABBLE) { + ehci_dbg(" BABBLE"); + } + if (token & QTD_STS_MMF) { + /* fs/ls interrupt xfer missed the complete-split */ + ehci_dbg(" missed micro frame"); + } + if (token & QTD_STS_DBE) { + ehci_dbg(" databuffer error"); + } + if (token & QTD_STS_XACT) { + ehci_dbg(" wrong ack"); + } + if (QTD_CERR (token)==0) + ehci_dbg(" toomany errors"); + ehci_dbg("\n"); + error = -1; + } + } + if(error){ +#ifdef DEBUG + verbose = 1; + num_command_before_no_verbose=100; +#endif + //dump_qh(ehci->asyncqh); + qtd->urb->actual_length = error; + } + ehci->qtd_used = 0; +} + +/* + * create a list of filled qtds for this URB; won't link into qh. + */ +struct ehci_qtd *qh_urb_transaction ( + struct ehci_urb *urb +) { + struct ehci_qtd *qtd, *qtd_prev; + struct ehci_qtd *head; + dma_addr_t buf; + int len, maxpacket; + int is_input; + u32 token; + + /* + * URBs map to sequences of QTDs: one logical transaction + */ + head = qtd = ehci_qtd_alloc (); + qtd->urb = urb; + + urb->actual_length = 0; + token = QTD_STS_ACTIVE; + token |= (EHCI_TUNE_CERR << 10); + /* for split transactions, SplitXState initialized to zero */ + + len = urb->transfer_buffer_length; + is_input = urb->input; + if (urb->ep==0) {/* is control */ + /* SETUP pid */ + qtd_fill( qtd, urb->setup_dma, + sizeof (usbctrlrequest), + token | (2 /* "setup" */ << 8), 8); + + /* ... and always at least one more pid */ + token ^= QTD_TOGGLE; + qtd_prev = qtd; + qtd = ehci_qtd_alloc (); + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT( qtd->qtd_dma); + qtd_prev->next = qtd; + + /* for zero length DATA stages, STATUS is always IN */ + if (len == 0) + token |= (1 /* "in" */ << 8); + } + + /* + * data transfer stage: buffer setup + */ + buf = urb->transfer_dma; + + if (is_input) + token |= (1 /* "in" */ << 8); + /* else it's already initted to "out" pid (0 << 8) */ + + maxpacket = max_packet(urb->maxpacket); + + /* + * buffer gets wrapped in one or more qtds; + * last one may be "short" (including zero len) + * and may serve as a control status ack + */ + for (;;) { + int this_qtd_len; + + this_qtd_len = qtd_fill( qtd, buf, len, token, maxpacket); + len -= this_qtd_len; + buf += this_qtd_len; + + /* + * short reads advance to a "magic" dummy instead of the next + * qtd ... that forces the queue to stop, for manual cleanup. + * (this will usually be overridden later.) + */ + if (is_input) + qtd->hw_alt_next = ehci->asyncqh->hw_alt_next; + + /* qh makes control packets use qtd toggle; maybe switch it */ + if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) + token ^= QTD_TOGGLE; + + if (likely (len <= 0)) + break; + + qtd_prev = qtd; + qtd = ehci_qtd_alloc (); + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT( qtd->qtd_dma); + qtd_prev->next = qtd; + } + + qtd->hw_alt_next = EHCI_LIST_END(); + + /* + * control requests may need a terminating data "status" ack; + * bulk ones may need a terminating short packet (zero length). + */ + if (likely (urb->transfer_buffer_length != 0)) { + int one_more = 0; + + if (urb->ep==0) { + one_more = 1; + token ^= 0x0100; /* "in" <--> "out" */ + token |= QTD_TOGGLE; /* force DATA1 */ + } else if(!(urb->transfer_buffer_length % maxpacket)) { + //one_more = 1; + } + if (one_more) { + qtd_prev = qtd; + qtd = ehci_qtd_alloc (); + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT( qtd->qtd_dma); + qtd_prev->next = qtd; + + /* never any data in such packets */ + qtd_fill( qtd, 0, 0, token, 0); + } + } + + /* by default, enable interrupt on urb completion */ + qtd->hw_token |= cpu_to_hc32( QTD_IOC); + return head; +} + +u32 usb_timeout=2000*1000; + int ehci_do_urb ( + struct ehci_device *dev, + struct ehci_urb *urb) +{ + struct ehci_qh *qh; + struct ehci_qtd *qtd; + u32 info1 = 0, info2 = 0; + int is_input; + int maxp = 0; + int retval=0; + + //ehci_dbg ("do urb %X %X ep %X\n",urb->setup_buffer,urb->transfer_buffer,urb->ep); + if(urb->ep==0) //control message + urb->setup_dma = ehci_dma_map_to(urb->setup_buffer,sizeof (usbctrlrequest)); + + if(urb->transfer_buffer_length){ + if(urb->input) + urb->transfer_dma = ehci_dma_map_to(urb->transfer_buffer,urb->transfer_buffer_length); + else + urb->transfer_dma = ehci_dma_map_from(urb->transfer_buffer,urb->transfer_buffer_length); + } + qh = ehci->asyncqh; + memset(qh,0,12*4); + qtd = qh_urb_transaction ( urb); + qh ->qtd_head = qtd; + + + info1 |= ((urb->ep)&0xf)<<8; + info1 |= dev->id; + is_input = urb->input; + maxp = urb->maxpacket; + + info1 |= (2 << 12); /* EPS "high" */ + if(urb->ep==0)// control + { + info1 |= (EHCI_TUNE_RL_HS << 28); + info1 |= 64 << 16; /* usb2 fixed maxpacket */ + info1 |= 1 << 14; /* toggle from qtd */ + info2 |= (EHCI_TUNE_MULT_HS << 30); + }else//bulk + { + info1 |= (EHCI_TUNE_RL_HS << 28); + /* The USB spec says that high speed bulk endpoints + * always use 512 byte maxpacket. But some device + * vendors decided to ignore that, and MSFT is happy + * to help them do so. So now people expect to use + * such nonconformant devices with Linux too; sigh. + */ + info1 |= max_packet(maxp) << 16; + info2 |= (EHCI_TUNE_MULT_HS << 30); + + } + //ehci_dbg("HW info: %08X\n",info1); + qh->hw_info1 = cpu_to_hc32( info1); + qh->hw_info2 = cpu_to_hc32( info2); + + qh->hw_next = QH_NEXT(qh->qh_dma); + qh->hw_qtd_next = QTD_NEXT( qtd->qtd_dma); + qh->hw_alt_next = EHCI_LIST_END(); + + if(urb->ep!=0){ + if(get_toggle(dev,urb->ep)) + qh->hw_token |= cpu_to_hc32(QTD_TOGGLE); + else + qh->hw_token &= ~cpu_to_hc32( QTD_TOGGLE); + //ehci_dbg("toggle for ep %x: %d %x\n",urb->ep,get_toggle(dev,urb->ep),qh->hw_token); + } + + qh->hw_token &= cpu_to_hc32( QTD_TOGGLE | QTD_STS_PING); + + qh->hw_next = QH_NEXT(ehci->async->qh_dma); + + + ehci_dma_map_bidir(qh,sizeof(struct ehci_qh)); + for(qtd = qh->qtd_head; qtd; qtd = qtd->next) + ehci_dma_map_bidir(qtd,sizeof(struct ehci_qtd)); +#if 0 + if(urb->ep!=0){ + dump_qh(ehci->async); + dump_qh(ehci->asyncqh); + } +#endif + // start (link qh) + ehci->async->hw_next = QH_NEXT(qh->qh_dma); + ehci_dma_map_bidir(ehci->async,sizeof(struct ehci_qh)); + + retval = handshake(&ehci->regs->port_status[dev->port],&ehci->regs->status,STS_INT,STS_INT,usb_timeout); + + //print_hex_dump_bytes ("qh mem",0,(void*)qh,17*4); + //retval = poll_transfer_end(1000*1000); + ehci_dma_unmap_bidir(ehci->async->qh_dma,sizeof(struct ehci_qh)); + ehci_dma_unmap_bidir(qh->qh_dma,sizeof(struct ehci_qh)); + for(qtd = qh->qtd_head; qtd; qtd = qtd->next) + ehci_dma_unmap_bidir(qtd->qtd_dma,sizeof(struct ehci_qtd)); + + // stop (unlink qh) + ehci->async->hw_next = QH_NEXT(ehci->async->qh_dma); + ehci_dma_map_bidir(ehci->async,sizeof(struct ehci_qh)); + ehci_dma_unmap_bidir(ehci->async->qh_dma,sizeof(struct ehci_qh)); + + // ack + ehci_writel( STS_RECL|STS_IAA|STS_INT, &ehci->regs->status); + + if(urb->ep!=0){ + set_toggle(dev,urb->ep,(qh->hw_token & cpu_to_hc32(QTD_TOGGLE))?1:0); + //ehci_dbg("toggle for ep %x: %d %d %x %X\n",urb->ep,get_toggle(dev,urb->ep),(qh->hw_token & cpu_to_hc32(QTD_TOGGLE)),qh->hw_token,dev->toggles); + } + + if(retval>=0) + // wait hc really stopped + retval = handshake(&ehci->regs->port_status[dev->port],&ehci->regs->async_next,~0,ehci->async->qh_dma,20*1000); + //release memory, and actualise urb->actual_length + qh_end_transfer(); + + + if(urb->transfer_buffer_length){ + if(urb->input) + ehci_dma_unmap_to(urb->transfer_dma,urb->transfer_buffer_length); + else + ehci_dma_unmap_from(urb->transfer_dma,urb->transfer_buffer_length); + } + if(urb->ep==0) //control message + ehci_dma_unmap_to(urb->setup_dma,sizeof (usbctrlrequest)); + if(retval==0){ + + return urb->actual_length; + + ehci_dbg ( "un successfull urb %d!!\n", retval); + } + return retval; +} + +s32 ehci_control_message(struct ehci_device *dev,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u32 wLength,void *buf) +{ + struct ehci_urb urb; + usbctrlrequest *req = ehci->ctrl_buffer; + if(verbose) + ehci_dbg ( "control msg: rt%02X r%02X v%04X i%04X s%04x %p\n", bmRequestType, bmRequest, wValue, wIndex,wLength,buf); + req->bRequestType = bmRequestType; + req->bRequest = bmRequest; + req->wValue = swab16(wValue); + req->wIndex = swab16(wIndex); + req->wLength = swab16(wLength); + urb.setup_buffer = req; + urb.ep = 0; + urb.input = (bmRequestType&USB_CTRLTYPE_DIR_DEVICE2HOST)!=0; + urb.maxpacket = 64; + urb.transfer_buffer_length = wLength; + if (((u32)buf) > 0x13880000){// HW cannot access this buffer, we allow this for convenience + int ret; + urb.transfer_buffer = USB_Alloc(wLength); + if (verbose) + ehci_dbg("alloc another buffer %p %p\n",buf,urb.transfer_buffer); + memcpy(urb.transfer_buffer,buf,wLength); + ret = ehci_do_urb(dev,&urb); + memcpy(buf,urb.transfer_buffer,wLength); + USB_Free(urb.transfer_buffer); + return ret; + } + else{ + urb.transfer_buffer = buf; + return ehci_do_urb(dev,&urb); + } +} +s32 ehci_bulk_message(struct ehci_device *dev,u8 bEndpoint,u32 wLength,void *rpData) +{ + struct ehci_urb urb; + s32 ret; + urb.setup_buffer = NULL; + urb.ep = bEndpoint; + urb.input = (bEndpoint&0x80)!=0; + urb.maxpacket = 512; + urb.maxpacket = 64; + urb.transfer_buffer_length = wLength; + urb.transfer_buffer = rpData; + if(verbose) + ehci_dbg ( "bulk msg: ep:%02X size:%02X addr:%04X", bEndpoint, wLength, rpData); + ret= ehci_do_urb(dev,&urb); + if(verbose) + ehci_dbg ( "==>%d\n", ret); + return ret; +} + +int ehci_reset_port(int port) +{ + u32 __iomem *status_reg = &ehci->regs->port_status[port]; + struct ehci_device *dev = &ehci->devices[port]; + u32 status = ehci_readl(status_reg); + int retval = 0; + dev->id = 0; + if ((PORT_OWNER&status) || !(PORT_CONNECT&status)) + { + ehci_writel( PORT_OWNER,status_reg); + ehci_dbg ( "port %d had no usb2 device connected at startup %X \n", port,ehci_readl(status_reg)); + return -ENODEV;// no USB2 device connected + } + char cad[128]; + char log[1024]; + ehci_dbg ( "port %d has usb2 device connected! reset it...\n", port); + debug_sprintf(cad," status1: %04X\n",status);strcat(log,cad); + status &= ~PORT_PE; + status |= PORT_RESET; + ehci_writel( status,status_reg); + msleep(50);// wait 50ms for the reset sequence + status=ehci_readl(status_reg); + debug_sprintf(cad," status2: %04X\n",status);strcat(log,cad); + status &= ~PORT_RESET; + ehci_writel( status,status_reg); + debug_sprintf(cad," status3: %04X\n",ehci_readl(status_reg));strcat(log,cad); + //ehci_writel( ehci_readl(status_reg)& (~PORT_RESET),status_reg); + retval = handshake(status_reg, status_reg, + PORT_RESET, 0, 750); + if (retval != 0) { + ehci_dbg ( "port %d reset error %d\n", + port, retval); + return retval; + } + status=ehci_readl(status_reg); + ehci_dbg ( "port %d reseted status:%04x...\n", port,status); + + debug_sprintf(cad," port reseted status: %04x\n",status);strcat(log,cad); + + if (!(status & PORT_PE)) { + // that means is low speed device so release + status |= PORT_OWNER; + status &= ~PORT_RWC_BITS; + ehci_writel( status, status_reg); + msleep(10); + status = ehci_readl(status_reg); + debug_sprintf(cad,"PORT_PE, release USB11 status: %04x\n",status); strcat(log,cad); + my_sprint(log,NULL); + return retval; + } + my_sprint(log,NULL); + +msleep(500); + // now the device has the default device id + retval = ehci_control_message(dev,USB_CTRLTYPE_DIR_DEVICE2HOST, + USB_REQ_GETDESCRIPTOR,USB_DT_DEVICE<<8,0,sizeof(dev->desc),&dev->desc); + + if (retval < 0) { + ehci_dbg ( "unable to get device desc...\n"); + return retval; + } + + retval = ehci_control_message(dev,USB_CTRLTYPE_DIR_HOST2DEVICE, + USB_REQ_SETADDRESS,port+1,0,0,0); + if (retval < 0) { + ehci_dbg ( "unable to set device addr...\n"); + return retval; + } + dev->toggles = 0; + + dev->id = port+1; + ehci_dbg ( "device %d: %X %X...\n", dev->id,le16_to_cpu(dev->desc.idVendor),le16_to_cpu(dev->desc.idProduct)); + return retval; + +} +/* +int init_port(int port) +{ + int retval=0; + struct ehci_device *dev = &ehci->devices[port]; + dev->id = 0; + + // now the device has the default device id + retval = ehci_control_message(dev,USB_CTRLTYPE_DIR_DEVICE2HOST, + USB_REQ_GETDESCRIPTOR,USB_DT_DEVICE<<8,0,sizeof(dev->desc),&dev->desc); + + if (retval < 0) { + ehci_dbg ( "unable to get device desc...\n"); + return retval; + } + + retval = ehci_control_message(dev,USB_CTRLTYPE_DIR_HOST2DEVICE, + USB_REQ_SETADDRESS,port+1,0,0,0); + if (retval < 0) { + ehci_dbg ( "unable to set device addr...\n"); + return retval; + } + dev->toggles = 0; + + dev->id = port+1; + ehci_dbg ( "device %d: %X %X...\n", dev->id,le16_to_cpu(dev->desc.idVendor),le16_to_cpu(dev->desc.idProduct)); + return retval; +} +*/ +int ehci_reset_port2(int port) +{ +u32 __iomem *status_reg = &ehci->regs->port_status[port]; +int ret=ehci_reset_port(port); +if(ret==-ENODEV) + { + msleep(2000); // power off 2 second + ehci_writel( 0x1001 ,status_reg); // power on + } +return ret; +} + +int ehci_reset_device(struct ehci_device *dev) +{ + return ehci_reset_port(dev->port); +} +#include "usbstorage.h" +int ehci_discover(void) +{ + int i; + // precondition: the ehci should be halted + for(i = 0;inum_port; i++){ + struct ehci_device *dev = &ehci->devices[i]; + dev->port = i; + ehci_reset_port(i); + } + return 0; +} +/* wii: quickly release non ehci or not connected ports, + as we can't kick OHCI drivers laters if we discover a device for them. +*/ +int ehci_release_ports(void) +{ + int i; + u32 __iomem *status_reg = &ehci->regs->port_status[2]; + while(ehci_readl(&ehci->regs->port_status[2]) == 0x1000) udelay(100);// wait port 2 to init + msleep(100);// wait another msec.. + for(i = 0;inum_port; i++){ + status_reg = &ehci->regs->port_status[i]; + u32 status = ehci_readl(status_reg); + if (i==2 || !(PORT_CONNECT&status) || PORT_USB11(status)) + ehci_writel( PORT_OWNER,status_reg); // release port. + } + return 0; +} + +int ehci_open_device(int vid,int pid,int fd) +{ + int i; + for(i=0;inum_port;i++) + { + //ehci_dbg("try device: %d\n",i); + if(ehci->devices[i].fd == 0 && + le16_to_cpu(ehci->devices[i].desc.idVendor) == vid && + le16_to_cpu(ehci->devices[i].desc.idProduct) == pid) + { + //ehci_dbg("found device: %x %x\n",vid,pid); + ehci->devices[i].fd = fd; + return fd; + } + } + return -6; +} +int ehci_close_device(struct ehci_device *dev) +{ + if (dev) + dev->fd = 0; + return 0; +} + void * ehci_fd_to_dev(int fd) +{ + int i; + for(i=0;inum_port;i++) + { + struct ehci_device *dev = &ehci->devices[i]; + //ehci_dbg ( "device %d:fd:%d %X %X...\n", dev->id,dev->fd,le16_to_cpu(dev->desc.idVendor),le16_to_cpu(dev->desc.idProduct)); + if(dev->fd == fd){ + return dev; + } + } + ehci_dbg("unkown fd! %d\n",fd); + return 0; +} +#define g_ehci #error +int ehci_get_device_list(u8 maxdev,u8 b0,u8*num,u16*buf) +{ + int i,j = 0; + for(i=0;inum_port && jdevices[i]; + if(dev->id != 0){ + //ehci_dbg ( "device %d: %X %X...\n", dev->id,le16_to_cpu(dev->desc.idVendor),le16_to_cpu(dev->desc.idProduct)); + buf[j*4] = 0; + buf[j*4+1] = 0; + buf[j*4+2] = le16_to_cpu(dev->desc.idVendor); + buf[j*4+3] = le16_to_cpu(dev->desc.idProduct); + j++; + } + } + //ehci_dbg("found %d devices\n",j); + *num = j; + return 0; +} +#include "usb.c" +#include "usbstorage.c" diff --git a/tinyehci/ehci1.c b/tinyehci/ehci1.c new file mode 100644 index 00000000..91827fdf --- /dev/null +++ b/tinyehci/ehci1.c @@ -0,0 +1,1538 @@ +/* simplest usb-ehci driver which features: + + control and bulk transfers only + only one transfer pending + driver is synchronous (waiting for the end of the transfer) + endianess independant + no uncached memory allocation needed + + this driver is originally based on the GPL linux ehci-hcd driver + +* Original Copyright (c) 2001 by David Brownell +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + + +/* magic numbers that can affect system performance */ +#define EHCI_TUNE_CERR 0 /* 0-3 qtd retries; 0 == don't stop */ /* by Hermes: i have replaced 3 by 0 and now it donīt hang when i extract the device*/ +#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ +#define EHCI_TUNE_RL_TT 0 +#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ +#define EHCI_TUNE_MULT_TT 1 +#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ + +extern int verbose; +#ifdef DEBUG +static int num_command_before_no_verbose = 100; +#endif +static void + +dbg_qtd (const char *label, struct ehci_qtd *qtd) +{ + ehci_dbg( "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, + hc32_to_cpup( &qtd->hw_next), + hc32_to_cpup( &qtd->hw_alt_next), + hc32_to_cpup( &qtd->hw_token), + hc32_to_cpup( &qtd->hw_buf [0])); + + if (qtd->hw_buf [1]) + ehci_dbg( " p1=%08x p2=%08x p3=%08x p4=%08x\n", + hc32_to_cpup( &qtd->hw_buf[1]), + hc32_to_cpup( &qtd->hw_buf[2]), + hc32_to_cpup( &qtd->hw_buf[3]), + hc32_to_cpup( &qtd->hw_buf[4])); +} + +static void + +dbg_qh (const char *label, struct ehci_qh *qh) +{ + ehci_dbg ( "%s qh %p n%08x info %x %x qtd %x\n", label, + qh, + hc32_to_cpu(qh->hw_next), + hc32_to_cpu(qh->hw_info1), + hc32_to_cpu(qh->hw_info2), + hc32_to_cpu(qh->hw_current)); + + dbg_qtd ("overlay", (struct ehci_qtd *) &qh->hw_qtd_next); +} + +static void +dbg_command (void) +{ +#ifdef DEBUG + u32 command = ehci_readl( &ehci->regs->command); + u32 async = ehci_readl( &ehci->regs->async_next); + + ehci_dbg ("async_next: %08x\n", async); + ehci_dbg ( + "command %06x %s=%d ithresh=%d%s%s%s%s %s %s\n", + command, + (command & CMD_PARK) ? "park" : "(park)", + CMD_PARK_CNT (command), + (command >> 16) & 0x3f, + (command & CMD_LRESET) ? " LReset" : "", + (command & CMD_IAAD) ? " IAAD" : "", + (command & CMD_ASE) ? " Async" : "", + (command & CMD_PSE) ? " Periodic" : "", + (command & CMD_RESET) ? " Reset" : "", + (command & CMD_RUN) ? "RUN" : "HALT" + ); +#endif +} + +static void +dbg_status (void) +{ +#ifdef DEBUG + u32 status = ehci_readl( &ehci->regs->status); + ehci_dbg ( + "status %04x%s%s%s%s%s%s%s%s%s%s\n", + status, + (status & STS_ASS) ? " Async" : "", + (status & STS_PSS) ? " Periodic" : "", + (status & STS_RECL) ? " Recl" : "", + (status & STS_HALT) ? " Halt" : "", + (status & STS_IAA) ? " IAA" : "", + (status & STS_FATAL) ? " FATAL" : "", + (status & STS_FLR) ? " FLR" : "", + (status & STS_PCD) ? " PCD" : "", + (status & STS_ERR) ? " ERR" : "", + (status & STS_INT) ? " INT" : "" + ); +#endif +} + +void debug_qtds(void) +{ + + struct ehci_qh *qh = ehci->async; + + struct ehci_qtd *qtd; + dbg_qh ("qh", qh); + dbg_command (); + dbg_status (); + + for (qtd = qh->qtd_head; qtd; qtd = qtd->next) + { + + ehci_dma_unmap_bidir(qtd->qtd_dma, sizeof(struct ehci_qtd)); + dbg_qtd("qtd", qtd); + + ehci_dma_map_bidir(qtd, sizeof(struct ehci_qtd)); + } + +} + +void dump_qh(struct ehci_qh *qh) +{ + + struct ehci_qtd *qtd; + dbg_command (); + dbg_status (); + + ehci_dma_unmap_bidir(qh->qh_dma, sizeof(struct ehci_qh)); + dbg_qh("qh", qh); + print_hex_dump_bytes("qh:", DUMP_PREFIX_OFFSET, (void*)qh, 12*4); + + for (qtd = qh->qtd_head; qtd; qtd = qtd->next) + { + u32 * buf; + + ehci_dma_unmap_bidir(qtd->qtd_dma, sizeof(struct ehci_qtd)); + dbg_qtd("qtd", qtd); + print_hex_dump_bytes("qtd:", DUMP_PREFIX_OFFSET, (void*)qtd, 8*4); + buf = (u32*)hc32_to_cpu(qtd->hw_buf[0]); + + if (buf) + print_hex_dump_bytes("qtd buf:", DUMP_PREFIX_OFFSET, (void*)(buf), 8*4); + + } +} + +/*-------------------------------------------------------------------------*/ + +/* + * handshake - spin reading hc until handshake completes or fails + * @ptr: address of hc register to be read + * @mask: bits to look at in result of read + * @done: value of those bits when handshake succeeds + * @usec: timeout in microseconds + * + * Returns negative errno, or zero on success + * + * Success happens when the "mask" bits have the specified value (hardware + * handshake done). There are two failure modes: "usec" have passed (major + * hardware flakeout), or the register reads as all-ones (hardware removed). + * + * That last failure should_only happen in cases like physical cardbus eject + * before driver shutdown. But it also seems to be caused by bugs in cardbus + * bridge shutdown: shutting down the bridge before the devices using it. + */ +int unplug_device = 0; + +#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT | STS_FLR) + + + +int handshake_mode = 0; // Modes: 0-> Transfer 1-> initialize 2->Test + +int handshake_us = 0; + +#define get_timer() (*(((volatile u32*)0x0D800010))) + +void ehci_mdelay(int msec); + +static int handshake (void __iomem *pstatus, void __iomem *ptr, + u32 mask, u32 done, int usec , u32 or_flags) +{ + u32 result, status, g_status, g_mstatus, ret = 0; + + u32 tmr, diff; + + handshake_us = 0; + tmr = get_timer(); + usec <<= 1; + + do + { + + ehci_usleep (125); /* Hermes: 125 microseconds is a good time to response and better for multithread (i think). + the new timer uses syscalls and queues to release the thread focus */ + //usec-=100; + handshake_us += 125; + + status = ehci_readl( pstatus); + result = ehci_readl( ptr); + g_status = ehci_readl(&ehci->regs->status); + g_mstatus = g_status & INTR_MASK; + + + if ((g_status == ~(u32)0)) /* card removed */ + { + unplug_device = 1; + return -ENODEV; + } + + + + if (!(PORT_CONNECT&status)) /* device removed */ + { + unplug_device = 1; + ret = -ENODEV; + goto handshake_exit; + } + + result &= mask; + + if (handshake_mode != 2) + { + if (g_status & (STS_ERR | STS_FATAL | STS_HALT)) + { + if (handshake_mode) ret = -ETIMEDOUT; else {unplug_device = 1;ret = -ETIMEDOUT; /*ret=-ENODEV;*/} + + goto handshake_exit; + + } + } + + if (result == done) + { + ret = 0; + goto handshake_exit; + } + + diff = get_timer(); + diff -= tmr; + + if (((int)diff) < 0) + { + // error en diferencial: teoricamente imposible, pero... + tmr = get_timer(); + } + } + + while (diff < usec /*usec > 0*/); + + + + if (handshake_mode) ret = -ETIMEDOUT; + else + { + unplug_device = 1; + ret = -ETIMEDOUT; + } + +handshake_exit: + + if (handshake_mode != 2) + { + ehci_writel (g_mstatus | or_flags, &ehci->regs->status); + /* complete the unlinking of some qh [4.15.2.3] */ + u32 command = ehci_readl( &ehci->regs->command); + + if (g_status & STS_IAA) + { + /* guard against (alleged) silicon errata */ + + if (command & CMD_IAAD) + { + ehci_writel(command & ~CMD_IAAD, + &ehci->regs->command); + + } + } + } + + return ret; +} + +#include "ehci-mem.c" +/* one-time init, only for memory state */ +static int ehci_init(void) +{ + int retval; + + if ((retval = ehci_mem_init()) < 0) + return retval; + + /* + * dedicate a qh for the async ring head, since we couldn't unlink + * a 'real' qh without stopping the async schedule [4.8]. use it + * as the 'reclamation list head' too. + * its dummy is used in hw_alt_next of many tds, to prevent the qh + * from automatically advancing to the next td after short reads. + */ + ehci->async->hw_next = QH_NEXT( ehci->async->qh_dma); + + ehci->async->hw_info1 = cpu_to_hc32( QH_HEAD); + + ehci->async->hw_token = cpu_to_hc32( QTD_STS_HALT); + + ehci->async->hw_qtd_next = EHCI_LIST_END(); + + ehci->async->hw_alt_next = EHCI_LIST_END(); //QTD_NEXT( ehci->async->dummy->qtd_dma); + + ehci->ctrl_buffer = USB_Alloc(sizeof(usbctrlrequest)); + + ehci->command = 0; + + ehci_writel( 0x008000002, &ehci->regs->command); + + ehci_writel( ehci->periodic_dma, &ehci->regs->frame_list); + + ehci_writel( ehci->async->qh_dma, &ehci->regs->async_next); + + ehci_writel( 0x00010009, &ehci->regs->command); + + ehci_writel( 1, &ehci->regs->configured_flag); + + ehci_writel( 0x00010029, &ehci->regs->command); + + + return 0; +} + +/* fill a qtd, returning how much of the buffer we were able to queue up */ +static int + +qtd_fill(struct ehci_qtd *qtd, dma_addr_t buf, + size_t len, int token, int maxpacket) +{ + int i, count; + u64 addr = buf; + //ehci_dbg("fill qtd with dma %X len %X\n",buf,len); + /* one buffer entry per 4K ... first might be short or unaligned */ + qtd->hw_buf[0] = cpu_to_hc32( (u32)addr); + qtd->hw_buf_hi[0] = 0; + count = 0x1000 - (buf & 0x0fff); /* rest of that page */ + + if (likely (len < count)) /* ... iff needed */ + count = len; + else + { + buf += 0x1000; + buf &= ~0x0fff; + + /* per-qtd limit: from 16K to 20K (best alignment) */ + + for (i = 1; count < len && i < 5; i++) + { + addr = buf; + qtd->hw_buf[i] = cpu_to_hc32( (u32)addr); + qtd->hw_buf_hi[i] = cpu_to_hc32( + (u32)(addr >> 32)); + buf += 0x1000; + + if ((count + 0x1000) < len) + count += 0x1000; + else + count = len; + } + + /* short packets may only terminate transfers */ + if (count != len) + count -= (count % maxpacket); + } + + qtd->hw_token = cpu_to_hc32( (count << 16) | token); + qtd->length = count; + + return count; +} + +// high bandwidth multiplier, as encoded in highspeed endpoint descriptors +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) +// ... and packet size, for any kind of endpoint descriptor +#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) + +/* + * reverse of qh_urb_transaction: free a list of TDs. + * also count the actual transfer length. + * + */ +static int qh_end_transfer (void) +{ + + struct ehci_qtd *qtd; + + struct ehci_qh *qh = ehci->asyncqh; + u32 token; + int error = 0; + + for (qtd = qh->qtd_head; qtd; qtd = qtd->next) + { + token = hc32_to_cpu( qtd->hw_token); + + if (likely (QTD_PID (token) != 2)) + qtd->urb->actual_length += qtd->length - QTD_LENGTH (token); + + if ( /*!(qtd->length ==0 && ((token & 0xff)==QTD_STS_HALT)) &&*/ + qtd->length != 0 && (token & QTD_STS_HALT)) + { + ehci_dbg("\nqtd error!:"); + + if (token & QTD_STS_BABBLE) + { + ehci_dbg(" BABBLE"); + } + + if (token & QTD_STS_MMF) + { + /* fs/ls interrupt xfer missed the complete-split */ + ehci_dbg(" missed micro frame"); + } + + if (token & QTD_STS_DBE) + { + ehci_dbg(" databuffer error"); + } + + if (token & QTD_STS_XACT) + { + ehci_dbg(" wrong ack"); + } + + if (QTD_CERR (token) == 0) + ehci_dbg(" toomany errors"); + + ehci_dbg("\n"); + + error = -1; + } + } + + if (error) + { +#ifdef DEBUG + verbose = 1; + num_command_before_no_verbose = 100; +#endif + //dump_qh(ehci->asyncqh); + qtd->urb->actual_length = error; + } + + ehci->qtd_used = 0; + return error; +} + +/* + * create a list of filled qtds for this URB; won't link into qh. + */ + +struct ehci_qtd *qh_urb_transaction ( + + struct ehci_urb *urb + ) +{ + + struct ehci_qtd *qtd, *qtd_prev; + + struct ehci_qtd *head; + dma_addr_t buf; + int len, maxpacket; + int is_input; + u32 token; + + /* + * URBs map to sequences of QTDs: one logical transaction + */ + head = qtd = ehci_qtd_alloc (); + + if (!head) return NULL; + + qtd->urb = urb; + + urb->actual_length = 0; + + token = QTD_STS_ACTIVE; + + token |= (EHCI_TUNE_CERR << 10); + + /* for split transactions, SplitXState initialized to zero */ + + len = urb->transfer_buffer_length; + + is_input = urb->input; + + if (urb->ep == 0) + { /* is control */ + /* SETUP pid */ + qtd_fill( qtd, urb->setup_dma, + sizeof (usbctrlrequest), + token | (2 /* "setup" */ << 8), 8); + + /* ... and always at least one more pid */ + token ^= QTD_TOGGLE; + qtd_prev = qtd; + qtd = ehci_qtd_alloc (); + + if (!qtd) goto cleanup; + + qtd->urb = urb; + + qtd_prev->hw_next = QTD_NEXT( qtd->qtd_dma); + + qtd_prev->next = qtd; + + /* for zero length DATA stages, STATUS is always IN */ + if (len == 0) + token |= (1 /* "in" */ << 8); + } + + /* + * data transfer stage: buffer setup + */ + buf = urb->transfer_dma; + + if (is_input) + token |= (1 /* "in" */ << 8); + + /* else it's already initted to "out" pid (0 << 8) */ + + maxpacket = max_packet(urb->maxpacket); + + /* + * buffer gets wrapped in one or more qtds; + * last one may be "short" (including zero len) + * and may serve as a control status ack + */ + for (;;) + { + int this_qtd_len; + + this_qtd_len = qtd_fill( qtd, buf, len, token, maxpacket); + len -= this_qtd_len; + buf += this_qtd_len; + + /* + * short reads advance to a "magic" dummy instead of the next + * qtd ... that forces the queue to stop, for manual cleanup. + * (this will usually be overridden later.) + */ + + if (is_input) + qtd->hw_alt_next = ehci->asyncqh->hw_alt_next; + + /* qh makes control packets use qtd toggle; maybe switch it */ + if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) + token ^= QTD_TOGGLE; + + if (likely (len <= 0)) + break; + + qtd_prev = qtd; + + qtd = ehci_qtd_alloc (); + + if (!qtd) goto cleanup; + + qtd->urb = urb; + + qtd_prev->hw_next = QTD_NEXT( qtd->qtd_dma); + + qtd_prev->next = qtd; + } + + qtd->hw_alt_next = EHCI_LIST_END(); + + /* + * control requests may need a terminating data "status" ack; + * bulk ones may need a terminating short packet (zero length). + */ + + if (likely (urb->transfer_buffer_length != 0)) + { + int one_more = 0; + + if (urb->ep == 0) + { + one_more = 1; + token ^= 0x0100; /* "in" <--> "out" */ + token |= QTD_TOGGLE; /* force DATA1 */ + } + + else if (!(urb->transfer_buffer_length % maxpacket)) + { + //one_more = 1; + } + + if (one_more) + { + qtd_prev = qtd; + qtd = ehci_qtd_alloc (); + + if (!qtd) goto cleanup; + + qtd->urb = urb; + + qtd_prev->hw_next = QTD_NEXT( qtd->qtd_dma); + + qtd_prev->next = qtd; + + /* never any data in such packets */ + qtd_fill( qtd, 0, 0, token, 0); + } + } + + /* by default, enable interrupt on urb completion */ + qtd->hw_token |= cpu_to_hc32( QTD_IOC); + + return head; + +cleanup: + return NULL; +} + +u32 usb_timeout = 1000*1000; + + + +int ehci_do_urb ( + + struct ehci_device *dev, + + struct ehci_urb *urb) +{ + + struct ehci_qh *qh; + + struct ehci_qtd *qtd; + u32 info1 = 0, info2 = 0; + int is_input; + int maxp = 0; + int retval = 0; + + + //ehci_dbg ("do urb %X %X ep %X\n",urb->setup_buffer,urb->transfer_buffer,urb->ep); + + if (urb->ep == 0) //control message + urb->setup_dma = ehci_dma_map_to(urb->setup_buffer, sizeof (usbctrlrequest)); + + if (urb->transfer_buffer_length) + { + if (urb->input) + urb->transfer_dma = ehci_dma_map_to(urb->transfer_buffer, urb->transfer_buffer_length); + else + urb->transfer_dma = ehci_dma_map_from(urb->transfer_buffer, urb->transfer_buffer_length); + } + + qh = ehci->asyncqh; + memset(qh, 0, 12*4); + qtd = qh_urb_transaction ( urb); + + if (!qtd) + { + ehci->qtd_used = 0; + return -ENOMEM; + } + + qh ->qtd_head = qtd; + + + info1 |= ((urb->ep)&0xf) << 8; + info1 |= dev->id; + is_input = urb->input; + maxp = urb->maxpacket; + + info1 |= (2 << 12); /* EPS "high" */ + + if (urb->ep == 0) // control + { + info1 |= (EHCI_TUNE_RL_HS << 28); + info1 |= 64 << 16; /* usb2 fixed maxpacket */ + info1 |= 1 << 14; /* toggle from qtd */ + info2 |= (EHCI_TUNE_MULT_HS << 30); + }else //bulk + { + info1 |= (EHCI_TUNE_RL_HS << 28); + /* The USB spec says that high speed bulk endpoints + * always use 512 byte maxpacket. But some device + * vendors decided to ignore that, and MSFT is happy + * to help them do so. So now people expect to use + * such nonconformant devices with Linux too; sigh. + */ + info1 |= max_packet(maxp) << 16; + info2 |= (EHCI_TUNE_MULT_HS << 30); + + } + + //ehci_dbg("HW info: %08X\n",info1); + qh->hw_info1 = cpu_to_hc32( info1); + + qh->hw_info2 = cpu_to_hc32( info2); + + qh->hw_next = QH_NEXT(qh->qh_dma); + + qh->hw_qtd_next = QTD_NEXT( qtd->qtd_dma); + + qh->hw_alt_next = EHCI_LIST_END(); + + if (urb->ep != 0) + { + if (get_toggle(dev, urb->ep)) + qh->hw_token |= cpu_to_hc32(QTD_TOGGLE); + else + qh->hw_token &= ~cpu_to_hc32( QTD_TOGGLE); + + //ehci_dbg("toggle for ep %x: %d %x\n",urb->ep,get_toggle(dev,urb->ep),qh->hw_token); + } + + qh->hw_token &= cpu_to_hc32( QTD_TOGGLE | QTD_STS_PING); + + qh->hw_next = QH_NEXT(ehci->async->qh_dma); + + + ehci_dma_map_bidir(qh, sizeof(struct ehci_qh)); + + for (qtd = qh->qtd_head; qtd; qtd = qtd->next) + + ehci_dma_map_bidir(qtd, sizeof(struct ehci_qtd)); + +#if 0 + if (urb->ep != 0) + { + dump_qh(ehci->async); + dump_qh(ehci->asyncqh); + } + +#endif + // start (link qh) + ehci->async->hw_next = QH_NEXT(qh->qh_dma); + + ehci_dma_map_bidir(ehci->async, sizeof(struct ehci_qh)); + + retval = handshake(&ehci->regs->port_status[dev->port], &ehci->regs->status, STS_INT, STS_INT, usb_timeout, 0 /*STS_RECL|STS_IAA|STS_INT*/); + + // from 2.6 + if (retval < 0 && handshake_mode != 2) + { + ehci->async->hw_next = QH_NEXT(ehci->async->qh_dma); + + ehci_dma_map_bidir(ehci->async, sizeof(struct ehci_qh)); + + ehci_dma_unmap_bidir(ehci->async->qh_dma, sizeof(struct ehci_qh)); + + ehci->qtd_used = 0; + return retval; + } + + //print_hex_dump_bytes ("qh mem",0,(void*)qh,17*4); + //retval = poll_transfer_end(1000*1000); + + ehci_dma_unmap_bidir(ehci->async->qh_dma, sizeof(struct ehci_qh)); + + ehci_dma_unmap_bidir(qh->qh_dma, sizeof(struct ehci_qh)); + + for (qtd = qh->qtd_head; qtd; qtd = qtd->next) + + ehci_dma_unmap_bidir(qtd->qtd_dma, sizeof(struct ehci_qtd)); + + // stop (unlink qh) + ehci->async->hw_next = QH_NEXT(ehci->async->qh_dma); + + ehci_dma_map_bidir(ehci->async, sizeof(struct ehci_qh)); + + ehci_dma_unmap_bidir(ehci->async->qh_dma, sizeof(struct ehci_qh)); + + + // ack + if (handshake_mode == 2) + ehci_writel( STS_RECL | STS_IAA | STS_INT, &ehci->regs->status); + + + if (urb->ep != 0) + { + set_toggle(dev, urb->ep, (qh->hw_token & cpu_to_hc32(QTD_TOGGLE)) ? 1 : 0); + //ehci_dbg("toggle for ep %x: %d %d %x %X\n",urb->ep,get_toggle(dev,urb->ep),(qh->hw_token & cpu_to_hc32(QTD_TOGGLE)),qh->hw_token,dev->toggles); + } + + + if (retval >= 0) + // wait hc really stopped + retval = handshake(&ehci->regs->port_status[dev->port], &ehci->regs->async_next, ~0, ehci->async->qh_dma, 20*1000, 0); + + // from 2.6 + if (retval < 0) + { + + ehci->qtd_used = 0; + return retval; + } + + //release memory, and actualise urb->actual_length + + if (qh_end_transfer() != 0) + { + + if (handshake_mode) retval = -ETIMEDOUT; else {unplug_device = 1;retval = -ETIMEDOUT; /*-ENODEV;*/} + + return retval; + } + + if (urb->transfer_buffer_length) + { + if (urb->input) + ehci_dma_unmap_to(urb->transfer_dma, urb->transfer_buffer_length); + else + ehci_dma_unmap_from(urb->transfer_dma, urb->transfer_buffer_length); + } + + if (urb->ep == 0) //control message + ehci_dma_unmap_to(urb->setup_dma, sizeof (usbctrlrequest)); + + + + if (retval == 0) + { + + return urb->actual_length; + + ehci_dbg ( "un successfull urb %d!!\n", retval); + } + + return retval; +} + +s32 ehci_control_message(struct ehci_device *dev, u8 bmRequestType, u8 bmRequest, u16 wValue, u16 wIndex, u16 wLength, void *buf) +{ + + struct ehci_urb urb; + usbctrlrequest *req = ehci->ctrl_buffer; + + if (verbose) + ehci_dbg ( "control msg: rt%02X r%02X v%04X i%04X s%04x %p\n", bmRequestType, bmRequest, wValue, wIndex, wLength, buf); + + req->bRequestType = bmRequestType; + + req->bRequest = bmRequest; + + req->wValue = swab16(wValue); + + req->wIndex = swab16(wIndex); + + req->wLength = swab16(wLength); + + urb.setup_buffer = req; + + urb.ep = 0; + + urb.input = (bmRequestType&USB_CTRLTYPE_DIR_DEVICE2HOST) != 0; + + urb.maxpacket = 64; + + urb.transfer_buffer_length = wLength; + + if (((u32)buf) > 0x13880000) + { // HW cannot access this buffer, we allow this for convenience + int ret; + urb.transfer_buffer = USB_Alloc(wLength); + + if (verbose) + ehci_dbg("alloc another buffer %p %p\n", buf, urb.transfer_buffer); + + memcpy(urb.transfer_buffer, buf, wLength); + + ret = ehci_do_urb(dev, &urb); + + memcpy(buf, urb.transfer_buffer, wLength); + + USB_Free(urb.transfer_buffer); + + return ret; + } + + else + { + urb.transfer_buffer = buf; + return ehci_do_urb(dev, &urb); + } +} + +s32 ehci_bulk_message(struct ehci_device *dev, u8 bEndpoint, u32 wLength, void *rpData) +{ + + struct ehci_urb urb; + s32 ret; + urb.setup_buffer = NULL; + urb.ep = bEndpoint; + urb.input = (bEndpoint&0x80) != 0; + urb.maxpacket = 512; + urb.transfer_buffer_length = wLength; + urb.transfer_buffer = rpData; + + if (verbose) + ehci_dbg ( "bulk msg: ep:%02X size:%02X addr:%04X", bEndpoint, wLength, rpData); + + ret = ehci_do_urb(dev, &urb); + + if (verbose) + ehci_dbg ( "==>%d\n", ret); + + return ret; +} + + + +int ehci_reset_port_old(int port) +{ + u32 __iomem *status_reg = &ehci->regs->port_status[port]; + + struct ehci_device *dev = &ehci->devices[port]; + u32 status ; //= ehci_readl(status_reg); + int retval = 0, i, f; + u32 g_status; + dev->id = 0; + + g_status = ehci_readl(&ehci->regs->status); + + if ((g_status & (STS_HALT | STS_FATAL)) || !(ehci_readl (&ehci->regs->command) & 1)) + { + // try LIGHT RESET and RUN (may be is unsopported, but i am sure you cannot perform the HCRESET without problems (so i try this method) + + ehci_writel( g_status & INTR_MASK, &ehci->regs->status); + g_status = ehci_readl (&ehci->regs->command); + //ehci_reset(); + ehci_writel( 0x008000080, &ehci->regs->command); + ehci_msleep(20); + ehci_writel( ehci->periodic_dma, &ehci->regs->frame_list); + ehci_writel( ehci->async->qh_dma, &ehci->regs->async_next); + ehci_writel( 0x00010009, &ehci->regs->command); + ehci_msleep(10); + //ehci_writel( 1, &ehci->regs->configured_flag); + ehci_writel( 0x00010029, &ehci->regs->command); + ehci_msleep(10); + g_status = ehci_readl (&ehci->regs->command); + g_status = ehci_readl(&ehci->regs->status); + } + + // clear status flags + + ehci_writel( g_status & INTR_MASK, &ehci->regs->status); + + g_status = ehci_readl (&ehci->regs->command); + + status = ehci_readl(status_reg); + + if ((PORT_OWNER&status) || !(PORT_CONNECT&status)) + { + // ehci_writel( PORT_OWNER,status_reg); + ehci_dbg ( "port %d had no usb2 device connected at startup %X \n", port, ehci_readl(status_reg)); + return -ENODEV; // no USB2 device connected + } + + ehci_dbg ( "port %d has usb2 device connected! reset it...\n", port); + + f = handshake_mode; + handshake_mode = 2; + + for (i = 0;i < 4;i++) //4 retries + { + u32 status; + ehci_writel( 0x1803, status_reg); + ehci_msleep(10); + ehci_writel( 0x1903, status_reg); + ehci_msleep(100); // wait 100ms for the reset sequence + ehci_writel( 0x1001, status_reg); + ehci_msleep(100); + status = ehci_readl(status_reg); + + if ((PORT_OWNER&status) || !(PORT_CONNECT&status) || !(status & PORT_PE) || !(status & PORT_POWER) || PORT_USB11(status)) + { + retval = -1; + continue; + } + + //ehci_writel( PORT_OWNER|PORT_POWER|PORT_RESET,status_reg); + + + //ehci_writel( ehci_readl(status_reg)& (~PORT_RESET),status_reg); + retval = handshake(status_reg, status_reg, + PORT_RESET, 0, 750, 0); + + if (retval == 0) + { + int old_time; + /* ehci_dbg ( "port %d reset error %d\n", + port, retval);*/ + + ehci_dbg ( "port %d reseted status:%04x...\n", port, ehci_readl(status_reg)); + ehci_msleep(100); + handshake_mode = 1; + old_time = usb_timeout; + usb_timeout = 200 * 1000; + // now the device has the default device id + retval = ehci_control_message(dev, USB_CTRLTYPE_DIR_DEVICE2HOST, + USB_REQ_GETDESCRIPTOR, USB_DT_DEVICE << 8, 0, sizeof(dev->desc), &dev->desc); + + if (retval >= 0) + { + + + retval = ehci_control_message(dev, USB_CTRLTYPE_DIR_HOST2DEVICE, + USB_REQ_SETADDRESS, port + 1, 0, 0, 0); + + if (retval >= 0) break; + } + + usb_timeout = old_time; + } + + } + + handshake_mode = f; + + if (retval < 0) + { + + return retval; + } + + dev->toggles = 0; + + dev->id = port + 1; + // ehci_dbg ( "device %d: %X %X...\n", dev->id,le16_to_cpu(dev->desc.idVendor),le16_to_cpu(dev->desc.idProduct)); + return retval; +} + +#if 1 + +void ehci_reset_hub(void) +{ + u32 g_status; + + g_status = ehci_readl(&ehci->regs->status); + + if ((g_status & (STS_HALT | STS_FATAL)) || !(ehci_readl (&ehci->regs->command) & 1)) + { + // try LIGHT RESET and RUN (may be is unsopported, but i am sure you cannot perform the HCRESET without problems (so i try this method) + + ehci_writel( g_status & INTR_MASK, &ehci->regs->status); + g_status = ehci_readl (&ehci->regs->command); + //ehci_reset(); + ehci_writel( 0x008000080, &ehci->regs->command); + ehci_msleep(20); + ehci_writel( ehci->periodic_dma, &ehci->regs->frame_list); + ehci_writel( ehci->async->qh_dma, &ehci->regs->async_next); + ehci_writel( 0x00010009, &ehci->regs->command); + ehci_msleep(10); + //ehci_writel( 1, &ehci->regs->configured_flag); + ehci_writel( 0x00010029, &ehci->regs->command); + ehci_msleep(10); + g_status = ehci_readl (&ehci->regs->command); + g_status = ehci_readl(&ehci->regs->status); + } + + // clear status flags + + ehci_writel( g_status & INTR_MASK, &ehci->regs->status); + + g_status = ehci_readl (&ehci->regs->command); +} + +void ehci_adquire_port(int port) +{ + u32 __iomem *status_reg = &ehci->regs->port_status[port]; + u32 status = ehci_readl(status_reg); + u32 new_owner = 0; + int i; + + for (i = 6; i > 0; --i) + { + status = ehci_readl(status_reg); + //sdlog("ehci_adquire_port owner: %x\n",(status & PORT_OWNER)); + + if ((status & PORT_OWNER) == new_owner || + (status & (PORT_OWNER | PORT_CONNECT)) == 0) + i = 0; + else + { + status ^= PORT_OWNER; + status &= ~(PORT_PE | PORT_RWC_BITS); + ehci_writel(status, status_reg); + } + + ehci_msleep(5); + } + + //enable port + status = ehci_readl(status_reg); + + status |= (PORT_CONNECT | PORT_POWER); + + //ehci_writel( 0x1001,status_reg); + ehci_msleep(5); +} + +int ehci_reset_usb_port(int port) +{ + + u32 __iomem *status_reg = &ehci->regs->port_status[port]; + //struct ehci_device *dev = &ehci->devices[port]; + u32 status = ehci_readl(status_reg); + + int retval = 0; + + if ((PORT_OWNER&status) || !(PORT_CONNECT&status)) + { + //char cad[128]; + ehci_writel( PORT_OWNER, status_reg); + //ehci_dbg ( "port %d had no usb2 device connected at startup %X \n", port,ehci_readl(status_reg)); + sdlog("port %d had no usb2 device connected at startup %x \n", port, ehci_readl(status_reg)); + //debug_sprintf(cad," usb not conected/owner port: %d status: %04X\n",port,status); + //my_sprint(cad,NULL); + return -ENODEV; // no USB2 device connected + } + + //char cad[128]; + //char str_log[1024]; + //str_log[0]='\0'; + ehci_dbg ( "port %d has usb2 device connected! reset it...\n", port); + + sdlog("status1: %x\n", status); + + //my_sprint(log,NULL); + int i; + + for (i = 0;i < 4;i++) //4 retries + { + status &= ~PORT_PE; + status |= PORT_RESET; + ehci_writel( status, status_reg); + ehci_msleep(60); // wait 50ms for the reset sequence + status = ehci_readl(status_reg); + sdlog("status2: %x\n", status); + status &= ~(PORT_RWC_BITS | PORT_RESET); /* force reset to complete */ + //status &= ~PORT_RESET; + //sdlog("status3: %x\n",ehci_readl(status_reg)); + ehci_writel( status, status_reg); + ehci_msleep(50); + retval = handshake(status_reg, status_reg, + PORT_RESET, 0, 750, 0); + + if (retval != 0) + { + ehci_dbg ( "port %d reset error %d\n", + port, retval); + status = ehci_readl(status_reg); + //debug_sprintf(cad,"handshake PORT_RESET error: %04x\n",status); strcat(log,cad); + sdlog("handshake PORT_RESET error: %x\n", status); + //my_sprint(log,NULL); + retval = -2000; + return retval; + } + + status = ehci_readl(status_reg); + ehci_dbg ( "port %d reseted status:%04x...\n", port, status); + + //debug_sprintf(cad," port reseted status: %04x\n",status);strcat(log,cad); + sdlog("port reseted status(%d): %x\n", i, status); + + if (i > 0 && status & PORT_PE) break; //port enabled + } + /* + if (!(status & PORT_PE)) { + // that means is low speed device so release + status |= PORT_OWNER; + status &= ~PORT_RWC_BITS; + ehci_writel( status, status_reg); + ehci_msleep(10); + status = ehci_readl(status_reg); + //debug_sprintf(cad,"PORT_PE, release USB11 status: %04x\n",status); strcat(log,cad); + sdlog("PORT_PE, release USB11 status: %x\n",status); + //my_sprint(log,NULL); + //return -1001; + } + */ + //my_sprint(log,NULL);ehci_msleep(50); + return retval; +} + +int ehci_init_port(int port) +{ + + struct ehci_device *dev = &ehci->devices[port]; + int i, retval = 0; + dev->id = 0; + + ehci_msleep(10); + //my_sprint("getting USB_REQ_GETDESCRIPTOR",NULL);ehci_msleep(50); + sdlog("getting USB_REQ_GETDESCRIPTOR\n"); + // now the device has the default device id + retval = ehci_control_message(dev, USB_CTRLTYPE_DIR_DEVICE2HOST, + USB_REQ_GETDESCRIPTOR, USB_DT_DEVICE << 8, 0, sizeof(dev->desc), &dev->desc); + //retval=-1; + + if (retval < 0) + { + //my_sprint("unable to get device desc...",NULL);ehci_msleep(50); + sdlog("error getting USB_REQ_GETDESCRIPTOR\n"); + //ehci_dbg ( "unable to get device desc...\n"); + retval = -2201; + + //return retval; + } + + if (retval < 0) + { + for (i = 0;i < 3;i++) + { + ehci_msleep(300); + ehci_adquire_port(port); + ehci_msleep(80); + ehci_reset_usb_port(port); + ehci_msleep(50); + //my_sprint("getting USB_REQ_GETDESCRIPTOR",NULL);ehci_msleep(50); + sdlog("getting USB_REQ_GETDESCRIPTOR (%i)\n", i + 1); + // now the device has the default device id + retval = ehci_control_message(dev, USB_CTRLTYPE_DIR_DEVICE2HOST, + USB_REQ_GETDESCRIPTOR, USB_DT_DEVICE << 8, 0, sizeof(dev->desc), &dev->desc); + + if (retval < 0) + { + //my_sprint("unable to get device desc...",NULL);ehci_msleep(50); + sdlog("error getting USB_REQ_GETDESCRIPTOR\n"); + //ehci_dbg ( "unable to get device desc...\n"); + retval = -2201; + + //return retval; + } + + else break; + } + } + + + if (retval < 0) return -2201; + + sdlog("USB_REQ_GETDESCRIPTOR ok\n"); + + int cnt = 2; + + do + { + ehci_msleep(100); + sdlog("trying USB_REQ_SETADDRESS: %d\n", cnt); + retval = ehci_control_message(dev, USB_CTRLTYPE_DIR_HOST2DEVICE, + USB_REQ_SETADDRESS, cnt, 0, 0, 0); + + if (retval < 0) + { + //my_sprint("unable to set device addr...",NULL); + ehci_dbg ( "unable to set device addr...\n"); + retval = -8000 - cnt; + sdlog("unable to set device addr: %d\n", cnt); + //return retval; + } + + else sdlog("USB_REQ_SETADDRESS ok: %d\n", cnt); + + dev->toggles = 0; + + dev->id = cnt; + + USB_ClearHalt(dev, 0); + + //USB_ClearHalt(dev, 0x80); + ehci_msleep(10); + + sdlog("checking USB_REQ_GETDESCRIPTOR\n"); + + retval = ehci_control_message(dev, USB_CTRLTYPE_DIR_DEVICE2HOST, + USB_REQ_GETDESCRIPTOR, USB_DT_DEVICE << 8, 0, sizeof(dev->desc), &dev->desc); + + + if (retval < 0) + { + //my_sprint("unable to get device desc...",NULL); + ehci_dbg ( "unable to get device desc...\n"); + sdlog("error checking USB_REQ_GETDESCRIPTOR\n"); + retval = -2242; + dev->id = 0; + //return retval; + } + + else sdlog("ok checking USB_REQ_GETDESCRIPTOR\n"); + + cnt++; + } + + while (retval < 0 && cnt < 20); + + if (retval >= 0)sdlog("init ok\n"); + + return retval; +} + +#endif +int ehci_reset_port(int port) +{ + int retval, h_flag; + + struct ehci_device *dev = &ehci->devices[port]; + dev->id = 0; + h_flag = handshake_mode; + //ehci_reset_hub(); + handshake_mode = 1; + retval = ehci_reset_usb_port(port); + + if (retval >= 0)retval = ehci_init_port(port); + + handshake_mode = h_flag; + + return retval; +} + + +int ehci_reset_port2(int port) +{ + u32 __iomem *status_reg = &ehci->regs->port_status[port]; + + + ehci->qtd_used = 0; + + int ret = ehci_reset_port_old(port); + + if (ret < 0 /*==-ENODEV || ret==-ETIMEDOUT*/) + { + ehci_msleep(100); // power off + ehci_writel( 0x1803, status_reg); + ehci_msleep(100); + ehci_writel( 0x1903, status_reg); + ehci_msleep(100); // wait 100ms for the reset sequence + ehci_writel( 0x1801, status_reg); + + //ehci_msleep(500); // power off + //ehci_writel( 0x1001,status_reg); + } + + return ret; +} + +int ehci_reset_device(struct ehci_device *dev) +{ + return ehci_reset_port(dev->port); +} + +#include "usbstorage.h" +int ehci_discover(void) +{ + int i; + // precondition: the ehci should be halted + + for (i = 0;i < ehci->num_port; i++) + { + + struct ehci_device *dev = &ehci->devices[i]; + dev->port = i; + ehci_adquire_port(i); + //ehci_reset_port(i); + } + + return 0; +} + +/* wii: quickly release non ehci or not connected ports, + as we can't kick OHCI drivers laters if we discover a device for them. +*/ +int ehci_release_ports(void) +{ + int i; + u32 __iomem *status_reg = &ehci->regs->port_status[2]; + + while (ehci_readl(&ehci->regs->port_status[2]) == 0x1000) ehci_usleep(100); // wait port 2 to init + + ehci_msleep(100); // wait another msec.. + + for (i = 0;i < ehci->num_port; i++) + { + status_reg = &ehci->regs->port_status[i]; + u32 status = ehci_readl(status_reg); + + if (i == 2 || !(PORT_CONNECT&status) || PORT_USB11(status)) + ehci_writel( PORT_OWNER, status_reg); // release port. + } + + return 0; +} + +void ehci_release_port(int port) +{ + u32 __iomem *status_reg = &ehci->regs->port_status[port]; + u32 status; + + status = ehci_readl(status_reg); + status |= PORT_OWNER; + status &= ~PORT_RWC_BITS; + ehci_writel( status, status_reg); + + +} + +int ehci_is_inserted(void) +{ + int i; + + struct ehci_device *dev ; + u32 __iomem *status_reg; + u32 status; + + for (i = 0;i < ehci->num_port;i++) + { + dev = &ehci->devices[i]; + //if(dev->id != 0) + { + status_reg = &ehci->regs->port_status[i]; + status = ehci_readl(status_reg); + + if (!(PORT_OWNER&status) && (PORT_CONNECT&status)) + { + return 1; + } + } + } + + return 0; +} + + +int ehci_open_device(int vid, int pid, int fd) +{ + int i; + + for (i = 0;i < ehci->num_port;i++) + { + //ehci_dbg("try device: %d\n",i); + + if (ehci->devices[i].fd == 0 && + le16_to_cpu(ehci->devices[i].desc.idVendor) == vid && + le16_to_cpu(ehci->devices[i].desc.idProduct) == pid) + { + //ehci_dbg("found device: %x %x\n",vid,pid); + ehci->devices[i].fd = fd; + return fd; + } + } + + return -6; +} + +int ehci_close_device(struct ehci_device *dev) +{ + if (dev) + dev->fd = -1; + + return 0; +} + +void * ehci_fd_to_dev(int fd) +{ + int i; + + for (i = 0;i < ehci->num_port;i++) + { + + struct ehci_device *dev = &ehci->devices[i]; + + return dev; // return always device[0] + +#if 0 + //ehci_dbg ( "device %d:fd:%d %X %X...\n", dev->id,dev->fd,le16_to_cpu(dev->desc.idVendor),le16_to_cpu(dev->desc.idProduct)); + + if (dev->fd == fd) + { + return dev; + + } + +#endif + + } + + ehci_dbg("unknown fd! %d\n", fd); + return 0; +} + +#define g_ehci #error +int ehci_get_device_list(u8 maxdev, u8 b0, u8*num, u16*buf) +{ + int i, j = 0; + + for (i = 0;i < ehci->num_port && j < maxdev ;i++) + { + + struct ehci_device *dev = &ehci->devices[i]; + + if (dev->id != 0) + { + //ehci_dbg ( "device %d: %X %X...\n", dev->id,le16_to_cpu(dev->desc.idVendor),le16_to_cpu(dev->desc.idProduct)); + buf[j*4] = 0; + buf[j*4 + 1] = 0; + buf[j*4 + 2] = le16_to_cpu(dev->desc.idVendor); + buf[j*4 + 3] = le16_to_cpu(dev->desc.idProduct); + j++; + } + } + + //ehci_dbg("found %d devices\n",j); + *num = j; + + return 0; +} + +#include "usb.c" +#include "usbstorage.c" diff --git a/tinyehci/usb.c b/tinyehci/usb.c index 2ac90cd1..9102db13 100644 --- a/tinyehci/usb.c +++ b/tinyehci/usb.c @@ -40,13 +40,41 @@ s32 USB_GetDescriptors(struct ehci_device * fd, usb_devdesc *udd) buffer = USB_Alloc(sizeof(*udd)); if(buffer == NULL) { + s_printf("buffer == NULL (no mem)\n"); retval = -ENOMEM; goto free_and_error; } + ehci_msleep(10); + retval = __usb_getdesc(fd, buffer, USB_DT_DEVICE, 0, USB_DT_DEVICE_SIZE); if(retval < 0) - goto free_and_error; + { + u32 status; + int ret; + + s_printf("__usb_getdesc error USB_DT_DEVICE: retry\n"); + + + ret=ehci_reset_port(current_port); + ehci_msleep(20); + status=ehci_readl(&ehci->regs->port_status[current_port]); + + if(ret<0 || (status & 0x3105)!=0x1005) + { + ret=ehci_reset_port2(current_port); + ehci_msleep(20); + ehci_readl(&ehci->regs->port_status[current_port]); + } + + ehci_msleep(30); + retval = __usb_getdesc(fd, buffer, USB_DT_DEVICE, 0, USB_DT_DEVICE_SIZE); + if(retval < 0) + { + s_printf("__usb_getdesc error USB_DT_DEVICE\n"); + goto free_and_error; + } + } memcpy(udd, buffer, USB_DT_DEVICE_SIZE); USB_Free(buffer); @@ -58,6 +86,7 @@ s32 USB_GetDescriptors(struct ehci_device * fd, usb_devdesc *udd) udd->configurations = USB_Alloc(udd->bNumConfigurations* sizeof(*udd->configurations)); if(udd->configurations == NULL) { + s_printf("udd->configurations == NULL (no mem)\n"); retval = -ENOMEM; goto free_and_error; } @@ -67,6 +96,7 @@ s32 USB_GetDescriptors(struct ehci_device * fd, usb_devdesc *udd) buffer = USB_Alloc( USB_DT_CONFIG_SIZE); if(buffer == NULL) { + s_printf("buffer == NULL (no mem)\n"); retval = -ENOMEM; goto free_and_error; } @@ -81,13 +111,17 @@ s32 USB_GetDescriptors(struct ehci_device * fd, usb_devdesc *udd) buffer = USB_Alloc( ucd->wTotalLength); if(buffer == NULL) { + s_printf("buffer == NULL (no mem)\n"); retval = -ENOMEM; goto free_and_error; } retval = __usb_getdesc(fd, buffer, USB_DT_CONFIG, iConf, ucd->wTotalLength); if(retval < 0) + { + s_printf("__usb_getdesc error USB_DT_CONFIG: retry\n"); goto free_and_error; + } ptr = buffer; ptr += ucd->bLength; @@ -96,7 +130,10 @@ s32 USB_GetDescriptors(struct ehci_device * fd, usb_devdesc *udd) retval = -ENOMEM; ucd->interfaces = USB_Alloc(ucd->bNumInterfaces* sizeof(*ucd->interfaces)); if(ucd->interfaces == NULL) + { + s_printf("ucd->interfaces == NULL (no mem)\n"); goto free_and_error; + } memset(ucd->interfaces,0,ucd->bNumInterfaces* sizeof(*ucd->interfaces)); for(iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++) { @@ -107,7 +144,10 @@ s32 USB_GetDescriptors(struct ehci_device * fd, usb_devdesc *udd) uid->endpoints = USB_Alloc(uid->bNumEndpoints* sizeof(*uid->endpoints)); if(uid->endpoints == NULL) + { + s_printf("uid->endpoints == NULL (no mem)\n"); goto free_and_error; + } memset(uid->endpoints,0,uid->bNumEndpoints* sizeof(*uid->endpoints)); // This skips vendor and class specific descriptors @@ -117,7 +157,10 @@ s32 USB_GetDescriptors(struct ehci_device * fd, usb_devdesc *udd) { uid->extra = USB_Alloc(i); if(uid->extra == NULL) + { + s_printf("uid->extra == NULL (no mem)\n"); goto free_and_error; + } memcpy(uid->extra, ptr, i); ptr += i; size -= i; diff --git a/tinyehci/usbstorage.c b/tinyehci/usbstorage.c index 9948a567..15b5f64d 100644 --- a/tinyehci/usbstorage.c +++ b/tinyehci/usbstorage.c @@ -672,7 +672,8 @@ static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun) //retval = __cycle(dev, lun, NULL, 0, cmd, 1, 1, &status, NULL, USBSTORAGE_CYCLE_RETRIES); - if(retval<0) s_printf(" SCSI_TEST_UNIT_READY ret %i\n", retval); + //if(retval<0) + s_printf(" SCSI_TEST_UNIT_READY ret %i\n", retval); @@ -1041,8 +1042,8 @@ found: if(USB_GetConfiguration(dev->usb_fd, &conf) < 0) { - s_printf("USB_GetConfiguration() Error\n"); - goto free_and_return; + s_printf("USB_GetConfiguration() Error. Continue.\n"); + //goto free_and_return; } s_printf("Actual conf: %x next conf: %x\n",conf, dev->configuration); try_status=-1202; @@ -1610,7 +1611,7 @@ s32 USBStorage_Init(void) - try_status=-1; + try_status=-100; unplug_device=1; __mounted=0; use_alternative_timeout=1; @@ -1664,6 +1665,7 @@ s_printf("\n***************************************************\nRodries ehcmodu if(ret==-1119 || ret==-1120) { try_status=ret; + try_status=-100; continue; } ret=ehci_reset_port2(i);