From e5102331542fb6d60a19489e2dc1bd5921e66ad8 Mon Sep 17 00:00:00 2001 From: dimok321 <15055714+dimok789@users.noreply.github.com> Date: Sat, 28 May 2011 19:52:00 +0000 Subject: [PATCH] *Added new ehcmodules with better and more drives support on Hermes IOS (thx rodries) *Fixed reset of the loader when loading game with IOS reload and disabled WiiTDB titles (Issue 1874) *Added new 'Inherit' or 'Use global' setting for game settings. If that option is set than the global setting is used for that option. (Issue 1842) *Fixed timeout timer on startup to count correctly. (Issue 1907) *Added two new video modes to force progressive video mode, 'FORCE PAL480p' and 'FORCE NTSC480p' (basically the same but oh well) (Issue 1902) *Added the new 'Return to' procedure for the d2x v4 IOS for Test purpose (didn't test it, need feedback on this one). The old method is used if this procedure fails. Please test it on problematic games (e.g. PoP). (Issue 1892) --- bin/ehcmodule_5.c | 944 ++++++++++++++++ bin/ehcmodule_5.h | 3 + cios_installer/add_dip_plugin.c | 288 +++++ cios_installer/add_dip_plugin.h | 8 + cios_installer/data/ticket.raw | Bin 0 -> 676 bytes cios_installer/data/tmd.raw | Bin 0 -> 520 bytes ehcmodule/EHCI.SearchResults | 3 + ehcmodule/bin/ehcmodule.elf | Bin 0 -> 26234 bytes ehcmodule/bin/ehcmodule.rar | Bin 0 -> 20606 bytes ehcmodule/source/ehc_loop.c.old | 498 +++++++++ ehcmodule/source/ehc_loop.c.orig | 522 +++++++++ ehcmodule/source/ums.c | 90 ++ mload/source/main.c.bak | 498 +++++++++ test/Makefile | 151 +++ test/c.bat | 7 + test/data/ehcmodule.elf | Bin 0 -> 25260 bytes test/dbg.bat | 1 + test/l.bat | 1 + test/m.bat | 7 + test/m2.bat | 7 + test/source/di2.c | 1072 ++++++++++++++++++ test/source/di2.h | 98 ++ test/source/libdvdiso.c | 1526 +++++++++++++++++++++++++ test/source/libdvdiso.h | 20 + test/source/libmload/include/mload.h | 203 ++++ test/source/libmload/source/mload.c | 410 +++++++ test/source/main.c | 441 ++++++++ test/source/mload.c | 493 +++++++++ test/source/mload.h | 240 ++++ test/source/usb2storage.c | 411 +++++++ test/source/usb2storage.h | 33 + test/test.rar | Bin 0 -> 131025 bytes tinyehci/ehci.c.old | 980 ++++++++++++++++ tinyehci/ehci.c.old2 | 862 +++++++++++++++ tinyehci/ehci1.c | 1538 ++++++++++++++++++++++++++ tinyehci/usb.c | 45 +- tinyehci/usbstorage.c | 10 +- 37 files changed, 11405 insertions(+), 5 deletions(-) create mode 100644 bin/ehcmodule_5.c create mode 100644 bin/ehcmodule_5.h create mode 100644 cios_installer/add_dip_plugin.c create mode 100644 cios_installer/add_dip_plugin.h create mode 100644 cios_installer/data/ticket.raw create mode 100644 cios_installer/data/tmd.raw create mode 100644 ehcmodule/EHCI.SearchResults create mode 100644 ehcmodule/bin/ehcmodule.elf create mode 100644 ehcmodule/bin/ehcmodule.rar create mode 100644 ehcmodule/source/ehc_loop.c.old create mode 100644 ehcmodule/source/ehc_loop.c.orig create mode 100644 ehcmodule/source/ums.c create mode 100644 mload/source/main.c.bak create mode 100644 test/Makefile create mode 100644 test/c.bat create mode 100644 test/data/ehcmodule.elf create mode 100644 test/dbg.bat create mode 100644 test/l.bat create mode 100644 test/m.bat create mode 100644 test/m2.bat create mode 100644 test/source/di2.c create mode 100644 test/source/di2.h create mode 100644 test/source/libdvdiso.c create mode 100644 test/source/libdvdiso.h create mode 100644 test/source/libmload/include/mload.h create mode 100644 test/source/libmload/source/mload.c create mode 100644 test/source/main.c create mode 100644 test/source/mload.c create mode 100644 test/source/mload.h create mode 100644 test/source/usb2storage.c create mode 100644 test/source/usb2storage.h create mode 100644 test/test.rar create mode 100644 tinyehci/ehci.c.old create mode 100644 tinyehci/ehci.c.old2 create mode 100644 tinyehci/ehci1.c 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 0000000000000000000000000000000000000000..7ce7bc59b835aefe6b8ff4dea8735f6f71b90da7 GIT binary patch literal 676 zcmZQzWME_%1!RXnP=0=iuCt>71Q_Z@1Vfp|WE)ALMvEmuX7A#mhG4|45EH4!|0e)Q IHIH=l0N;Bk+5i9m literal 0 HcmV?d00001 diff --git a/cios_installer/data/tmd.raw b/cios_installer/data/tmd.raw new file mode 100644 index 0000000000000000000000000000000000000000..f720ef6ccca02075b64d3a706e827e120f0e6227 GIT binary patch literal 520 xcmZQzWME_%1!RXnP=0=iuCt>71Q_Z%2SAx7WEqJR7vf|YfTjt_JTw_JE&!5U1~vcy literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..abad624cdbd2bf8ac82f0250132502bdc7283a6c GIT binary patch literal 26234 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 bwE*;BQjj28>odrO> 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 +#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 0000000000000000000000000000000000000000..24f58176e746a0c385bcc3d00a046a6cf09c9d74 GIT binary patch literal 25260 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..096bebaf2c5d517a744c5e90d32d2db2400fa9cb GIT binary patch literal 131025 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

>%eH4 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, + 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);